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();
240 return toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble();
243 QPointF textPosition() {
244 return QPointF(splashWidth()/2 - 16, splashHeigth() - 40);
248 return QSize(width_ * pixelRatio(),height_ * pixelRatio());
251 double splashWidth() {
252 return splash_.width()/splashPixelRatio();
255 double splashHeigth() {
256 return splash_.height()/splashPixelRatio();
259 /// Ratio between physical pixels and device-independent pixels of splash image
260 double splashPixelRatio() const {
261 #if QT_VERSION >= 0x050000
262 return splash_.devicePixelRatio();
270 /// Toolbar store providing access to individual toolbars by name.
271 typedef map<string, GuiToolbar *> ToolbarMap;
273 typedef shared_ptr<Dialog> DialogPtr;
278 struct GuiView::GuiViewPrivate
280 GuiViewPrivate(GuiView * gv)
281 : gv_(gv), current_work_area_(0), current_main_work_area_(0),
282 layout_(0), autosave_timeout_(5000),
285 // hardcode here the platform specific icon size
286 smallIconSize = 16; // scaling problems
287 normalIconSize = 20; // ok, default if iconsize.png is missing
288 bigIconSize = 26; // better for some math icons
289 hugeIconSize = 32; // better for hires displays
292 // if it exists, use width of iconsize.png as normal size
293 QString const dir = toqstr(addPath("images", lyxrc.icon_set));
294 FileName const fn = lyx::libFileSearch(dir, "iconsize.png");
296 QImage image(toqstr(fn.absFileName()));
297 if (image.width() < int(smallIconSize))
298 normalIconSize = smallIconSize;
299 else if (image.width() > int(giantIconSize))
300 normalIconSize = giantIconSize;
302 normalIconSize = image.width();
305 splitter_ = new QSplitter;
306 bg_widget_ = new BackgroundWidget(400, 250);
307 stack_widget_ = new QStackedWidget;
308 stack_widget_->addWidget(bg_widget_);
309 stack_widget_->addWidget(splitter_);
312 // TODO cleanup, remove the singleton, handle multiple Windows?
313 progress_ = ProgressInterface::instance();
314 if (!dynamic_cast<GuiProgress*>(progress_)) {
315 progress_ = new GuiProgress; // TODO who deletes it
316 ProgressInterface::setInstance(progress_);
319 dynamic_cast<GuiProgress*>(progress_),
320 SIGNAL(updateStatusBarMessage(QString const&)),
321 gv, SLOT(updateStatusBarMessage(QString const&)));
323 dynamic_cast<GuiProgress*>(progress_),
324 SIGNAL(clearMessageText()),
325 gv, SLOT(clearMessageText()));
332 delete stack_widget_;
335 QMenu * toolBarPopup(GuiView * parent)
337 // FIXME: translation
338 QMenu * menu = new QMenu(parent);
339 QActionGroup * iconSizeGroup = new QActionGroup(parent);
341 QAction * smallIcons = new QAction(iconSizeGroup);
342 smallIcons->setText(qt_("Small-sized icons"));
343 smallIcons->setCheckable(true);
344 QObject::connect(smallIcons, SIGNAL(triggered()),
345 parent, SLOT(smallSizedIcons()));
346 menu->addAction(smallIcons);
348 QAction * normalIcons = new QAction(iconSizeGroup);
349 normalIcons->setText(qt_("Normal-sized icons"));
350 normalIcons->setCheckable(true);
351 QObject::connect(normalIcons, SIGNAL(triggered()),
352 parent, SLOT(normalSizedIcons()));
353 menu->addAction(normalIcons);
355 QAction * bigIcons = new QAction(iconSizeGroup);
356 bigIcons->setText(qt_("Big-sized icons"));
357 bigIcons->setCheckable(true);
358 QObject::connect(bigIcons, SIGNAL(triggered()),
359 parent, SLOT(bigSizedIcons()));
360 menu->addAction(bigIcons);
362 QAction * hugeIcons = new QAction(iconSizeGroup);
363 hugeIcons->setText(qt_("Huge-sized icons"));
364 hugeIcons->setCheckable(true);
365 QObject::connect(hugeIcons, SIGNAL(triggered()),
366 parent, SLOT(hugeSizedIcons()));
367 menu->addAction(hugeIcons);
369 QAction * giantIcons = new QAction(iconSizeGroup);
370 giantIcons->setText(qt_("Giant-sized icons"));
371 giantIcons->setCheckable(true);
372 QObject::connect(giantIcons, SIGNAL(triggered()),
373 parent, SLOT(giantSizedIcons()));
374 menu->addAction(giantIcons);
376 unsigned int cur = parent->iconSize().width();
377 if ( cur == parent->d.smallIconSize)
378 smallIcons->setChecked(true);
379 else if (cur == parent->d.normalIconSize)
380 normalIcons->setChecked(true);
381 else if (cur == parent->d.bigIconSize)
382 bigIcons->setChecked(true);
383 else if (cur == parent->d.hugeIconSize)
384 hugeIcons->setChecked(true);
385 else if (cur == parent->d.giantIconSize)
386 giantIcons->setChecked(true);
393 stack_widget_->setCurrentWidget(bg_widget_);
394 bg_widget_->setUpdatesEnabled(true);
395 bg_widget_->setFocus();
398 int tabWorkAreaCount()
400 return splitter_->count();
403 TabWorkArea * tabWorkArea(int i)
405 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
408 TabWorkArea * currentTabWorkArea()
410 int areas = tabWorkAreaCount();
412 // The first TabWorkArea is always the first one, if any.
413 return tabWorkArea(0);
415 for (int i = 0; i != areas; ++i) {
416 TabWorkArea * twa = tabWorkArea(i);
417 if (current_main_work_area_ == twa->currentWorkArea())
421 // None has the focus so we just take the first one.
422 return tabWorkArea(0);
425 int countWorkAreasOf(Buffer & buf)
427 int areas = tabWorkAreaCount();
429 for (int i = 0; i != areas; ++i) {
430 TabWorkArea * twa = tabWorkArea(i);
431 if (twa->workArea(buf))
437 void setPreviewFuture(QFuture<Buffer::ExportStatus> const & f)
439 if (processing_thread_watcher_.isRunning()) {
440 // we prefer to cancel this preview in order to keep a snappy
444 processing_thread_watcher_.setFuture(f);
449 GuiWorkArea * current_work_area_;
450 GuiWorkArea * current_main_work_area_;
451 QSplitter * splitter_;
452 QStackedWidget * stack_widget_;
453 BackgroundWidget * bg_widget_;
455 ToolbarMap toolbars_;
456 ProgressInterface* progress_;
457 /// The main layout box.
459 * \warning Don't Delete! The layout box is actually owned by
460 * whichever toolbar contains it. All the GuiView class needs is a
461 * means of accessing it.
463 * FIXME: replace that with a proper model so that we are not limited
464 * to only one dialog.
469 map<string, DialogPtr> dialogs_;
471 unsigned int smallIconSize;
472 unsigned int normalIconSize;
473 unsigned int bigIconSize;
474 unsigned int hugeIconSize;
475 unsigned int giantIconSize;
477 QTimer statusbar_timer_;
478 /// auto-saving of buffers
479 Timeout autosave_timeout_;
480 /// flag against a race condition due to multiclicks, see bug #1119
484 TocModels toc_models_;
487 QFutureWatcher<docstring> autosave_watcher_;
488 QFutureWatcher<Buffer::ExportStatus> processing_thread_watcher_;
490 string last_export_format;
491 string processing_format;
493 static QSet<Buffer const *> busyBuffers;
494 static Buffer::ExportStatus previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
495 static Buffer::ExportStatus exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
496 static Buffer::ExportStatus compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
497 static docstring autosaveAndDestroy(Buffer const * orig, Buffer * buffer);
500 static Buffer::ExportStatus runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format);
502 // TODO syncFunc/previewFunc: use bind
503 bool asyncBufferProcessing(string const & argument,
504 Buffer const * used_buffer,
505 docstring const & msg,
506 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
507 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
508 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const);
510 QVector<GuiWorkArea*> guiWorkAreas();
513 QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
516 GuiView::GuiView(int id)
517 : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0)
519 // GuiToolbars *must* be initialised before the menu bar.
520 normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
523 // set ourself as the current view. This is needed for the menu bar
524 // filling, at least for the static special menu item on Mac. Otherwise
525 // they are greyed out.
526 guiApp->setCurrentView(this);
528 // Fill up the menu bar.
529 guiApp->menus().fillMenuBar(menuBar(), this, true);
531 setCentralWidget(d.stack_widget_);
533 // Start autosave timer
534 if (lyxrc.autosave) {
535 d.autosave_timeout_.timeout.connect(bind(&GuiView::autoSave, this));
536 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
537 d.autosave_timeout_.start();
539 connect(&d.statusbar_timer_, SIGNAL(timeout()),
540 this, SLOT(clearMessage()));
542 // We don't want to keep the window in memory if it is closed.
543 setAttribute(Qt::WA_DeleteOnClose, true);
545 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
546 // QIcon::fromTheme was introduced in Qt 4.6
547 #if (QT_VERSION >= 0x040600)
548 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
549 // since the icon is provided in the application bundle. We use a themed
550 // version when available and use the bundled one as fallback.
551 setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
553 setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
557 resetWindowTitleAndIconText();
559 // use tabbed dock area for multiple docks
560 // (such as "source" and "messages")
561 setDockOptions(QMainWindow::ForceTabbedDocks);
564 setAcceptDrops(true);
566 // add busy indicator to statusbar
567 QLabel * busylabel = new QLabel(statusBar());
568 statusBar()->addPermanentWidget(busylabel);
569 search_mode mode = theGuiApp()->imageSearchMode();
570 QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
571 QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
572 busylabel->setMovie(busyanim);
576 connect(&d.processing_thread_watcher_, SIGNAL(started()),
577 busylabel, SLOT(show()));
578 connect(&d.processing_thread_watcher_, SIGNAL(finished()),
579 busylabel, SLOT(hide()));
581 statusBar()->setSizeGripEnabled(true);
584 connect(&d.autosave_watcher_, SIGNAL(finished()), this,
585 SLOT(autoSaveThreadFinished()));
587 connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
588 SLOT(processingThreadStarted()));
589 connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
590 SLOT(processingThreadFinished()));
592 connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
593 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
595 // Forbid too small unresizable window because it can happen
596 // with some window manager under X11.
597 setMinimumSize(300, 200);
599 if (lyxrc.allow_geometry_session) {
600 // Now take care of session management.
605 // no session handling, default to a sane size.
606 setGeometry(50, 50, 690, 510);
609 // clear session data if any.
611 settings.remove("views");
621 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
623 QVector<GuiWorkArea*> areas;
624 for (int i = 0; i < tabWorkAreaCount(); i++) {
625 TabWorkArea* ta = tabWorkArea(i);
626 for (int u = 0; u < ta->count(); u++) {
627 areas << ta->workArea(u);
633 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
634 string const & format)
636 docstring const fmt = formats.prettyName(format);
639 case Buffer::ExportSuccess:
640 msg = bformat(_("Successful export to format: %1$s"), fmt);
642 case Buffer::ExportCancel:
643 msg = _("Document export cancelled.");
645 case Buffer::ExportError:
646 case Buffer::ExportNoPathToFormat:
647 case Buffer::ExportTexPathHasSpaces:
648 case Buffer::ExportConverterError:
649 msg = bformat(_("Error while exporting format: %1$s"), fmt);
651 case Buffer::PreviewSuccess:
652 msg = bformat(_("Successful preview of format: %1$s"), fmt);
654 case Buffer::PreviewError:
655 msg = bformat(_("Error while previewing format: %1$s"), fmt);
662 void GuiView::processingThreadStarted()
667 void GuiView::processingThreadFinished()
669 QFutureWatcher<Buffer::ExportStatus> const * watcher =
670 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
672 Buffer::ExportStatus const status = watcher->result();
673 handleExportStatus(this, status, d.processing_format);
676 BufferView const * const bv = currentBufferView();
677 if (bv && !bv->buffer().errorList("Export").empty()) {
681 errors(d.last_export_format);
685 void GuiView::autoSaveThreadFinished()
687 QFutureWatcher<docstring> const * watcher =
688 static_cast<QFutureWatcher<docstring> const *>(sender());
689 message(watcher->result());
694 void GuiView::saveLayout() const
697 settings.beginGroup("views");
698 settings.beginGroup(QString::number(id_));
699 #if defined(Q_WS_X11) || defined(QPA_XCB)
700 settings.setValue("pos", pos());
701 settings.setValue("size", size());
703 settings.setValue("geometry", saveGeometry());
705 settings.setValue("layout", saveState(0));
706 settings.setValue("icon_size", iconSize());
710 void GuiView::saveUISettings() const
712 // Save the toolbar private states
713 ToolbarMap::iterator end = d.toolbars_.end();
714 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
715 it->second->saveSession();
716 // Now take care of all other dialogs
717 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
718 for (; it!= d.dialogs_.end(); ++it)
719 it->second->saveSession();
723 bool GuiView::restoreLayout()
726 settings.beginGroup("views");
727 settings.beginGroup(QString::number(id_));
728 QString const icon_key = "icon_size";
729 if (!settings.contains(icon_key))
732 //code below is skipped when when ~/.config/LyX is (re)created
733 QSize icon_size = settings.value(icon_key).toSize();
734 // Check whether session size changed.
735 if (icon_size.width() != int(d.smallIconSize) &&
736 icon_size.width() != int(d.normalIconSize) &&
737 icon_size.width() != int(d.bigIconSize) &&
738 icon_size.width() != int(d.hugeIconSize) &&
739 icon_size.width() != int(d.giantIconSize)) {
740 icon_size.setWidth(d.normalIconSize);
741 icon_size.setHeight(d.normalIconSize);
743 setIconSize(icon_size);
745 #if defined(Q_WS_X11) || defined(QPA_XCB)
746 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
747 QSize size = settings.value("size", QSize(690, 510)).toSize();
751 // Work-around for bug #6034: the window ends up in an undetermined
752 // state when trying to restore a maximized window when it is
753 // already maximized.
754 if (!(windowState() & Qt::WindowMaximized))
755 if (!restoreGeometry(settings.value("geometry").toByteArray()))
756 setGeometry(50, 50, 690, 510);
758 // Make sure layout is correctly oriented.
759 setLayoutDirection(qApp->layoutDirection());
761 // Allow the toc and view-source dock widget to be restored if needed.
763 if ((dialog = findOrBuild("toc", true)))
764 // see bug 5082. At least setup title and enabled state.
765 // Visibility will be adjusted by restoreState below.
766 dialog->prepareView();
767 if ((dialog = findOrBuild("view-source", true)))
768 dialog->prepareView();
769 if ((dialog = findOrBuild("progress", true)))
770 dialog->prepareView();
772 if (!restoreState(settings.value("layout").toByteArray(), 0))
775 // init the toolbars that have not been restored
776 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
777 Toolbars::Infos::iterator end = guiApp->toolbars().end();
778 for (; cit != end; ++cit) {
779 GuiToolbar * tb = toolbar(cit->name);
780 if (tb && !tb->isRestored())
781 initToolbar(cit->name);
789 GuiToolbar * GuiView::toolbar(string const & name)
791 ToolbarMap::iterator it = d.toolbars_.find(name);
792 if (it != d.toolbars_.end())
795 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
800 void GuiView::constructToolbars()
802 ToolbarMap::iterator it = d.toolbars_.begin();
803 for (; it != d.toolbars_.end(); ++it)
807 // I don't like doing this here, but the standard toolbar
808 // destroys this object when it's destroyed itself (vfr)
809 d.layout_ = new LayoutBox(*this);
810 d.stack_widget_->addWidget(d.layout_);
811 d.layout_->move(0,0);
813 // extracts the toolbars from the backend
814 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
815 Toolbars::Infos::iterator end = guiApp->toolbars().end();
816 for (; cit != end; ++cit)
817 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
821 void GuiView::initToolbars()
823 // extracts the toolbars from the backend
824 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
825 Toolbars::Infos::iterator end = guiApp->toolbars().end();
826 for (; cit != end; ++cit)
827 initToolbar(cit->name);
831 void GuiView::initToolbar(string const & name)
833 GuiToolbar * tb = toolbar(name);
836 int const visibility = guiApp->toolbars().defaultVisibility(name);
837 bool newline = !(visibility & Toolbars::SAMEROW);
838 tb->setVisible(false);
839 tb->setVisibility(visibility);
841 if (visibility & Toolbars::TOP) {
843 addToolBarBreak(Qt::TopToolBarArea);
844 addToolBar(Qt::TopToolBarArea, tb);
847 if (visibility & Toolbars::BOTTOM) {
849 addToolBarBreak(Qt::BottomToolBarArea);
850 addToolBar(Qt::BottomToolBarArea, tb);
853 if (visibility & Toolbars::LEFT) {
855 addToolBarBreak(Qt::LeftToolBarArea);
856 addToolBar(Qt::LeftToolBarArea, tb);
859 if (visibility & Toolbars::RIGHT) {
861 addToolBarBreak(Qt::RightToolBarArea);
862 addToolBar(Qt::RightToolBarArea, tb);
865 if (visibility & Toolbars::ON)
866 tb->setVisible(true);
870 TocModels & GuiView::tocModels()
872 return d.toc_models_;
876 void GuiView::setFocus()
878 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
879 QMainWindow::setFocus();
883 bool GuiView::hasFocus() const
885 if (currentWorkArea())
886 return currentWorkArea()->hasFocus();
887 if (currentMainWorkArea())
888 return currentMainWorkArea()->hasFocus();
889 return d.bg_widget_->hasFocus();
893 void GuiView::focusInEvent(QFocusEvent * e)
895 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
896 QMainWindow::focusInEvent(e);
897 // Make sure guiApp points to the correct view.
898 guiApp->setCurrentView(this);
899 if (currentWorkArea())
900 currentWorkArea()->setFocus();
901 else if (currentMainWorkArea())
902 currentMainWorkArea()->setFocus();
904 d.bg_widget_->setFocus();
908 QMenu * GuiView::createPopupMenu()
910 return d.toolBarPopup(this);
914 void GuiView::showEvent(QShowEvent * e)
916 LYXERR(Debug::GUI, "Passed Geometry "
917 << size().height() << "x" << size().width()
918 << "+" << pos().x() << "+" << pos().y());
920 if (d.splitter_->count() == 0)
921 // No work area, switch to the background widget.
925 QMainWindow::showEvent(e);
929 bool GuiView::closeScheduled()
936 bool GuiView::prepareAllBuffersForLogout()
938 Buffer * first = theBufferList().first();
942 // First, iterate over all buffers and ask the users if unsaved
943 // changes should be saved.
944 // We cannot use a for loop as the buffer list cycles.
947 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
949 b = theBufferList().next(b);
950 } while (b != first);
952 // Next, save session state
953 // When a view/window was closed before without quitting LyX, there
954 // are already entries in the lastOpened list.
955 theSession().lastOpened().clear();
962 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
963 ** is responsibility of the container (e.g., dialog)
965 void GuiView::closeEvent(QCloseEvent * close_event)
967 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
969 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
970 Alert::warning(_("Exit LyX"),
971 _("LyX could not be closed because documents are being processed by LyX."));
972 close_event->setAccepted(false);
976 // If the user pressed the x (so we didn't call closeView
977 // programmatically), we want to clear all existing entries.
979 theSession().lastOpened().clear();
984 // it can happen that this event arrives without selecting the view,
985 // e.g. when clicking the close button on a background window.
987 if (!closeWorkAreaAll()) {
989 close_event->ignore();
993 // Make sure that nothing will use this to be closed View.
994 guiApp->unregisterView(this);
996 if (isFullScreen()) {
997 // Switch off fullscreen before closing.
1002 // Make sure the timer time out will not trigger a statusbar update.
1003 d.statusbar_timer_.stop();
1005 // Saving fullscreen requires additional tweaks in the toolbar code.
1006 // It wouldn't also work under linux natively.
1007 if (lyxrc.allow_geometry_session) {
1012 close_event->accept();
1016 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1018 if (event->mimeData()->hasUrls())
1020 /// \todo Ask lyx-devel is this is enough:
1021 /// if (event->mimeData()->hasFormat("text/plain"))
1022 /// event->acceptProposedAction();
1026 void GuiView::dropEvent(QDropEvent * event)
1028 QList<QUrl> files = event->mimeData()->urls();
1029 if (files.isEmpty())
1032 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1033 for (int i = 0; i != files.size(); ++i) {
1034 string const file = os::internal_path(fromqstr(
1035 files.at(i).toLocalFile()));
1039 string const ext = support::getExtension(file);
1040 vector<const Format *> found_formats;
1042 // Find all formats that have the correct extension.
1043 vector<const Format *> const & import_formats
1044 = theConverters().importableFormats();
1045 vector<const Format *>::const_iterator it = import_formats.begin();
1046 for (; it != import_formats.end(); ++it)
1047 if ((*it)->hasExtension(ext))
1048 found_formats.push_back(*it);
1051 if (found_formats.size() >= 1) {
1052 if (found_formats.size() > 1) {
1053 //FIXME: show a dialog to choose the correct importable format
1054 LYXERR(Debug::FILES,
1055 "Multiple importable formats found, selecting first");
1057 string const arg = found_formats[0]->name() + " " + file;
1058 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1061 //FIXME: do we have to explicitly check whether it's a lyx file?
1062 LYXERR(Debug::FILES,
1063 "No formats found, trying to open it as a lyx file");
1064 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1066 // add the functions to the queue
1067 guiApp->addToFuncRequestQueue(cmd);
1070 // now process the collected functions. We perform the events
1071 // asynchronously. This prevents potential problems in case the
1072 // BufferView is closed within an event.
1073 guiApp->processFuncRequestQueueAsync();
1077 void GuiView::message(docstring const & str)
1079 if (ForkedProcess::iAmAChild())
1082 // call is moved to GUI-thread by GuiProgress
1083 d.progress_->appendMessage(toqstr(str));
1087 void GuiView::clearMessageText()
1089 message(docstring());
1093 void GuiView::updateStatusBarMessage(QString const & str)
1095 statusBar()->showMessage(str);
1096 d.statusbar_timer_.stop();
1097 d.statusbar_timer_.start(3000);
1101 void GuiView::smallSizedIcons()
1103 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
1107 void GuiView::normalSizedIcons()
1109 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
1113 void GuiView::bigSizedIcons()
1115 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
1119 void GuiView::hugeSizedIcons()
1121 setIconSize(QSize(d.hugeIconSize, d.hugeIconSize));
1125 void GuiView::giantSizedIcons()
1127 setIconSize(QSize(d.giantIconSize, d.giantIconSize));
1131 void GuiView::clearMessage()
1133 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1134 // the hasFocus function mostly returns false, even if the focus is on
1135 // a workarea in this view.
1139 d.statusbar_timer_.stop();
1143 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1145 if (wa != d.current_work_area_
1146 || wa->bufferView().buffer().isInternal())
1148 setWindowTitle(qt_("LyX: ") + wa->windowTitle());
1149 setWindowIconText(wa->windowIconText());
1150 #if (QT_VERSION >= 0x040400)
1151 // Sets the path for the window: this is used by OSX to
1152 // allow a context click on the title bar showing a menu
1153 // with the path up to the file
1154 setWindowFilePath(toqstr(wa->bufferView().buffer().absFileName()));
1159 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1161 if (d.current_work_area_)
1162 QObject::disconnect(d.current_work_area_, SIGNAL(busy(bool)),
1163 this, SLOT(setBusy(bool)));
1165 disconnectBufferView();
1166 connectBufferView(wa->bufferView());
1167 connectBuffer(wa->bufferView().buffer());
1168 d.current_work_area_ = wa;
1169 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1170 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1171 QObject::connect(wa, SIGNAL(busy(bool)), this, SLOT(setBusy(bool)));
1172 updateWindowTitle(wa);
1176 // The document settings needs to be reinitialised.
1177 updateDialog("document", "");
1179 // Buffer-dependent dialogs must be updated. This is done here because
1180 // some dialogs require buffer()->text.
1185 void GuiView::on_lastWorkAreaRemoved()
1188 // We already are in a close event. Nothing more to do.
1191 if (d.splitter_->count() > 1)
1192 // We have a splitter so don't close anything.
1195 // Reset and updates the dialogs.
1196 d.toc_models_.reset(0);
1197 updateDialog("document", "");
1200 resetWindowTitleAndIconText();
1203 if (lyxrc.open_buffers_in_tabs)
1204 // Nothing more to do, the window should stay open.
1207 if (guiApp->viewIds().size() > 1) {
1213 // On Mac we also close the last window because the application stay
1214 // resident in memory. On other platforms we don't close the last
1215 // window because this would quit the application.
1221 void GuiView::updateStatusBar()
1223 // let the user see the explicit message
1224 if (d.statusbar_timer_.isActive())
1231 void GuiView::showMessage()
1235 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1236 if (msg.isEmpty()) {
1237 BufferView const * bv = currentBufferView();
1239 msg = toqstr(bv->cursor().currentState());
1241 msg = qt_("Welcome to LyX!");
1243 statusBar()->showMessage(msg);
1247 bool GuiView::event(QEvent * e)
1251 // Useful debug code:
1252 //case QEvent::ActivationChange:
1253 //case QEvent::WindowDeactivate:
1254 //case QEvent::Paint:
1255 //case QEvent::Enter:
1256 //case QEvent::Leave:
1257 //case QEvent::HoverEnter:
1258 //case QEvent::HoverLeave:
1259 //case QEvent::HoverMove:
1260 //case QEvent::StatusTip:
1261 //case QEvent::DragEnter:
1262 //case QEvent::DragLeave:
1263 //case QEvent::Drop:
1266 case QEvent::WindowActivate: {
1267 GuiView * old_view = guiApp->currentView();
1268 if (this == old_view) {
1270 return QMainWindow::event(e);
1272 if (old_view && old_view->currentBufferView()) {
1273 // save current selection to the selection buffer to allow
1274 // middle-button paste in this window.
1275 cap::saveSelection(old_view->currentBufferView()->cursor());
1277 guiApp->setCurrentView(this);
1278 if (d.current_work_area_) {
1279 BufferView & bv = d.current_work_area_->bufferView();
1280 connectBufferView(bv);
1281 connectBuffer(bv.buffer());
1282 // The document structure, name and dialogs might have
1283 // changed in another view.
1285 // The document settings needs to be reinitialised.
1286 updateDialog("document", "");
1289 resetWindowTitleAndIconText();
1292 return QMainWindow::event(e);
1295 case QEvent::ShortcutOverride: {
1297 if (isFullScreen() && menuBar()->isHidden()) {
1298 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1299 // FIXME: we should also try to detect special LyX shortcut such as
1300 // Alt-P and Alt-M. Right now there is a hack in
1301 // GuiWorkArea::processKeySym() that hides again the menubar for
1303 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1305 return QMainWindow::event(e);
1308 return QMainWindow::event(e);
1312 return QMainWindow::event(e);
1316 void GuiView::resetWindowTitleAndIconText()
1318 setWindowTitle(qt_("LyX"));
1319 setWindowIconText(qt_("LyX"));
1322 bool GuiView::focusNextPrevChild(bool /*next*/)
1329 bool GuiView::busy() const
1335 void GuiView::setBusy(bool busy)
1337 bool const busy_before = busy_ > 0;
1338 busy ? ++busy_ : --busy_;
1339 if ((busy_ > 0) == busy_before)
1340 // busy state didn't change
1344 QApplication::setOverrideCursor(Qt::WaitCursor);
1347 QApplication::restoreOverrideCursor();
1352 double GuiView::pixelRatio() const
1354 #if QT_VERSION >= 0x050000
1355 return devicePixelRatio();
1362 GuiWorkArea * GuiView::workArea(int index)
1364 if (TabWorkArea * twa = d.currentTabWorkArea())
1365 if (index < twa->count())
1366 return dynamic_cast<GuiWorkArea *>(twa->widget(index));
1371 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1373 if (currentWorkArea()
1374 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1375 return (GuiWorkArea *) currentWorkArea();
1376 if (TabWorkArea * twa = d.currentTabWorkArea())
1377 return twa->workArea(buffer);
1382 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1384 // Automatically create a TabWorkArea if there are none yet.
1385 TabWorkArea * tab_widget = d.splitter_->count()
1386 ? d.currentTabWorkArea() : addTabWorkArea();
1387 return tab_widget->addWorkArea(buffer, *this);
1391 TabWorkArea * GuiView::addTabWorkArea()
1393 TabWorkArea * twa = new TabWorkArea;
1394 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1395 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1396 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1397 this, SLOT(on_lastWorkAreaRemoved()));
1399 d.splitter_->addWidget(twa);
1400 d.stack_widget_->setCurrentWidget(d.splitter_);
1405 GuiWorkArea const * GuiView::currentWorkArea() const
1407 return d.current_work_area_;
1411 GuiWorkArea * GuiView::currentWorkArea()
1413 return d.current_work_area_;
1417 GuiWorkArea const * GuiView::currentMainWorkArea() const
1419 if (!d.currentTabWorkArea())
1421 return d.currentTabWorkArea()->currentWorkArea();
1425 GuiWorkArea * GuiView::currentMainWorkArea()
1427 if (!d.currentTabWorkArea())
1429 return d.currentTabWorkArea()->currentWorkArea();
1433 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1435 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1437 d.current_work_area_ = 0;
1442 // FIXME: I've no clue why this is here and why it accesses
1443 // theGuiApp()->currentView, which might be 0 (bug 6464).
1444 // See also 27525 (vfr).
1445 if (theGuiApp()->currentView() == this
1446 && theGuiApp()->currentView()->currentWorkArea() == wa)
1449 if (currentBufferView())
1450 cap::saveSelection(currentBufferView()->cursor());
1452 theGuiApp()->setCurrentView(this);
1453 d.current_work_area_ = wa;
1455 // We need to reset this now, because it will need to be
1456 // right if the tabWorkArea gets reset in the for loop. We
1457 // will change it back if we aren't in that case.
1458 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1459 d.current_main_work_area_ = wa;
1461 for (int i = 0; i != d.splitter_->count(); ++i) {
1462 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1463 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1464 << ", Current main wa: " << currentMainWorkArea());
1469 d.current_main_work_area_ = old_cmwa;
1471 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1472 on_currentWorkAreaChanged(wa);
1473 BufferView & bv = wa->bufferView();
1474 bv.cursor().fixIfBroken();
1476 wa->setUpdatesEnabled(true);
1477 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1481 void GuiView::removeWorkArea(GuiWorkArea * wa)
1483 LASSERT(wa, return);
1484 if (wa == d.current_work_area_) {
1486 disconnectBufferView();
1487 d.current_work_area_ = 0;
1488 d.current_main_work_area_ = 0;
1491 bool found_twa = false;
1492 for (int i = 0; i != d.splitter_->count(); ++i) {
1493 TabWorkArea * twa = d.tabWorkArea(i);
1494 if (twa->removeWorkArea(wa)) {
1495 // Found in this tab group, and deleted the GuiWorkArea.
1497 if (twa->count() != 0) {
1498 if (d.current_work_area_ == 0)
1499 // This means that we are closing the current GuiWorkArea, so
1500 // switch to the next GuiWorkArea in the found TabWorkArea.
1501 setCurrentWorkArea(twa->currentWorkArea());
1503 // No more WorkAreas in this tab group, so delete it.
1510 // It is not a tabbed work area (i.e., the search work area), so it
1511 // should be deleted by other means.
1512 LASSERT(found_twa, return);
1514 if (d.current_work_area_ == 0) {
1515 if (d.splitter_->count() != 0) {
1516 TabWorkArea * twa = d.currentTabWorkArea();
1517 setCurrentWorkArea(twa->currentWorkArea());
1519 // No more work areas, switch to the background widget.
1520 setCurrentWorkArea(0);
1526 LayoutBox * GuiView::getLayoutDialog() const
1532 void GuiView::updateLayoutList()
1535 d.layout_->updateContents(false);
1539 void GuiView::updateToolbars()
1541 ToolbarMap::iterator end = d.toolbars_.end();
1542 if (d.current_work_area_) {
1544 d.current_work_area_->bufferView().cursor().inMathed()
1545 && !d.current_work_area_->bufferView().cursor().inRegexped();
1547 lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
1549 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
1550 lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true);
1551 bool const mathmacrotemplate =
1552 lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
1554 lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled();
1556 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1557 it->second->update(math, table, review, mathmacrotemplate, ipa);
1559 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1560 it->second->update(false, false, false, false, false);
1564 void GuiView::setBuffer(Buffer * newBuffer)
1566 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1567 LASSERT(newBuffer, return);
1569 GuiWorkArea * wa = workArea(*newBuffer);
1572 newBuffer->masterBuffer()->updateBuffer();
1574 wa = addWorkArea(*newBuffer);
1575 // scroll to the position when the BufferView was last closed
1576 if (lyxrc.use_lastfilepos) {
1577 LastFilePosSection::FilePos filepos =
1578 theSession().lastFilePos().load(newBuffer->fileName());
1579 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1582 //Disconnect the old buffer...there's no new one.
1585 connectBuffer(*newBuffer);
1586 connectBufferView(wa->bufferView());
1587 setCurrentWorkArea(wa);
1591 void GuiView::connectBuffer(Buffer & buf)
1593 buf.setGuiDelegate(this);
1597 void GuiView::disconnectBuffer()
1599 if (d.current_work_area_)
1600 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1604 void GuiView::connectBufferView(BufferView & bv)
1606 bv.setGuiDelegate(this);
1610 void GuiView::disconnectBufferView()
1612 if (d.current_work_area_)
1613 d.current_work_area_->bufferView().setGuiDelegate(0);
1617 void GuiView::errors(string const & error_type, bool from_master)
1619 BufferView const * const bv = currentBufferView();
1623 #if EXPORT_in_THREAD
1624 // We are called with from_master == false by default, so we
1625 // have to figure out whether that is the case or not.
1626 ErrorList & el = bv->buffer().errorList(error_type);
1628 el = bv->buffer().masterBuffer()->errorList(error_type);
1632 ErrorList const & el = from_master ?
1633 bv->buffer().masterBuffer()->errorList(error_type) :
1634 bv->buffer().errorList(error_type);
1640 string data = error_type;
1642 data = "from_master|" + error_type;
1643 showDialog("errorlist", data);
1647 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1649 d.toc_models_.updateItem(toqstr(type), dit);
1653 void GuiView::structureChanged()
1655 d.toc_models_.reset(documentBufferView());
1656 // Navigator needs more than a simple update in this case. It needs to be
1658 updateDialog("toc", "");
1662 void GuiView::updateDialog(string const & name, string const & data)
1664 if (!isDialogVisible(name))
1667 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1668 if (it == d.dialogs_.end())
1671 Dialog * const dialog = it->second.get();
1672 if (dialog->isVisibleView())
1673 dialog->initialiseParams(data);
1677 BufferView * GuiView::documentBufferView()
1679 return currentMainWorkArea()
1680 ? ¤tMainWorkArea()->bufferView()
1685 BufferView const * GuiView::documentBufferView() const
1687 return currentMainWorkArea()
1688 ? ¤tMainWorkArea()->bufferView()
1693 BufferView * GuiView::currentBufferView()
1695 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1699 BufferView const * GuiView::currentBufferView() const
1701 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1705 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1706 Buffer const * orig, Buffer * clone)
1708 bool const success = clone->autoSave();
1710 busyBuffers.remove(orig);
1712 ? _("Automatic save done.")
1713 : _("Automatic save failed!");
1717 void GuiView::autoSave()
1719 LYXERR(Debug::INFO, "Running autoSave()");
1721 Buffer * buffer = documentBufferView()
1722 ? &documentBufferView()->buffer() : 0;
1724 resetAutosaveTimers();
1728 GuiViewPrivate::busyBuffers.insert(buffer);
1729 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1730 buffer, buffer->cloneBufferOnly());
1731 d.autosave_watcher_.setFuture(f);
1732 resetAutosaveTimers();
1736 void GuiView::resetAutosaveTimers()
1739 d.autosave_timeout_.restart();
1743 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1746 Buffer * buf = currentBufferView()
1747 ? ¤tBufferView()->buffer() : 0;
1748 Buffer * doc_buffer = documentBufferView()
1749 ? &(documentBufferView()->buffer()) : 0;
1751 /* In LyX/Mac, when a dialog is open, the menus of the
1752 application can still be accessed without giving focus to
1753 the main window. In this case, we want to disable the menu
1754 entries that are buffer-related.
1756 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1761 // Check whether we need a buffer
1762 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1763 // no, exit directly
1764 flag.message(from_utf8(N_("Command not allowed with"
1765 "out any document open")));
1766 flag.setEnabled(false);
1770 if (cmd.origin() == FuncRequest::TOC) {
1771 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1772 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1773 flag.setEnabled(false);
1777 switch(cmd.action()) {
1778 case LFUN_BUFFER_IMPORT:
1781 case LFUN_MASTER_BUFFER_UPDATE:
1782 case LFUN_MASTER_BUFFER_VIEW:
1784 && (doc_buffer->parent() != 0
1785 || doc_buffer->hasChildren())
1786 && !d.processing_thread_watcher_.isRunning();
1789 case LFUN_BUFFER_UPDATE:
1790 case LFUN_BUFFER_VIEW: {
1791 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1795 string format = to_utf8(cmd.argument());
1796 if (cmd.argument().empty())
1797 format = doc_buffer->params().getDefaultOutputFormat();
1798 enable = doc_buffer->params().isExportableFormat(format);
1802 case LFUN_BUFFER_RELOAD:
1803 enable = doc_buffer && !doc_buffer->isUnnamed()
1804 && doc_buffer->fileName().exists()
1805 && (!doc_buffer->isClean()
1806 || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1809 case LFUN_BUFFER_CHILD_OPEN:
1810 enable = doc_buffer;
1813 case LFUN_BUFFER_WRITE:
1814 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1817 //FIXME: This LFUN should be moved to GuiApplication.
1818 case LFUN_BUFFER_WRITE_ALL: {
1819 // We enable the command only if there are some modified buffers
1820 Buffer * first = theBufferList().first();
1825 // We cannot use a for loop as the buffer list is a cycle.
1827 if (!b->isClean()) {
1831 b = theBufferList().next(b);
1832 } while (b != first);
1836 case LFUN_BUFFER_WRITE_AS:
1837 case LFUN_BUFFER_EXPORT_AS:
1838 enable = doc_buffer;
1841 case LFUN_BUFFER_CLOSE:
1842 case LFUN_VIEW_CLOSE:
1843 enable = doc_buffer;
1846 case LFUN_BUFFER_CLOSE_ALL:
1847 enable = theBufferList().last() != theBufferList().first();
1850 case LFUN_VIEW_SPLIT:
1851 if (cmd.getArg(0) == "vertical")
1852 enable = doc_buffer && (d.splitter_->count() == 1 ||
1853 d.splitter_->orientation() == Qt::Vertical);
1855 enable = doc_buffer && (d.splitter_->count() == 1 ||
1856 d.splitter_->orientation() == Qt::Horizontal);
1859 case LFUN_TAB_GROUP_CLOSE:
1860 enable = d.tabWorkAreaCount() > 1;
1863 case LFUN_TOOLBAR_TOGGLE: {
1864 string const name = cmd.getArg(0);
1865 if (GuiToolbar * t = toolbar(name))
1866 flag.setOnOff(t->isVisible());
1869 docstring const msg =
1870 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1876 case LFUN_DROP_LAYOUTS_CHOICE:
1880 case LFUN_UI_TOGGLE:
1881 flag.setOnOff(isFullScreen());
1884 case LFUN_DIALOG_DISCONNECT_INSET:
1887 case LFUN_DIALOG_HIDE:
1888 // FIXME: should we check if the dialog is shown?
1891 case LFUN_DIALOG_TOGGLE:
1892 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1893 // fall through to set "enable"
1894 case LFUN_DIALOG_SHOW: {
1895 string const name = cmd.getArg(0);
1897 enable = name == "aboutlyx"
1898 || name == "file" //FIXME: should be removed.
1900 || name == "texinfo"
1901 || name == "progress"
1902 || name == "compare";
1903 else if (name == "print")
1904 enable = doc_buffer->params().isExportable("dvi")
1905 && lyxrc.print_command != "none";
1906 else if (name == "character" || name == "symbols"
1907 || name == "mathdelimiter" || name == "mathmatrix") {
1908 if (!buf || buf->isReadonly())
1911 Cursor const & cur = currentBufferView()->cursor();
1912 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1915 else if (name == "latexlog")
1916 enable = FileName(doc_buffer->logName()).isReadableFile();
1917 else if (name == "spellchecker")
1918 enable = theSpellChecker()
1919 && !doc_buffer->isReadonly()
1920 && !doc_buffer->text().empty();
1921 else if (name == "vclog")
1922 enable = doc_buffer->lyxvc().inUse();
1926 case LFUN_DIALOG_UPDATE: {
1927 string const name = cmd.getArg(0);
1929 enable = name == "prefs";
1933 case LFUN_COMMAND_EXECUTE:
1935 case LFUN_MENU_OPEN:
1936 // Nothing to check.
1939 case LFUN_COMPLETION_INLINE:
1940 if (!d.current_work_area_
1941 || !d.current_work_area_->completer().inlinePossible(
1942 currentBufferView()->cursor()))
1946 case LFUN_COMPLETION_POPUP:
1947 if (!d.current_work_area_
1948 || !d.current_work_area_->completer().popupPossible(
1949 currentBufferView()->cursor()))
1954 if (!d.current_work_area_
1955 || !d.current_work_area_->completer().inlinePossible(
1956 currentBufferView()->cursor()))
1960 case LFUN_COMPLETION_ACCEPT:
1961 if (!d.current_work_area_
1962 || (!d.current_work_area_->completer().popupVisible()
1963 && !d.current_work_area_->completer().inlineVisible()
1964 && !d.current_work_area_->completer().completionAvailable()))
1968 case LFUN_COMPLETION_CANCEL:
1969 if (!d.current_work_area_
1970 || (!d.current_work_area_->completer().popupVisible()
1971 && !d.current_work_area_->completer().inlineVisible()))
1975 case LFUN_BUFFER_ZOOM_OUT:
1976 enable = doc_buffer && lyxrc.zoom > 10;
1979 case LFUN_BUFFER_ZOOM_IN:
1980 enable = doc_buffer;
1983 case LFUN_BUFFER_MOVE_NEXT:
1984 case LFUN_BUFFER_MOVE_PREVIOUS:
1985 // we do not cycle when moving
1986 case LFUN_BUFFER_NEXT:
1987 case LFUN_BUFFER_PREVIOUS:
1988 // because we cycle, it doesn't matter whether on first or last
1989 enable = (d.currentTabWorkArea()->count() > 1);
1991 case LFUN_BUFFER_SWITCH:
1992 // toggle on the current buffer, but do not toggle off
1993 // the other ones (is that a good idea?)
1995 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
1996 flag.setOnOff(true);
1999 case LFUN_VC_REGISTER:
2000 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2002 case LFUN_VC_RENAME:
2003 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2006 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2008 case LFUN_VC_CHECK_IN:
2009 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2011 case LFUN_VC_CHECK_OUT:
2012 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2014 case LFUN_VC_LOCKING_TOGGLE:
2015 enable = doc_buffer && !doc_buffer->isReadonly()
2016 && doc_buffer->lyxvc().lockingToggleEnabled();
2017 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2019 case LFUN_VC_REVERT:
2020 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2022 case LFUN_VC_UNDO_LAST:
2023 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2025 case LFUN_VC_REPO_UPDATE:
2026 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2028 case LFUN_VC_COMMAND: {
2029 if (cmd.argument().empty())
2031 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2035 case LFUN_VC_COMPARE:
2036 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2039 case LFUN_SERVER_GOTO_FILE_ROW:
2041 case LFUN_FORWARD_SEARCH:
2042 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2045 case LFUN_FILE_INSERT_PLAINTEXT:
2046 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2047 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2050 case LFUN_SPELLING_CONTINUOUSLY:
2051 flag.setOnOff(lyxrc.spellcheck_continuously);
2059 flag.setEnabled(false);
2065 static FileName selectTemplateFile()
2067 FileDialog dlg(qt_("Select template file"));
2068 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2069 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2071 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2072 QStringList(qt_("LyX Documents (*.lyx)")));
2074 if (result.first == FileDialog::Later)
2076 if (result.second.isEmpty())
2078 return FileName(fromqstr(result.second));
2082 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2086 Buffer * newBuffer = 0;
2088 newBuffer = checkAndLoadLyXFile(filename);
2089 } catch (ExceptionMessage const & e) {
2096 message(_("Document not loaded."));
2100 setBuffer(newBuffer);
2101 newBuffer->errors("Parse");
2104 theSession().lastFiles().add(filename);
2110 void GuiView::openDocument(string const & fname)
2112 string initpath = lyxrc.document_path;
2114 if (documentBufferView()) {
2115 string const trypath = documentBufferView()->buffer().filePath();
2116 // If directory is writeable, use this as default.
2117 if (FileName(trypath).isDirWritable())
2123 if (fname.empty()) {
2124 FileDialog dlg(qt_("Select document to open"));
2125 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2126 dlg.setButton2(qt_("Examples|#E#e"),
2127 toqstr(addPath(package().system_support().absFileName(), "examples")));
2129 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2130 FileDialog::Result result =
2131 dlg.open(toqstr(initpath), filter);
2133 if (result.first == FileDialog::Later)
2136 filename = fromqstr(result.second);
2138 // check selected filename
2139 if (filename.empty()) {
2140 message(_("Canceled."));
2146 // get absolute path of file and add ".lyx" to the filename if
2148 FileName const fullname =
2149 fileSearch(string(), filename, "lyx", support::may_not_exist);
2150 if (!fullname.empty())
2151 filename = fullname.absFileName();
2153 if (!fullname.onlyPath().isDirectory()) {
2154 Alert::warning(_("Invalid filename"),
2155 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2156 from_utf8(fullname.absFileName())));
2160 // if the file doesn't exist and isn't already open (bug 6645),
2161 // let the user create one
2162 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2163 !LyXVC::file_not_found_hook(fullname)) {
2164 // the user specifically chose this name. Believe him.
2165 Buffer * const b = newFile(filename, string(), true);
2171 docstring const disp_fn = makeDisplayPath(filename);
2172 message(bformat(_("Opening document %1$s..."), disp_fn));
2175 Buffer * buf = loadDocument(fullname);
2177 str2 = bformat(_("Document %1$s opened."), disp_fn);
2178 if (buf->lyxvc().inUse())
2179 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2180 " " + _("Version control detected.");
2182 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2187 // FIXME: clean that
2188 static bool import(GuiView * lv, FileName const & filename,
2189 string const & format, ErrorList & errorList)
2191 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2193 string loader_format;
2194 vector<string> loaders = theConverters().loaders();
2195 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2196 vector<string>::const_iterator it = loaders.begin();
2197 vector<string>::const_iterator en = loaders.end();
2198 for (; it != en; ++it) {
2199 if (!theConverters().isReachable(format, *it))
2202 string const tofile =
2203 support::changeExtension(filename.absFileName(),
2204 formats.extension(*it));
2205 if (!theConverters().convert(0, filename, FileName(tofile),
2206 filename, format, *it, errorList))
2208 loader_format = *it;
2211 if (loader_format.empty()) {
2212 frontend::Alert::error(_("Couldn't import file"),
2213 bformat(_("No information for importing the format %1$s."),
2214 formats.prettyName(format)));
2218 loader_format = format;
2220 if (loader_format == "lyx") {
2221 Buffer * buf = lv->loadDocument(lyxfile);
2225 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2229 bool as_paragraphs = loader_format == "textparagraph";
2230 string filename2 = (loader_format == format) ? filename.absFileName()
2231 : support::changeExtension(filename.absFileName(),
2232 formats.extension(loader_format));
2233 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2235 guiApp->setCurrentView(lv);
2236 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2243 void GuiView::importDocument(string const & argument)
2246 string filename = split(argument, format, ' ');
2248 LYXERR(Debug::INFO, format << " file: " << filename);
2250 // need user interaction
2251 if (filename.empty()) {
2252 string initpath = lyxrc.document_path;
2253 if (documentBufferView()) {
2254 string const trypath = documentBufferView()->buffer().filePath();
2255 // If directory is writeable, use this as default.
2256 if (FileName(trypath).isDirWritable())
2260 docstring const text = bformat(_("Select %1$s file to import"),
2261 formats.prettyName(format));
2263 FileDialog dlg(toqstr(text));
2264 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2265 dlg.setButton2(qt_("Examples|#E#e"),
2266 toqstr(addPath(package().system_support().absFileName(), "examples")));
2268 docstring filter = formats.prettyName(format);
2271 filter += from_utf8(formats.extensions(format));
2274 FileDialog::Result result =
2275 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2277 if (result.first == FileDialog::Later)
2280 filename = fromqstr(result.second);
2282 // check selected filename
2283 if (filename.empty())
2284 message(_("Canceled."));
2287 if (filename.empty())
2290 // get absolute path of file
2291 FileName const fullname(support::makeAbsPath(filename));
2293 // Can happen if the user entered a path into the dialog
2295 if (fullname.onlyFileName().empty()) {
2296 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2297 "Aborting import."),
2298 from_utf8(fullname.absFileName()));
2299 frontend::Alert::error(_("File name error"), msg);
2300 message(_("Canceled."));
2305 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2307 // Check if the document already is open
2308 Buffer * buf = theBufferList().getBuffer(lyxfile);
2311 if (!closeBuffer()) {
2312 message(_("Canceled."));
2317 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2319 // if the file exists already, and we didn't do
2320 // -i lyx thefile.lyx, warn
2321 if (lyxfile.exists() && fullname != lyxfile) {
2323 docstring text = bformat(_("The document %1$s already exists.\n\n"
2324 "Do you want to overwrite that document?"), displaypath);
2325 int const ret = Alert::prompt(_("Overwrite document?"),
2326 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2329 message(_("Canceled."));
2334 message(bformat(_("Importing %1$s..."), displaypath));
2335 ErrorList errorList;
2336 if (import(this, fullname, format, errorList))
2337 message(_("imported."));
2339 message(_("file not imported!"));
2341 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2345 void GuiView::newDocument(string const & filename, bool from_template)
2347 FileName initpath(lyxrc.document_path);
2348 if (documentBufferView()) {
2349 FileName const trypath(documentBufferView()->buffer().filePath());
2350 // If directory is writeable, use this as default.
2351 if (trypath.isDirWritable())
2355 string templatefile;
2356 if (from_template) {
2357 templatefile = selectTemplateFile().absFileName();
2358 if (templatefile.empty())
2363 if (filename.empty())
2364 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2366 b = newFile(filename, templatefile, true);
2371 // If no new document could be created, it is unsure
2372 // whether there is a valid BufferView.
2373 if (currentBufferView())
2374 // Ensure the cursor is correctly positioned on screen.
2375 currentBufferView()->showCursor();
2379 void GuiView::insertLyXFile(docstring const & fname)
2381 BufferView * bv = documentBufferView();
2386 FileName filename(to_utf8(fname));
2387 if (filename.empty()) {
2388 // Launch a file browser
2390 string initpath = lyxrc.document_path;
2391 string const trypath = bv->buffer().filePath();
2392 // If directory is writeable, use this as default.
2393 if (FileName(trypath).isDirWritable())
2397 FileDialog dlg(qt_("Select LyX document to insert"));
2398 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2399 dlg.setButton2(qt_("Examples|#E#e"),
2400 toqstr(addPath(package().system_support().absFileName(),
2403 FileDialog::Result result = dlg.open(toqstr(initpath),
2404 QStringList(qt_("LyX Documents (*.lyx)")));
2406 if (result.first == FileDialog::Later)
2410 filename.set(fromqstr(result.second));
2412 // check selected filename
2413 if (filename.empty()) {
2414 // emit message signal.
2415 message(_("Canceled."));
2420 bv->insertLyXFile(filename);
2421 bv->buffer().errors("Parse");
2425 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2427 FileName fname = b.fileName();
2428 FileName const oldname = fname;
2430 if (!newname.empty()) {
2432 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2434 // Switch to this Buffer.
2437 // No argument? Ask user through dialog.
2439 FileDialog dlg(qt_("Choose a filename to save document as"));
2440 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2441 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2443 if (!isLyXFileName(fname.absFileName()))
2444 fname.changeExtension(".lyx");
2446 FileDialog::Result result =
2447 dlg.save(toqstr(fname.onlyPath().absFileName()),
2448 QStringList(qt_("LyX Documents (*.lyx)")),
2449 toqstr(fname.onlyFileName()));
2451 if (result.first == FileDialog::Later)
2454 fname.set(fromqstr(result.second));
2459 if (!isLyXFileName(fname.absFileName()))
2460 fname.changeExtension(".lyx");
2463 // fname is now the new Buffer location.
2465 // if there is already a Buffer open with this name, we do not want
2466 // to have another one. (the second test makes sure we're not just
2467 // trying to overwrite ourselves, which is fine.)
2468 if (theBufferList().exists(fname) && fname != oldname
2469 && theBufferList().getBuffer(fname) != &b) {
2470 docstring const text =
2471 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2472 "Please close it before attempting to overwrite it.\n"
2473 "Do you want to choose a new filename?"),
2474 from_utf8(fname.absFileName()));
2475 int const ret = Alert::prompt(_("Chosen File Already Open"),
2476 text, 0, 1, _("&Rename"), _("&Cancel"));
2478 case 0: return renameBuffer(b, docstring(), kind);
2479 case 1: return false;
2484 bool const existsLocal = fname.exists();
2485 bool const existsInVC = LyXVC::fileInVC(fname);
2486 if (existsLocal || existsInVC) {
2487 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2488 if (kind != LV_WRITE_AS && existsInVC) {
2489 // renaming to a name that is already in VC
2491 docstring text = bformat(_("The document %1$s "
2492 "is already registered.\n\n"
2493 "Do you want to choose a new name?"),
2495 docstring const title = (kind == LV_VC_RENAME) ?
2496 _("Rename document?") : _("Copy document?");
2497 docstring const button = (kind == LV_VC_RENAME) ?
2498 _("&Rename") : _("&Copy");
2499 int const ret = Alert::prompt(title, text, 0, 1,
2500 button, _("&Cancel"));
2502 case 0: return renameBuffer(b, docstring(), kind);
2503 case 1: return false;
2508 docstring text = bformat(_("The document %1$s "
2509 "already exists.\n\n"
2510 "Do you want to overwrite that document?"),
2512 int const ret = Alert::prompt(_("Overwrite document?"),
2513 text, 0, 2, _("&Overwrite"),
2514 _("&Rename"), _("&Cancel"));
2517 case 1: return renameBuffer(b, docstring(), kind);
2518 case 2: return false;
2524 case LV_VC_RENAME: {
2525 string msg = b.lyxvc().rename(fname);
2528 message(from_utf8(msg));
2532 string msg = b.lyxvc().copy(fname);
2535 message(from_utf8(msg));
2541 // LyXVC created the file already in case of LV_VC_RENAME or
2542 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2543 // relative paths of included stuff right if we moved e.g. from
2544 // /a/b.lyx to /a/c/b.lyx.
2546 bool const saved = saveBuffer(b, fname);
2553 struct PrettyNameComparator
2555 bool operator()(Format const *first, Format const *second) const {
2556 return compare_no_case(translateIfPossible(from_ascii(first->prettyname())),
2557 translateIfPossible(from_ascii(second->prettyname()))) <= 0;
2562 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2564 FileName fname = b.fileName();
2566 FileDialog dlg(qt_("Choose a filename to export the document as"));
2567 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2570 QString const anyformat = qt_("Guess from extension (*.*)");
2572 Formats::const_iterator it = formats.begin();
2573 vector<Format const *> export_formats;
2574 for (; it != formats.end(); ++it)
2575 if (it->documentFormat())
2576 export_formats.push_back(&(*it));
2577 PrettyNameComparator cmp;
2578 sort(export_formats.begin(), export_formats.end(), cmp);
2579 vector<Format const *>::const_iterator fit = export_formats.begin();
2580 map<QString, string> fmap;
2583 for (; fit != export_formats.end(); ++fit) {
2584 docstring const loc_prettyname =
2585 translateIfPossible(from_utf8((*fit)->prettyname()));
2586 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2588 from_ascii((*fit)->extension())));
2589 types << loc_filter;
2590 fmap[loc_filter] = (*fit)->name();
2591 if (from_ascii((*fit)->name()) == iformat) {
2592 filter = loc_filter;
2593 ext = (*fit)->extension();
2596 string ofname = fname.onlyFileName();
2598 ofname = support::changeExtension(ofname, ext);
2599 FileDialog::Result result =
2600 dlg.save(toqstr(fname.onlyPath().absFileName()),
2604 if (result.first != FileDialog::Chosen)
2608 fname.set(fromqstr(result.second));
2609 if (filter == anyformat)
2610 fmt_name = formats.getFormatFromExtension(fname.extension());
2612 fmt_name = fmap[filter];
2613 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2614 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2616 if (fmt_name.empty() || fname.empty())
2619 // fname is now the new Buffer location.
2620 if (FileName(fname).exists()) {
2621 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2622 docstring text = bformat(_("The document %1$s already "
2623 "exists.\n\nDo you want to "
2624 "overwrite that document?"),
2626 int const ret = Alert::prompt(_("Overwrite document?"),
2627 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2630 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2631 case 2: return false;
2635 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2638 return dr.dispatched();
2642 bool GuiView::saveBuffer(Buffer & b)
2644 return saveBuffer(b, FileName());
2648 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2650 if (workArea(b) && workArea(b)->inDialogMode())
2653 if (fn.empty() && b.isUnnamed())
2654 return renameBuffer(b, docstring());
2656 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2658 theSession().lastFiles().add(b.fileName());
2662 // Switch to this Buffer.
2665 // FIXME: we don't tell the user *WHY* the save failed !!
2666 docstring const file = makeDisplayPath(b.absFileName(), 30);
2667 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2668 "Do you want to rename the document and "
2669 "try again?"), file);
2670 int const ret = Alert::prompt(_("Rename and save?"),
2671 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2674 if (!renameBuffer(b, docstring()))
2683 return saveBuffer(b, fn);
2687 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2689 return closeWorkArea(wa, false);
2693 // We only want to close the buffer if it is not visible in other workareas
2694 // of the same view, nor in other views, and if this is not a child
2695 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2697 Buffer & buf = wa->bufferView().buffer();
2699 bool last_wa = d.countWorkAreasOf(buf) == 1
2700 && !inOtherView(buf) && !buf.parent();
2702 bool close_buffer = last_wa;
2705 if (lyxrc.close_buffer_with_last_view == "yes")
2707 else if (lyxrc.close_buffer_with_last_view == "no")
2708 close_buffer = false;
2711 if (buf.isUnnamed())
2712 file = from_utf8(buf.fileName().onlyFileName());
2714 file = buf.fileName().displayName(30);
2715 docstring const text = bformat(
2716 _("Last view on document %1$s is being closed.\n"
2717 "Would you like to close or hide the document?\n"
2719 "Hidden documents can be displayed back through\n"
2720 "the menu: View->Hidden->...\n"
2722 "To remove this question, set your preference in:\n"
2723 " Tools->Preferences->Look&Feel->UserInterface\n"
2725 int ret = Alert::prompt(_("Close or hide document?"),
2726 text, 0, 1, _("&Close"), _("&Hide"));
2727 close_buffer = (ret == 0);
2731 return closeWorkArea(wa, close_buffer);
2735 bool GuiView::closeBuffer()
2737 GuiWorkArea * wa = currentMainWorkArea();
2738 setCurrentWorkArea(wa);
2739 Buffer & buf = wa->bufferView().buffer();
2740 return wa && closeWorkArea(wa, !buf.parent());
2744 void GuiView::writeSession() const {
2745 GuiWorkArea const * active_wa = currentMainWorkArea();
2746 for (int i = 0; i < d.splitter_->count(); ++i) {
2747 TabWorkArea * twa = d.tabWorkArea(i);
2748 for (int j = 0; j < twa->count(); ++j) {
2749 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2750 Buffer & buf = wa->bufferView().buffer();
2751 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2757 bool GuiView::closeBufferAll()
2759 // Close the workareas in all other views
2760 QList<int> const ids = guiApp->viewIds();
2761 for (int i = 0; i != ids.size(); ++i) {
2762 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2766 // Close our own workareas
2767 if (!closeWorkAreaAll())
2770 // Now close the hidden buffers. We prevent hidden buffers from being
2771 // dirty, so we can just close them.
2772 theBufferList().closeAll();
2777 bool GuiView::closeWorkAreaAll()
2779 setCurrentWorkArea(currentMainWorkArea());
2781 // We might be in a situation that there is still a tabWorkArea, but
2782 // there are no tabs anymore. This can happen when we get here after a
2783 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2784 // many TabWorkArea's have no documents anymore.
2787 // We have to call count() each time, because it can happen that
2788 // more than one splitter will disappear in one iteration (bug 5998).
2789 for (; d.splitter_->count() > empty_twa; ) {
2790 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2792 if (twa->count() == 0)
2795 setCurrentWorkArea(twa->currentWorkArea());
2796 if (!closeTabWorkArea(twa))
2804 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2809 Buffer & buf = wa->bufferView().buffer();
2811 if (close_buffer && GuiViewPrivate::busyBuffers.contains(&buf)) {
2812 Alert::warning(_("Close document"),
2813 _("Document could not be closed because it is being processed by LyX."));
2818 return closeBuffer(buf);
2820 if (!inMultiTabs(wa))
2821 if (!saveBufferIfNeeded(buf, true))
2829 bool GuiView::closeBuffer(Buffer & buf)
2831 // If we are in a close_event all children will be closed in some time,
2832 // so no need to do it here. This will ensure that the children end up
2833 // in the session file in the correct order. If we close the master
2834 // buffer, we can close or release the child buffers here too.
2835 bool success = true;
2837 ListOfBuffers clist = buf.getChildren();
2838 ListOfBuffers::const_iterator it = clist.begin();
2839 ListOfBuffers::const_iterator const bend = clist.end();
2840 for (; it != bend; ++it) {
2841 // If a child is dirty, do not close
2842 // without user intervention
2843 //FIXME: should we look in other tabworkareas?
2844 Buffer * child_buf = *it;
2845 GuiWorkArea * child_wa = workArea(*child_buf);
2847 if (!closeWorkArea(child_wa, true)) {
2852 theBufferList().releaseChild(&buf, child_buf);
2856 // goto bookmark to update bookmark pit.
2857 //FIXME: we should update only the bookmarks related to this buffer!
2858 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2859 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2860 guiApp->gotoBookmark(i+1, false, false);
2862 if (saveBufferIfNeeded(buf, false)) {
2863 buf.removeAutosaveFile();
2864 theBufferList().release(&buf);
2868 // open all children again to avoid a crash because of dangling
2869 // pointers (bug 6603)
2875 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2877 while (twa == d.currentTabWorkArea()) {
2878 twa->setCurrentIndex(twa->count()-1);
2880 GuiWorkArea * wa = twa->currentWorkArea();
2881 Buffer & b = wa->bufferView().buffer();
2883 // We only want to close the buffer if the same buffer is not visible
2884 // in another view, and if this is not a child and if we are closing
2885 // a view (not a tabgroup).
2886 bool const close_buffer =
2887 !inOtherView(b) && !b.parent() && closing_;
2889 if (!closeWorkArea(wa, close_buffer))
2896 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2898 if (buf.isClean() || buf.paragraphs().empty())
2901 // Switch to this Buffer.
2906 if (buf.isUnnamed())
2907 file = from_utf8(buf.fileName().onlyFileName());
2909 file = buf.fileName().displayName(30);
2911 // Bring this window to top before asking questions.
2916 if (hiding && buf.isUnnamed()) {
2917 docstring const text = bformat(_("The document %1$s has not been "
2918 "saved yet.\n\nDo you want to save "
2919 "the document?"), file);
2920 ret = Alert::prompt(_("Save new document?"),
2921 text, 0, 1, _("&Save"), _("&Cancel"));
2925 docstring const text = bformat(_("The document %1$s has unsaved changes."
2926 "\n\nDo you want to save the document or discard the changes?"), file);
2927 ret = Alert::prompt(_("Save changed document?"),
2928 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2933 if (!saveBuffer(buf))
2937 // If we crash after this we could have no autosave file
2938 // but I guess this is really improbable (Jug).
2939 // Sometimes improbable things happen:
2940 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
2941 // buf.removeAutosaveFile();
2943 // revert all changes
2954 bool GuiView::inMultiTabs(GuiWorkArea * wa)
2956 Buffer & buf = wa->bufferView().buffer();
2958 for (int i = 0; i != d.splitter_->count(); ++i) {
2959 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
2960 if (wa_ && wa_ != wa)
2963 return inOtherView(buf);
2967 bool GuiView::inOtherView(Buffer & buf)
2969 QList<int> const ids = guiApp->viewIds();
2971 for (int i = 0; i != ids.size(); ++i) {
2975 if (guiApp->view(ids[i]).workArea(buf))
2982 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
2984 if (!documentBufferView())
2987 if (TabWorkArea * twa = d.currentTabWorkArea()) {
2988 Buffer * const curbuf = &documentBufferView()->buffer();
2989 int nwa = twa->count();
2990 for (int i = 0; i < nwa; ++i) {
2991 if (&workArea(i)->bufferView().buffer() == curbuf) {
2993 if (np == NEXTBUFFER)
2994 next_index = (i == nwa - 1 ? 0 : i + 1);
2996 next_index = (i == 0 ? nwa - 1 : i - 1);
2998 twa->moveTab(i, next_index);
3000 setBuffer(&workArea(next_index)->bufferView().buffer());
3008 /// make sure the document is saved
3009 static bool ensureBufferClean(Buffer * buffer)
3011 LASSERT(buffer, return false);
3012 if (buffer->isClean() && !buffer->isUnnamed())
3015 docstring const file = buffer->fileName().displayName(30);
3018 if (!buffer->isUnnamed()) {
3019 text = bformat(_("The document %1$s has unsaved "
3020 "changes.\n\nDo you want to save "
3021 "the document?"), file);
3022 title = _("Save changed document?");
3025 text = bformat(_("The document %1$s has not been "
3026 "saved yet.\n\nDo you want to save "
3027 "the document?"), file);
3028 title = _("Save new document?");
3030 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3033 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3035 return buffer->isClean() && !buffer->isUnnamed();
3039 bool GuiView::reloadBuffer(Buffer & buf)
3041 Buffer::ReadStatus status = buf.reload();
3042 return status == Buffer::ReadSuccess;
3046 void GuiView::checkExternallyModifiedBuffers()
3048 BufferList::iterator bit = theBufferList().begin();
3049 BufferList::iterator const bend = theBufferList().end();
3050 for (; bit != bend; ++bit) {
3051 Buffer * buf = *bit;
3052 if (buf->fileName().exists()
3053 && buf->isExternallyModified(Buffer::checksum_method)) {
3054 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3055 " Reload now? Any local changes will be lost."),
3056 from_utf8(buf->absFileName()));
3057 int const ret = Alert::prompt(_("Reload externally changed document?"),
3058 text, 0, 1, _("&Reload"), _("&Cancel"));
3066 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3068 Buffer * buffer = documentBufferView()
3069 ? &(documentBufferView()->buffer()) : 0;
3071 switch (cmd.action()) {
3072 case LFUN_VC_REGISTER:
3073 if (!buffer || !ensureBufferClean(buffer))
3075 if (!buffer->lyxvc().inUse()) {
3076 if (buffer->lyxvc().registrer()) {
3077 reloadBuffer(*buffer);
3078 dr.clearMessageUpdate();
3083 case LFUN_VC_RENAME:
3084 case LFUN_VC_COPY: {
3085 if (!buffer || !ensureBufferClean(buffer))
3087 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3088 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3089 // Some changes are not yet committed.
3090 // We test here and not in getStatus(), since
3091 // this test is expensive.
3093 LyXVC::CommandResult ret =
3094 buffer->lyxvc().checkIn(log);
3096 if (ret == LyXVC::ErrorCommand ||
3097 ret == LyXVC::VCSuccess)
3098 reloadBuffer(*buffer);
3099 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3100 frontend::Alert::error(
3101 _("Revision control error."),
3102 _("Document could not be checked in."));
3106 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3107 LV_VC_RENAME : LV_VC_COPY;
3108 renameBuffer(*buffer, cmd.argument(), kind);
3113 case LFUN_VC_CHECK_IN:
3114 if (!buffer || !ensureBufferClean(buffer))
3116 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3118 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3120 // Only skip reloading if the checkin was cancelled or
3121 // an error occurred before the real checkin VCS command
3122 // was executed, since the VCS might have changed the
3123 // file even if it could not checkin successfully.
3124 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3125 reloadBuffer(*buffer);
3129 case LFUN_VC_CHECK_OUT:
3130 if (!buffer || !ensureBufferClean(buffer))
3132 if (buffer->lyxvc().inUse()) {
3133 dr.setMessage(buffer->lyxvc().checkOut());
3134 reloadBuffer(*buffer);
3138 case LFUN_VC_LOCKING_TOGGLE:
3139 LASSERT(buffer, return);
3140 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3142 if (buffer->lyxvc().inUse()) {
3143 string res = buffer->lyxvc().lockingToggle();
3145 frontend::Alert::error(_("Revision control error."),
3146 _("Error when setting the locking property."));
3149 reloadBuffer(*buffer);
3154 case LFUN_VC_REVERT:
3155 LASSERT(buffer, return);
3156 if (buffer->lyxvc().revert()) {
3157 reloadBuffer(*buffer);
3158 dr.clearMessageUpdate();
3162 case LFUN_VC_UNDO_LAST:
3163 LASSERT(buffer, return);
3164 buffer->lyxvc().undoLast();
3165 reloadBuffer(*buffer);
3166 dr.clearMessageUpdate();
3169 case LFUN_VC_REPO_UPDATE:
3170 LASSERT(buffer, return);
3171 if (ensureBufferClean(buffer)) {
3172 dr.setMessage(buffer->lyxvc().repoUpdate());
3173 checkExternallyModifiedBuffers();
3177 case LFUN_VC_COMMAND: {
3178 string flag = cmd.getArg(0);
3179 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3182 if (contains(flag, 'M')) {
3183 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3186 string path = cmd.getArg(1);
3187 if (contains(path, "$$p") && buffer)
3188 path = subst(path, "$$p", buffer->filePath());
3189 LYXERR(Debug::LYXVC, "Directory: " << path);
3191 if (!pp.isReadableDirectory()) {
3192 lyxerr << _("Directory is not accessible.") << endl;
3195 support::PathChanger p(pp);
3197 string command = cmd.getArg(2);
3198 if (command.empty())
3201 command = subst(command, "$$i", buffer->absFileName());
3202 command = subst(command, "$$p", buffer->filePath());
3204 command = subst(command, "$$m", to_utf8(message));
3205 LYXERR(Debug::LYXVC, "Command: " << command);
3207 one.startscript(Systemcall::Wait, command);
3211 if (contains(flag, 'I'))
3212 buffer->markDirty();
3213 if (contains(flag, 'R'))
3214 reloadBuffer(*buffer);
3219 case LFUN_VC_COMPARE: {
3221 if (cmd.argument().empty()) {
3222 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3226 string rev1 = cmd.getArg(0);
3230 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3233 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3234 f2 = buffer->absFileName();
3236 string rev2 = cmd.getArg(1);
3240 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3244 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3245 f1 << "\n" << f2 << "\n" );
3246 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3247 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3257 void GuiView::openChildDocument(string const & fname)
3259 LASSERT(documentBufferView(), return);
3260 Buffer & buffer = documentBufferView()->buffer();
3261 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3262 documentBufferView()->saveBookmark(false);
3264 if (theBufferList().exists(filename)) {
3265 child = theBufferList().getBuffer(filename);
3268 message(bformat(_("Opening child document %1$s..."),
3269 makeDisplayPath(filename.absFileName())));
3270 child = loadDocument(filename, false);
3272 // Set the parent name of the child document.
3273 // This makes insertion of citations and references in the child work,
3274 // when the target is in the parent or another child document.
3276 child->setParent(&buffer);
3280 bool GuiView::goToFileRow(string const & argument)
3284 size_t i = argument.find_last_of(' ');
3285 if (i != string::npos) {
3286 file_name = os::internal_path(trim(argument.substr(0, i)));
3287 istringstream is(argument.substr(i + 1));
3292 if (i == string::npos) {
3293 LYXERR0("Wrong argument: " << argument);
3297 string const abstmp = package().temp_dir().absFileName();
3298 string const realtmp = package().temp_dir().realPath();
3299 // We have to use os::path_prefix_is() here, instead of
3300 // simply prefixIs(), because the file name comes from
3301 // an external application and may need case adjustment.
3302 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3303 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3304 // Needed by inverse dvi search. If it is a file
3305 // in tmpdir, call the apropriated function.
3306 // If tmpdir is a symlink, we may have the real
3307 // path passed back, so we correct for that.
3308 if (!prefixIs(file_name, abstmp))
3309 file_name = subst(file_name, realtmp, abstmp);
3310 buf = theBufferList().getBufferFromTmp(file_name);
3312 // Must replace extension of the file to be .lyx
3313 // and get full path
3314 FileName const s = fileSearch(string(),
3315 support::changeExtension(file_name, ".lyx"), "lyx");
3316 // Either change buffer or load the file
3317 if (theBufferList().exists(s))
3318 buf = theBufferList().getBuffer(s);
3319 else if (s.exists()) {
3320 buf = loadDocument(s);
3325 _("File does not exist: %1$s"),
3326 makeDisplayPath(file_name)));
3332 _("No buffer for file: %1$s."),
3333 makeDisplayPath(file_name))
3338 documentBufferView()->setCursorFromRow(row);
3344 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3346 Buffer::ExportStatus const status = func(format);
3348 // the cloning operation will have produced a clone of the entire set of
3349 // documents, starting from the master. so we must delete those.
3350 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3352 busyBuffers.remove(orig);
3357 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3359 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3360 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3364 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(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, false), orig, clone, format);
3371 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3373 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3374 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3378 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3379 string const & argument,
3380 Buffer const * used_buffer,
3381 docstring const & msg,
3382 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3383 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3384 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3389 string format = argument;
3391 format = used_buffer->params().getDefaultOutputFormat();
3392 processing_format = format;
3394 progress_->clearMessages();
3397 #if EXPORT_in_THREAD
3398 GuiViewPrivate::busyBuffers.insert(used_buffer);
3399 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3400 if (!cloned_buffer) {
3401 Alert::error(_("Export Error"),
3402 _("Error cloning the Buffer."));
3405 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3410 setPreviewFuture(f);
3411 last_export_format = used_buffer->params().bufferFormat();
3414 // We are asynchronous, so we don't know here anything about the success
3417 Buffer::ExportStatus status;
3419 // TODO check here if it breaks exporting with Qt < 4.4
3420 status = (used_buffer->*syncFunc)(format, true);
3421 } else if (previewFunc) {
3422 status = (used_buffer->*previewFunc)(format);
3425 handleExportStatus(gv_, status, format);
3427 return (status == Buffer::ExportSuccess
3428 || status == Buffer::PreviewSuccess);
3432 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3434 BufferView * bv = currentBufferView();
3435 LASSERT(bv, return);
3437 // Let the current BufferView dispatch its own actions.
3438 bv->dispatch(cmd, dr);
3439 if (dr.dispatched())
3442 // Try with the document BufferView dispatch if any.
3443 BufferView * doc_bv = documentBufferView();
3444 if (doc_bv && doc_bv != bv) {
3445 doc_bv->dispatch(cmd, dr);
3446 if (dr.dispatched())
3450 // Then let the current Cursor dispatch its own actions.
3451 bv->cursor().dispatch(cmd);
3453 // update completion. We do it here and not in
3454 // processKeySym to avoid another redraw just for a
3455 // changed inline completion
3456 if (cmd.origin() == FuncRequest::KEYBOARD) {
3457 if (cmd.action() == LFUN_SELF_INSERT
3458 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3459 updateCompletion(bv->cursor(), true, true);
3460 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3461 updateCompletion(bv->cursor(), false, true);
3463 updateCompletion(bv->cursor(), false, false);
3466 dr = bv->cursor().result();
3470 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3472 BufferView * bv = currentBufferView();
3473 // By default we won't need any update.
3474 dr.screenUpdate(Update::None);
3475 // assume cmd will be dispatched
3476 dr.dispatched(true);
3478 Buffer * doc_buffer = documentBufferView()
3479 ? &(documentBufferView()->buffer()) : 0;
3481 if (cmd.origin() == FuncRequest::TOC) {
3482 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3483 // FIXME: do we need to pass a DispatchResult object here?
3484 toc->doDispatch(bv->cursor(), cmd);
3488 string const argument = to_utf8(cmd.argument());
3490 switch(cmd.action()) {
3491 case LFUN_BUFFER_CHILD_OPEN:
3492 openChildDocument(to_utf8(cmd.argument()));
3495 case LFUN_BUFFER_IMPORT:
3496 importDocument(to_utf8(cmd.argument()));
3499 case LFUN_BUFFER_EXPORT: {
3502 FileName target_dir = doc_buffer->fileName().onlyPath();
3503 string const dest = cmd.getArg(1);
3504 if (!dest.empty() && FileName::isAbsolute(dest))
3505 target_dir = FileName(support::onlyPath(dest));
3506 // GCC only sees strfwd.h when building merged
3507 if (::lyx::operator==(cmd.argument(), "custom")) {
3508 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3511 if (!target_dir.isDirWritable()) {
3512 exportBufferAs(*doc_buffer, cmd.argument());
3515 /* TODO/Review: Is it a problem to also export the children?
3516 See the update_unincluded flag */
3517 d.asyncBufferProcessing(argument,
3520 &GuiViewPrivate::exportAndDestroy,
3523 // TODO Inform user about success
3527 case LFUN_BUFFER_EXPORT_AS: {
3528 LASSERT(doc_buffer, break);
3529 docstring f = cmd.argument();
3531 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3532 exportBufferAs(*doc_buffer, f);
3536 case LFUN_BUFFER_UPDATE: {
3537 d.asyncBufferProcessing(argument,
3540 &GuiViewPrivate::compileAndDestroy,
3545 case LFUN_BUFFER_VIEW: {
3546 d.asyncBufferProcessing(argument,
3548 _("Previewing ..."),
3549 &GuiViewPrivate::previewAndDestroy,
3554 case LFUN_MASTER_BUFFER_UPDATE: {
3555 d.asyncBufferProcessing(argument,
3556 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3558 &GuiViewPrivate::compileAndDestroy,
3563 case LFUN_MASTER_BUFFER_VIEW: {
3564 d.asyncBufferProcessing(argument,
3565 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3567 &GuiViewPrivate::previewAndDestroy,
3568 0, &Buffer::preview);
3571 case LFUN_BUFFER_SWITCH: {
3572 string const file_name = to_utf8(cmd.argument());
3573 if (!FileName::isAbsolute(file_name)) {
3575 dr.setMessage(_("Absolute filename expected."));
3579 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3582 dr.setMessage(_("Document not loaded"));
3586 // Do we open or switch to the buffer in this view ?
3587 if (workArea(*buffer)
3588 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3593 // Look for the buffer in other views
3594 QList<int> const ids = guiApp->viewIds();
3596 for (; i != ids.size(); ++i) {
3597 GuiView & gv = guiApp->view(ids[i]);
3598 if (gv.workArea(*buffer)) {
3599 gv.activateWindow();
3600 gv.setBuffer(buffer);
3605 // If necessary, open a new window as a last resort
3606 if (i == ids.size()) {
3607 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3613 case LFUN_BUFFER_NEXT:
3614 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3617 case LFUN_BUFFER_MOVE_NEXT:
3618 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3621 case LFUN_BUFFER_PREVIOUS:
3622 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3625 case LFUN_BUFFER_MOVE_PREVIOUS:
3626 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3629 case LFUN_COMMAND_EXECUTE: {
3630 bool const show_it = cmd.argument() != "off";
3631 // FIXME: this is a hack, "minibuffer" should not be
3633 if (GuiToolbar * t = toolbar("minibuffer")) {
3634 t->setVisible(show_it);
3635 if (show_it && t->commandBuffer())
3636 t->commandBuffer()->setFocus();
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);
4433 if (name == "print")
4434 return createGuiPrint(*this);
4436 return createGuiRef(*this);
4437 if (name == "sendto")
4438 return createGuiSendTo(*this);
4439 if (name == "spellchecker")
4440 return createGuiSpellchecker(*this);
4441 if (name == "symbols")
4442 return createGuiSymbols(*this);
4443 if (name == "tabularcreate")
4444 return createGuiTabularCreate(*this);
4445 if (name == "texinfo")
4446 return createGuiTexInfo(*this);
4447 if (name == "thesaurus")
4448 return createGuiThesaurus(*this);
4450 return createGuiToc(*this);
4451 if (name == "view-source")
4452 return createGuiViewSource(*this);
4454 return createGuiWrap(*this);
4455 if (name == "progress")
4456 return createGuiProgressView(*this);
4462 } // namespace frontend
4465 #include "moc_GuiView.cpp"