3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
8 * \author Abdelrazak Younes
11 * Full author contact details are available in file CREDITS.
19 #include "DispatchResult.h"
20 #include "FileDialog.h"
21 #include "FontLoader.h"
22 #include "GuiApplication.h"
23 #include "GuiCommandBuffer.h"
24 #include "GuiCompleter.h"
25 #include "GuiKeySymbol.h"
27 #include "GuiToolbar.h"
28 #include "GuiWorkArea.h"
29 #include "GuiProgress.h"
30 #include "LayoutBox.h"
34 #include "qt_helpers.h"
35 #include "support/filetools.h"
37 #include "frontends/alert.h"
38 #include "frontends/KeySymbol.h"
40 #include "buffer_funcs.h"
42 #include "BufferList.h"
43 #include "BufferParams.h"
44 #include "BufferView.h"
46 #include "Converter.h"
48 #include "CutAndPaste.h"
50 #include "ErrorList.h"
52 #include "FuncStatus.h"
53 #include "FuncRequest.h"
57 #include "LyXAction.h"
61 #include "Paragraph.h"
62 #include "SpellChecker.h"
65 #include "TextClass.h"
70 #include "support/convert.h"
71 #include "support/debug.h"
72 #include "support/ExceptionMessage.h"
73 #include "support/FileName.h"
74 #include "support/filetools.h"
75 #include "support/gettext.h"
76 #include "support/filetools.h"
77 #include "support/ForkedCalls.h"
78 #include "support/lassert.h"
79 #include "support/lstrings.h"
80 #include "support/os.h"
81 #include "support/Package.h"
82 #include "support/PathChanger.h"
83 #include "support/Systemcall.h"
84 #include "support/Timeout.h"
85 #include "support/ProgressInterface.h"
88 #include <QApplication>
89 #include <QCloseEvent>
91 #include <QDesktopWidget>
92 #include <QDragEnterEvent>
95 #include <QFutureWatcher>
104 #include <QPixmapCache>
106 #include <QPushButton>
107 #include <QScrollBar>
109 #include <QShowEvent>
111 #include <QStackedWidget>
112 #include <QStatusBar>
113 #include <QSvgRenderer>
114 #include <QtConcurrentRun>
122 // sync with GuiAlert.cpp
123 #define EXPORT_in_THREAD 1
126 #include "support/bind.h"
130 #ifdef HAVE_SYS_TIME_H
131 # include <sys/time.h>
139 using namespace lyx::support;
143 using support::addExtension;
144 using support::changeExtension;
145 using support::removeExtension;
151 class BackgroundWidget : public QWidget
154 BackgroundWidget(int width, int height)
155 : width_(width), height_(height)
157 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
158 if (!lyxrc.show_banner)
160 /// The text to be written on top of the pixmap
161 QString const text = lyx_version ?
162 qt_("version ") + lyx_version : qt_("unknown version");
163 #if QT_VERSION >= 0x050000
164 QString imagedir = "images/";
165 FileName fname = imageLibFileSearch(imagedir, "banner", "svgz");
166 QSvgRenderer svgRenderer(toqstr(fname.absFileName()));
167 if (svgRenderer.isValid()) {
168 splash_ = QPixmap(splashSize());
169 QPainter painter(&splash_);
170 svgRenderer.render(&painter);
171 splash_.setDevicePixelRatio(pixelRatio());
173 splash_ = getPixmap("images/", "banner", "png");
176 splash_ = getPixmap("images/", "banner", "svgz,png");
179 QPainter pain(&splash_);
180 pain.setPen(QColor(0, 0, 0));
181 qreal const fsize = fontSize();
182 QPointF const position = textPosition();
184 "widget pixel ratio: " << pixelRatio() <<
185 " splash pixel ratio: " << splashPixelRatio() <<
186 " version text size,position: " << fsize << "@" << position.x() << "+" << position.y());
188 // The font used to display the version info
189 font.setStyleHint(QFont::SansSerif);
190 font.setWeight(QFont::Bold);
191 font.setPointSizeF(fsize);
193 pain.drawText(position, text);
194 setFocusPolicy(Qt::StrongFocus);
197 void paintEvent(QPaintEvent *)
199 int const w = width_;
200 int const h = height_;
201 int const x = (width() - w) / 2;
202 int const y = (height() - h) / 2;
204 "widget pixel ratio: " << pixelRatio() <<
205 " splash pixel ratio: " << splashPixelRatio() <<
206 " paint pixmap: " << w << "x" << h << "@" << x << "+" << y);
208 pain.drawPixmap(x, y, w, h, splash_);
211 void keyPressEvent(QKeyEvent * ev)
214 setKeySymbol(&sym, ev);
216 guiApp->processKeySym(sym, q_key_state(ev->modifiers()));
228 /// Current ratio between physical pixels and device-independent pixels
229 double pixelRatio() const {
230 #if QT_VERSION >= 0x050000
231 return devicePixelRatio();
237 qreal fontSize() const {
238 return toqstr(lyxrc.font_sizes[FONT_SIZE_NORMAL]).toDouble();
241 QPointF textPosition() const {
242 return QPointF(width_/2 - 18, height_/2 + 45);
245 QSize splashSize() const {
247 static_cast<unsigned int>(width_ * pixelRatio()),
248 static_cast<unsigned int>(height_ * pixelRatio()));
251 /// Ratio between physical pixels and device-independent pixels of splash image
252 double splashPixelRatio() const {
253 #if QT_VERSION >= 0x050000
254 return splash_.devicePixelRatio();
262 /// Toolbar store providing access to individual toolbars by name.
263 typedef map<string, GuiToolbar *> ToolbarMap;
265 typedef shared_ptr<Dialog> DialogPtr;
270 class GuiView::GuiViewPrivate
273 GuiViewPrivate(GuiViewPrivate const &);
274 void operator=(GuiViewPrivate const &);
276 GuiViewPrivate(GuiView * gv)
277 : gv_(gv), current_work_area_(0), current_main_work_area_(0),
278 layout_(0), autosave_timeout_(5000),
281 // hardcode here the platform specific icon size
282 smallIconSize = 16; // scaling problems
283 normalIconSize = 20; // ok, default if iconsize.png is missing
284 bigIconSize = 26; // better for some math icons
285 hugeIconSize = 32; // better for hires displays
288 // if it exists, use width of iconsize.png as normal size
289 QString const dir = toqstr(addPath("images", lyxrc.icon_set));
290 FileName const fn = lyx::libFileSearch(dir, "iconsize.png");
292 QImage image(toqstr(fn.absFileName()));
293 if (image.width() < int(smallIconSize))
294 normalIconSize = smallIconSize;
295 else if (image.width() > int(giantIconSize))
296 normalIconSize = giantIconSize;
298 normalIconSize = image.width();
301 splitter_ = new QSplitter;
302 bg_widget_ = new BackgroundWidget(400, 250);
303 stack_widget_ = new QStackedWidget;
304 stack_widget_->addWidget(bg_widget_);
305 stack_widget_->addWidget(splitter_);
308 // TODO cleanup, remove the singleton, handle multiple Windows?
309 progress_ = ProgressInterface::instance();
310 if (!dynamic_cast<GuiProgress*>(progress_)) {
311 progress_ = new GuiProgress; // TODO who deletes it
312 ProgressInterface::setInstance(progress_);
315 dynamic_cast<GuiProgress*>(progress_),
316 SIGNAL(updateStatusBarMessage(QString const&)),
317 gv, SLOT(updateStatusBarMessage(QString const&)));
319 dynamic_cast<GuiProgress*>(progress_),
320 SIGNAL(clearMessageText()),
321 gv, SLOT(clearMessageText()));
328 delete stack_widget_;
331 QMenu * toolBarPopup(GuiView * parent)
333 // FIXME: translation
334 QMenu * menu = new QMenu(parent);
335 QActionGroup * iconSizeGroup = new QActionGroup(parent);
337 QAction * smallIcons = new QAction(iconSizeGroup);
338 smallIcons->setText(qt_("Small-sized icons"));
339 smallIcons->setCheckable(true);
340 QObject::connect(smallIcons, SIGNAL(triggered()),
341 parent, SLOT(smallSizedIcons()));
342 menu->addAction(smallIcons);
344 QAction * normalIcons = new QAction(iconSizeGroup);
345 normalIcons->setText(qt_("Normal-sized icons"));
346 normalIcons->setCheckable(true);
347 QObject::connect(normalIcons, SIGNAL(triggered()),
348 parent, SLOT(normalSizedIcons()));
349 menu->addAction(normalIcons);
351 QAction * bigIcons = new QAction(iconSizeGroup);
352 bigIcons->setText(qt_("Big-sized icons"));
353 bigIcons->setCheckable(true);
354 QObject::connect(bigIcons, SIGNAL(triggered()),
355 parent, SLOT(bigSizedIcons()));
356 menu->addAction(bigIcons);
358 QAction * hugeIcons = new QAction(iconSizeGroup);
359 hugeIcons->setText(qt_("Huge-sized icons"));
360 hugeIcons->setCheckable(true);
361 QObject::connect(hugeIcons, SIGNAL(triggered()),
362 parent, SLOT(hugeSizedIcons()));
363 menu->addAction(hugeIcons);
365 QAction * giantIcons = new QAction(iconSizeGroup);
366 giantIcons->setText(qt_("Giant-sized icons"));
367 giantIcons->setCheckable(true);
368 QObject::connect(giantIcons, SIGNAL(triggered()),
369 parent, SLOT(giantSizedIcons()));
370 menu->addAction(giantIcons);
372 unsigned int cur = parent->iconSize().width();
373 if ( cur == parent->d.smallIconSize)
374 smallIcons->setChecked(true);
375 else if (cur == parent->d.normalIconSize)
376 normalIcons->setChecked(true);
377 else if (cur == parent->d.bigIconSize)
378 bigIcons->setChecked(true);
379 else if (cur == parent->d.hugeIconSize)
380 hugeIcons->setChecked(true);
381 else if (cur == parent->d.giantIconSize)
382 giantIcons->setChecked(true);
389 stack_widget_->setCurrentWidget(bg_widget_);
390 bg_widget_->setUpdatesEnabled(true);
391 bg_widget_->setFocus();
394 int tabWorkAreaCount()
396 return splitter_->count();
399 TabWorkArea * tabWorkArea(int i)
401 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
404 TabWorkArea * currentTabWorkArea()
406 int areas = tabWorkAreaCount();
408 // The first TabWorkArea is always the first one, if any.
409 return tabWorkArea(0);
411 for (int i = 0; i != areas; ++i) {
412 TabWorkArea * twa = tabWorkArea(i);
413 if (current_main_work_area_ == twa->currentWorkArea())
417 // None has the focus so we just take the first one.
418 return tabWorkArea(0);
421 int countWorkAreasOf(Buffer & buf)
423 int areas = tabWorkAreaCount();
425 for (int i = 0; i != areas; ++i) {
426 TabWorkArea * twa = tabWorkArea(i);
427 if (twa->workArea(buf))
433 void setPreviewFuture(QFuture<Buffer::ExportStatus> const & f)
435 if (processing_thread_watcher_.isRunning()) {
436 // we prefer to cancel this preview in order to keep a snappy
440 processing_thread_watcher_.setFuture(f);
445 GuiWorkArea * current_work_area_;
446 GuiWorkArea * current_main_work_area_;
447 QSplitter * splitter_;
448 QStackedWidget * stack_widget_;
449 BackgroundWidget * bg_widget_;
451 ToolbarMap toolbars_;
452 ProgressInterface* progress_;
453 /// The main layout box.
455 * \warning Don't Delete! The layout box is actually owned by
456 * whichever toolbar contains it. All the GuiView class needs is a
457 * means of accessing it.
459 * FIXME: replace that with a proper model so that we are not limited
460 * to only one dialog.
465 map<string, DialogPtr> dialogs_;
467 unsigned int smallIconSize;
468 unsigned int normalIconSize;
469 unsigned int bigIconSize;
470 unsigned int hugeIconSize;
471 unsigned int giantIconSize;
473 QTimer statusbar_timer_;
474 /// auto-saving of buffers
475 Timeout autosave_timeout_;
476 /// flag against a race condition due to multiclicks, see bug #1119
480 TocModels toc_models_;
483 QFutureWatcher<docstring> autosave_watcher_;
484 QFutureWatcher<Buffer::ExportStatus> processing_thread_watcher_;
486 string last_export_format;
487 string processing_format;
489 static QSet<Buffer const *> busyBuffers;
490 static Buffer::ExportStatus previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
491 static Buffer::ExportStatus exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
492 static Buffer::ExportStatus compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
493 static docstring autosaveAndDestroy(Buffer const * orig, Buffer * buffer);
496 static Buffer::ExportStatus runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format);
498 // TODO syncFunc/previewFunc: use bind
499 bool asyncBufferProcessing(string const & argument,
500 Buffer const * used_buffer,
501 docstring const & msg,
502 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
503 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
504 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const);
506 QVector<GuiWorkArea*> guiWorkAreas();
509 QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
512 GuiView::GuiView(int id)
513 : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0),
514 command_execute_(false), minibuffer_focus_(false)
516 // GuiToolbars *must* be initialised before the menu bar.
517 normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
520 // set ourself as the current view. This is needed for the menu bar
521 // filling, at least for the static special menu item on Mac. Otherwise
522 // they are greyed out.
523 guiApp->setCurrentView(this);
525 // Fill up the menu bar.
526 guiApp->menus().fillMenuBar(menuBar(), this, true);
528 setCentralWidget(d.stack_widget_);
530 // Start autosave timer
531 if (lyxrc.autosave) {
532 d.autosave_timeout_.timeout.connect(bind(&GuiView::autoSave, this));
533 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
534 d.autosave_timeout_.start();
536 connect(&d.statusbar_timer_, SIGNAL(timeout()),
537 this, SLOT(clearMessage()));
539 // We don't want to keep the window in memory if it is closed.
540 setAttribute(Qt::WA_DeleteOnClose, true);
542 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
543 // QIcon::fromTheme was introduced in Qt 4.6
544 #if (QT_VERSION >= 0x040600)
545 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
546 // since the icon is provided in the application bundle. We use a themed
547 // version when available and use the bundled one as fallback.
548 setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
550 setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
556 // use tabbed dock area for multiple docks
557 // (such as "source" and "messages")
558 setDockOptions(QMainWindow::ForceTabbedDocks);
561 setAcceptDrops(true);
563 // add busy indicator to statusbar
564 QLabel * busylabel = new QLabel(statusBar());
565 statusBar()->addPermanentWidget(busylabel);
566 search_mode mode = theGuiApp()->imageSearchMode();
567 QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
568 QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
569 busylabel->setMovie(busyanim);
573 connect(&d.processing_thread_watcher_, SIGNAL(started()),
574 busylabel, SLOT(show()));
575 connect(&d.processing_thread_watcher_, SIGNAL(finished()),
576 busylabel, SLOT(hide()));
578 QFontMetrics const fm(statusBar()->fontMetrics());
579 int const roheight = max(int(d.normalIconSize), fm.height());
580 QSize const rosize(roheight, roheight);
581 QPixmap readonly = QIcon(getPixmap("images/", "emblem-readonly", "svgz,png")).pixmap(rosize);
582 read_only_ = new QLabel(statusBar());
583 read_only_->setPixmap(readonly);
584 read_only_->setScaledContents(true);
585 read_only_->setAlignment(Qt::AlignCenter);
587 statusBar()->addPermanentWidget(read_only_);
589 version_control_ = new QLabel(statusBar());
590 version_control_->setAlignment(Qt::AlignCenter);
591 version_control_->setFrameStyle(QFrame::StyledPanel);
592 version_control_->hide();
593 statusBar()->addPermanentWidget(version_control_);
595 statusBar()->setSizeGripEnabled(true);
598 connect(&d.autosave_watcher_, SIGNAL(finished()), this,
599 SLOT(autoSaveThreadFinished()));
601 connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
602 SLOT(processingThreadStarted()));
603 connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
604 SLOT(processingThreadFinished()));
606 connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
607 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
609 // Forbid too small unresizable window because it can happen
610 // with some window manager under X11.
611 setMinimumSize(300, 200);
613 if (lyxrc.allow_geometry_session) {
614 // Now take care of session management.
619 // no session handling, default to a sane size.
620 setGeometry(50, 50, 690, 510);
623 // clear session data if any.
625 settings.remove("views");
635 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
637 QVector<GuiWorkArea*> areas;
638 for (int i = 0; i < tabWorkAreaCount(); i++) {
639 TabWorkArea* ta = tabWorkArea(i);
640 for (int u = 0; u < ta->count(); u++) {
641 areas << ta->workArea(u);
647 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
648 string const & format)
650 docstring const fmt = formats.prettyName(format);
653 case Buffer::ExportSuccess:
654 msg = bformat(_("Successful export to format: %1$s"), fmt);
656 case Buffer::ExportCancel:
657 msg = _("Document export cancelled.");
659 case Buffer::ExportError:
660 case Buffer::ExportNoPathToFormat:
661 case Buffer::ExportTexPathHasSpaces:
662 case Buffer::ExportConverterError:
663 msg = bformat(_("Error while exporting format: %1$s"), fmt);
665 case Buffer::PreviewSuccess:
666 msg = bformat(_("Successful preview of format: %1$s"), fmt);
668 case Buffer::PreviewError:
669 msg = bformat(_("Error while previewing format: %1$s"), fmt);
676 void GuiView::processingThreadStarted()
681 void GuiView::processingThreadFinished()
683 QFutureWatcher<Buffer::ExportStatus> const * watcher =
684 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
686 Buffer::ExportStatus const status = watcher->result();
687 handleExportStatus(this, status, d.processing_format);
690 BufferView const * const bv = currentBufferView();
691 if (bv && !bv->buffer().errorList("Export").empty()) {
695 errors(d.last_export_format);
699 void GuiView::autoSaveThreadFinished()
701 QFutureWatcher<docstring> const * watcher =
702 static_cast<QFutureWatcher<docstring> const *>(sender());
703 message(watcher->result());
708 void GuiView::saveLayout() const
711 settings.beginGroup("views");
712 settings.beginGroup(QString::number(id_));
713 #if defined(Q_WS_X11) || defined(QPA_XCB)
714 settings.setValue("pos", pos());
715 settings.setValue("size", size());
717 settings.setValue("geometry", saveGeometry());
719 settings.setValue("layout", saveState(0));
720 settings.setValue("icon_size", iconSize());
724 void GuiView::saveUISettings() const
726 // Save the toolbar private states
727 ToolbarMap::iterator end = d.toolbars_.end();
728 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
729 it->second->saveSession();
730 // Now take care of all other dialogs
731 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
732 for (; it!= d.dialogs_.end(); ++it)
733 it->second->saveSession();
737 bool GuiView::restoreLayout()
740 settings.beginGroup("views");
741 settings.beginGroup(QString::number(id_));
742 QString const icon_key = "icon_size";
743 if (!settings.contains(icon_key))
746 //code below is skipped when when ~/.config/LyX is (re)created
747 QSize icon_size = settings.value(icon_key).toSize();
748 // Check whether session size changed.
749 if (icon_size.width() != int(d.smallIconSize) &&
750 icon_size.width() != int(d.normalIconSize) &&
751 icon_size.width() != int(d.bigIconSize) &&
752 icon_size.width() != int(d.hugeIconSize) &&
753 icon_size.width() != int(d.giantIconSize)) {
754 icon_size.setWidth(d.normalIconSize);
755 icon_size.setHeight(d.normalIconSize);
757 setIconSize(icon_size);
759 #if defined(Q_WS_X11) || defined(QPA_XCB)
760 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
761 QSize size = settings.value("size", QSize(690, 510)).toSize();
765 // Work-around for bug #6034: the window ends up in an undetermined
766 // state when trying to restore a maximized window when it is
767 // already maximized.
768 if (!(windowState() & Qt::WindowMaximized))
769 if (!restoreGeometry(settings.value("geometry").toByteArray()))
770 setGeometry(50, 50, 690, 510);
772 // Make sure layout is correctly oriented.
773 setLayoutDirection(qApp->layoutDirection());
775 // Allow the toc and view-source dock widget to be restored if needed.
777 if ((dialog = findOrBuild("toc", true)))
778 // see bug 5082. At least setup title and enabled state.
779 // Visibility will be adjusted by restoreState below.
780 dialog->prepareView();
781 if ((dialog = findOrBuild("view-source", true)))
782 dialog->prepareView();
783 if ((dialog = findOrBuild("progress", true)))
784 dialog->prepareView();
786 if (!restoreState(settings.value("layout").toByteArray(), 0))
789 // init the toolbars that have not been restored
790 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
791 Toolbars::Infos::iterator end = guiApp->toolbars().end();
792 for (; cit != end; ++cit) {
793 GuiToolbar * tb = toolbar(cit->name);
794 if (tb && !tb->isRestored())
795 initToolbar(cit->name);
803 GuiToolbar * GuiView::toolbar(string const & name)
805 ToolbarMap::iterator it = d.toolbars_.find(name);
806 if (it != d.toolbars_.end())
809 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
814 void GuiView::constructToolbars()
816 ToolbarMap::iterator it = d.toolbars_.begin();
817 for (; it != d.toolbars_.end(); ++it)
821 // I don't like doing this here, but the standard toolbar
822 // destroys this object when it's destroyed itself (vfr)
823 d.layout_ = new LayoutBox(*this);
824 d.stack_widget_->addWidget(d.layout_);
825 d.layout_->move(0,0);
827 // extracts the toolbars from the backend
828 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
829 Toolbars::Infos::iterator end = guiApp->toolbars().end();
830 for (; cit != end; ++cit)
831 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
835 void GuiView::initToolbars()
837 // extracts the toolbars from the backend
838 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
839 Toolbars::Infos::iterator end = guiApp->toolbars().end();
840 for (; cit != end; ++cit)
841 initToolbar(cit->name);
845 void GuiView::initToolbar(string const & name)
847 GuiToolbar * tb = toolbar(name);
850 int const visibility = guiApp->toolbars().defaultVisibility(name);
851 bool newline = !(visibility & Toolbars::SAMEROW);
852 tb->setVisible(false);
853 tb->setVisibility(visibility);
855 if (visibility & Toolbars::TOP) {
857 addToolBarBreak(Qt::TopToolBarArea);
858 addToolBar(Qt::TopToolBarArea, tb);
861 if (visibility & Toolbars::BOTTOM) {
863 addToolBarBreak(Qt::BottomToolBarArea);
864 addToolBar(Qt::BottomToolBarArea, tb);
867 if (visibility & Toolbars::LEFT) {
869 addToolBarBreak(Qt::LeftToolBarArea);
870 addToolBar(Qt::LeftToolBarArea, tb);
873 if (visibility & Toolbars::RIGHT) {
875 addToolBarBreak(Qt::RightToolBarArea);
876 addToolBar(Qt::RightToolBarArea, tb);
879 if (visibility & Toolbars::ON)
880 tb->setVisible(true);
884 TocModels & GuiView::tocModels()
886 return d.toc_models_;
890 void GuiView::setFocus()
892 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
893 QMainWindow::setFocus();
897 bool GuiView::hasFocus() const
899 if (currentWorkArea())
900 return currentWorkArea()->hasFocus();
901 if (currentMainWorkArea())
902 return currentMainWorkArea()->hasFocus();
903 return d.bg_widget_->hasFocus();
907 void GuiView::focusInEvent(QFocusEvent * e)
909 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
910 QMainWindow::focusInEvent(e);
911 // Make sure guiApp points to the correct view.
912 guiApp->setCurrentView(this);
913 if (currentWorkArea())
914 currentWorkArea()->setFocus();
915 else if (currentMainWorkArea())
916 currentMainWorkArea()->setFocus();
918 d.bg_widget_->setFocus();
922 QMenu * GuiView::createPopupMenu()
924 return d.toolBarPopup(this);
928 void GuiView::showEvent(QShowEvent * e)
930 LYXERR(Debug::GUI, "Passed Geometry "
931 << size().height() << "x" << size().width()
932 << "+" << pos().x() << "+" << pos().y());
934 if (d.splitter_->count() == 0)
935 // No work area, switch to the background widget.
939 QMainWindow::showEvent(e);
943 bool GuiView::closeScheduled()
950 bool GuiView::prepareAllBuffersForLogout()
952 Buffer * first = theBufferList().first();
956 // First, iterate over all buffers and ask the users if unsaved
957 // changes should be saved.
958 // We cannot use a for loop as the buffer list cycles.
961 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
963 b = theBufferList().next(b);
964 } while (b != first);
966 // Next, save session state
967 // When a view/window was closed before without quitting LyX, there
968 // are already entries in the lastOpened list.
969 theSession().lastOpened().clear();
976 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
977 ** is responsibility of the container (e.g., dialog)
979 void GuiView::closeEvent(QCloseEvent * close_event)
981 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
983 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
984 Alert::warning(_("Exit LyX"),
985 _("LyX could not be closed because documents are being processed by LyX."));
986 close_event->setAccepted(false);
990 // If the user pressed the x (so we didn't call closeView
991 // programmatically), we want to clear all existing entries.
993 theSession().lastOpened().clear();
998 // it can happen that this event arrives without selecting the view,
999 // e.g. when clicking the close button on a background window.
1001 if (!closeWorkAreaAll()) {
1003 close_event->ignore();
1007 // Make sure that nothing will use this to be closed View.
1008 guiApp->unregisterView(this);
1010 if (isFullScreen()) {
1011 // Switch off fullscreen before closing.
1016 // Make sure the timer time out will not trigger a statusbar update.
1017 d.statusbar_timer_.stop();
1019 // Saving fullscreen requires additional tweaks in the toolbar code.
1020 // It wouldn't also work under linux natively.
1021 if (lyxrc.allow_geometry_session) {
1026 close_event->accept();
1030 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1032 if (event->mimeData()->hasUrls())
1034 /// \todo Ask lyx-devel is this is enough:
1035 /// if (event->mimeData()->hasFormat("text/plain"))
1036 /// event->acceptProposedAction();
1040 void GuiView::dropEvent(QDropEvent * event)
1042 QList<QUrl> files = event->mimeData()->urls();
1043 if (files.isEmpty())
1046 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1047 for (int i = 0; i != files.size(); ++i) {
1048 string const file = os::internal_path(fromqstr(
1049 files.at(i).toLocalFile()));
1053 string const ext = support::getExtension(file);
1054 vector<const Format *> found_formats;
1056 // Find all formats that have the correct extension.
1057 vector<const Format *> const & import_formats
1058 = theConverters().importableFormats();
1059 vector<const Format *>::const_iterator it = import_formats.begin();
1060 for (; it != import_formats.end(); ++it)
1061 if ((*it)->hasExtension(ext))
1062 found_formats.push_back(*it);
1065 if (found_formats.size() >= 1) {
1066 if (found_formats.size() > 1) {
1067 //FIXME: show a dialog to choose the correct importable format
1068 LYXERR(Debug::FILES,
1069 "Multiple importable formats found, selecting first");
1071 string const arg = found_formats[0]->name() + " " + file;
1072 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1075 //FIXME: do we have to explicitly check whether it's a lyx file?
1076 LYXERR(Debug::FILES,
1077 "No formats found, trying to open it as a lyx file");
1078 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1080 // add the functions to the queue
1081 guiApp->addToFuncRequestQueue(cmd);
1084 // now process the collected functions. We perform the events
1085 // asynchronously. This prevents potential problems in case the
1086 // BufferView is closed within an event.
1087 guiApp->processFuncRequestQueueAsync();
1091 void GuiView::message(docstring const & str)
1093 if (ForkedProcess::iAmAChild())
1096 // call is moved to GUI-thread by GuiProgress
1097 d.progress_->appendMessage(toqstr(str));
1101 void GuiView::clearMessageText()
1103 message(docstring());
1107 void GuiView::updateStatusBarMessage(QString const & str)
1109 statusBar()->showMessage(str);
1110 d.statusbar_timer_.stop();
1111 d.statusbar_timer_.start(3000);
1115 void GuiView::smallSizedIcons()
1117 setIconSize(QSize(d.smallIconSize, d.smallIconSize));
1121 void GuiView::normalSizedIcons()
1123 setIconSize(QSize(d.normalIconSize, d.normalIconSize));
1127 void GuiView::bigSizedIcons()
1129 setIconSize(QSize(d.bigIconSize, d.bigIconSize));
1133 void GuiView::hugeSizedIcons()
1135 setIconSize(QSize(d.hugeIconSize, d.hugeIconSize));
1139 void GuiView::giantSizedIcons()
1141 setIconSize(QSize(d.giantIconSize, d.giantIconSize));
1145 void GuiView::clearMessage()
1147 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1148 // the hasFocus function mostly returns false, even if the focus is on
1149 // a workarea in this view.
1153 d.statusbar_timer_.stop();
1157 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1159 if (wa != d.current_work_area_
1160 || wa->bufferView().buffer().isInternal())
1162 Buffer const & buf = wa->bufferView().buffer();
1163 // Set the windows title
1164 docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
1166 title += from_ascii(" - LyX");
1168 setWindowTitle(toqstr(title));
1169 // Sets the path for the window: this is used by OSX to
1170 // allow a context click on the title bar showing a menu
1171 // with the path up to the file
1172 setWindowFilePath(toqstr(buf.absFileName()));
1173 // Tell Qt whether the current document is changed
1174 setWindowModified(!buf.isClean());
1176 if (buf.isReadonly())
1181 if (buf.lyxvc().inUse()) {
1182 version_control_->show();
1183 if (buf.lyxvc().locking())
1184 version_control_->setText(
1185 toqstr(bformat(_("%1$s lock"),
1186 from_ascii(buf.lyxvc().vcname()))));
1188 version_control_->setText(toqstr(buf.lyxvc().vcname()));
1190 version_control_->hide();
1194 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1196 if (d.current_work_area_)
1197 QObject::disconnect(d.current_work_area_, SIGNAL(busy(bool)),
1198 this, SLOT(setBusy(bool)));
1200 disconnectBufferView();
1201 connectBufferView(wa->bufferView());
1202 connectBuffer(wa->bufferView().buffer());
1203 d.current_work_area_ = wa;
1204 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1205 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1206 QObject::connect(wa, SIGNAL(busy(bool)), this, SLOT(setBusy(bool)));
1207 updateWindowTitle(wa);
1211 // The document settings needs to be reinitialised.
1212 updateDialog("document", "");
1214 // Buffer-dependent dialogs must be updated. This is done here because
1215 // some dialogs require buffer()->text.
1220 void GuiView::on_lastWorkAreaRemoved()
1223 // We already are in a close event. Nothing more to do.
1226 if (d.splitter_->count() > 1)
1227 // We have a splitter so don't close anything.
1230 // Reset and updates the dialogs.
1231 d.toc_models_.reset(0);
1232 updateDialog("document", "");
1238 if (lyxrc.open_buffers_in_tabs)
1239 // Nothing more to do, the window should stay open.
1242 if (guiApp->viewIds().size() > 1) {
1248 // On Mac we also close the last window because the application stay
1249 // resident in memory. On other platforms we don't close the last
1250 // window because this would quit the application.
1256 void GuiView::updateStatusBar()
1258 // let the user see the explicit message
1259 if (d.statusbar_timer_.isActive())
1266 void GuiView::showMessage()
1270 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1271 if (msg.isEmpty()) {
1272 BufferView const * bv = currentBufferView();
1274 msg = toqstr(bv->cursor().currentState());
1276 msg = qt_("Welcome to LyX!");
1278 statusBar()->showMessage(msg);
1282 bool GuiView::event(QEvent * e)
1286 // Useful debug code:
1287 //case QEvent::ActivationChange:
1288 //case QEvent::WindowDeactivate:
1289 //case QEvent::Paint:
1290 //case QEvent::Enter:
1291 //case QEvent::Leave:
1292 //case QEvent::HoverEnter:
1293 //case QEvent::HoverLeave:
1294 //case QEvent::HoverMove:
1295 //case QEvent::StatusTip:
1296 //case QEvent::DragEnter:
1297 //case QEvent::DragLeave:
1298 //case QEvent::Drop:
1301 case QEvent::WindowActivate: {
1302 GuiView * old_view = guiApp->currentView();
1303 if (this == old_view) {
1305 return QMainWindow::event(e);
1307 if (old_view && old_view->currentBufferView()) {
1308 // save current selection to the selection buffer to allow
1309 // middle-button paste in this window.
1310 cap::saveSelection(old_view->currentBufferView()->cursor());
1312 guiApp->setCurrentView(this);
1313 if (d.current_work_area_) {
1314 BufferView & bv = d.current_work_area_->bufferView();
1315 connectBufferView(bv);
1316 connectBuffer(bv.buffer());
1317 // The document structure, name and dialogs might have
1318 // changed in another view.
1320 // The document settings needs to be reinitialised.
1321 updateDialog("document", "");
1327 return QMainWindow::event(e);
1330 case QEvent::ShortcutOverride: {
1332 if (isFullScreen() && menuBar()->isHidden()) {
1333 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1334 // FIXME: we should also try to detect special LyX shortcut such as
1335 // Alt-P and Alt-M. Right now there is a hack in
1336 // GuiWorkArea::processKeySym() that hides again the menubar for
1338 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1340 return QMainWindow::event(e);
1343 return QMainWindow::event(e);
1347 return QMainWindow::event(e);
1351 void GuiView::resetWindowTitle()
1353 setWindowTitle(qt_("LyX"));
1356 bool GuiView::focusNextPrevChild(bool /*next*/)
1363 bool GuiView::busy() const
1369 void GuiView::setBusy(bool busy)
1371 bool const busy_before = busy_ > 0;
1372 busy ? ++busy_ : --busy_;
1373 if ((busy_ > 0) == busy_before)
1374 // busy state didn't change
1378 QApplication::setOverrideCursor(Qt::WaitCursor);
1381 QApplication::restoreOverrideCursor();
1386 void GuiView::resetCommandExecute()
1388 command_execute_ = false;
1393 double GuiView::pixelRatio() const
1395 #if QT_VERSION >= 0x050000
1396 return devicePixelRatio();
1403 GuiWorkArea * GuiView::workArea(int index)
1405 if (TabWorkArea * twa = d.currentTabWorkArea())
1406 if (index < twa->count())
1407 return dynamic_cast<GuiWorkArea *>(twa->widget(index));
1412 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1414 if (currentWorkArea()
1415 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1416 return currentWorkArea();
1417 if (TabWorkArea * twa = d.currentTabWorkArea())
1418 return twa->workArea(buffer);
1423 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1425 // Automatically create a TabWorkArea if there are none yet.
1426 TabWorkArea * tab_widget = d.splitter_->count()
1427 ? d.currentTabWorkArea() : addTabWorkArea();
1428 return tab_widget->addWorkArea(buffer, *this);
1432 TabWorkArea * GuiView::addTabWorkArea()
1434 TabWorkArea * twa = new TabWorkArea;
1435 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1436 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1437 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1438 this, SLOT(on_lastWorkAreaRemoved()));
1440 d.splitter_->addWidget(twa);
1441 d.stack_widget_->setCurrentWidget(d.splitter_);
1446 GuiWorkArea const * GuiView::currentWorkArea() const
1448 return d.current_work_area_;
1452 GuiWorkArea * GuiView::currentWorkArea()
1454 return d.current_work_area_;
1458 GuiWorkArea const * GuiView::currentMainWorkArea() const
1460 if (!d.currentTabWorkArea())
1462 return d.currentTabWorkArea()->currentWorkArea();
1466 GuiWorkArea * GuiView::currentMainWorkArea()
1468 if (!d.currentTabWorkArea())
1470 return d.currentTabWorkArea()->currentWorkArea();
1474 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1476 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1478 d.current_work_area_ = 0;
1483 // FIXME: I've no clue why this is here and why it accesses
1484 // theGuiApp()->currentView, which might be 0 (bug 6464).
1485 // See also 27525 (vfr).
1486 if (theGuiApp()->currentView() == this
1487 && theGuiApp()->currentView()->currentWorkArea() == wa)
1490 if (currentBufferView())
1491 cap::saveSelection(currentBufferView()->cursor());
1493 theGuiApp()->setCurrentView(this);
1494 d.current_work_area_ = wa;
1496 // We need to reset this now, because it will need to be
1497 // right if the tabWorkArea gets reset in the for loop. We
1498 // will change it back if we aren't in that case.
1499 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1500 d.current_main_work_area_ = wa;
1502 for (int i = 0; i != d.splitter_->count(); ++i) {
1503 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1504 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1505 << ", Current main wa: " << currentMainWorkArea());
1510 d.current_main_work_area_ = old_cmwa;
1512 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1513 on_currentWorkAreaChanged(wa);
1514 BufferView & bv = wa->bufferView();
1515 bv.cursor().fixIfBroken();
1517 wa->setUpdatesEnabled(true);
1518 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1522 void GuiView::removeWorkArea(GuiWorkArea * wa)
1524 LASSERT(wa, return);
1525 if (wa == d.current_work_area_) {
1527 disconnectBufferView();
1528 d.current_work_area_ = 0;
1529 d.current_main_work_area_ = 0;
1532 bool found_twa = false;
1533 for (int i = 0; i != d.splitter_->count(); ++i) {
1534 TabWorkArea * twa = d.tabWorkArea(i);
1535 if (twa->removeWorkArea(wa)) {
1536 // Found in this tab group, and deleted the GuiWorkArea.
1538 if (twa->count() != 0) {
1539 if (d.current_work_area_ == 0)
1540 // This means that we are closing the current GuiWorkArea, so
1541 // switch to the next GuiWorkArea in the found TabWorkArea.
1542 setCurrentWorkArea(twa->currentWorkArea());
1544 // No more WorkAreas in this tab group, so delete it.
1551 // It is not a tabbed work area (i.e., the search work area), so it
1552 // should be deleted by other means.
1553 LASSERT(found_twa, return);
1555 if (d.current_work_area_ == 0) {
1556 if (d.splitter_->count() != 0) {
1557 TabWorkArea * twa = d.currentTabWorkArea();
1558 setCurrentWorkArea(twa->currentWorkArea());
1560 // No more work areas, switch to the background widget.
1561 setCurrentWorkArea(0);
1567 LayoutBox * GuiView::getLayoutDialog() const
1573 void GuiView::updateLayoutList()
1576 d.layout_->updateContents(false);
1580 void GuiView::updateToolbars()
1582 ToolbarMap::iterator end = d.toolbars_.end();
1583 if (d.current_work_area_) {
1585 if (d.current_work_area_->bufferView().cursor().inMathed()
1586 && !d.current_work_area_->bufferView().cursor().inRegexped())
1587 context |= Toolbars::MATH;
1588 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1589 context |= Toolbars::TABLE;
1590 if (currentBufferView()->buffer().areChangesPresent()
1591 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1592 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1593 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1594 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1595 context |= Toolbars::REVIEW;
1596 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1597 context |= Toolbars::MATHMACROTEMPLATE;
1598 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1599 context |= Toolbars::IPA;
1600 if (command_execute_)
1601 context |= Toolbars::MINIBUFFER;
1602 if (minibuffer_focus_) {
1603 context |= Toolbars::MINIBUFFER_FOCUS;
1604 minibuffer_focus_ = false;
1607 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1608 it->second->update(context);
1610 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1611 it->second->update();
1615 void GuiView::setBuffer(Buffer * newBuffer)
1617 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1618 LASSERT(newBuffer, return);
1620 GuiWorkArea * wa = workArea(*newBuffer);
1623 newBuffer->masterBuffer()->updateBuffer();
1625 wa = addWorkArea(*newBuffer);
1626 // scroll to the position when the BufferView was last closed
1627 if (lyxrc.use_lastfilepos) {
1628 LastFilePosSection::FilePos filepos =
1629 theSession().lastFilePos().load(newBuffer->fileName());
1630 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1633 //Disconnect the old buffer...there's no new one.
1636 connectBuffer(*newBuffer);
1637 connectBufferView(wa->bufferView());
1638 setCurrentWorkArea(wa);
1642 void GuiView::connectBuffer(Buffer & buf)
1644 buf.setGuiDelegate(this);
1648 void GuiView::disconnectBuffer()
1650 if (d.current_work_area_)
1651 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1655 void GuiView::connectBufferView(BufferView & bv)
1657 bv.setGuiDelegate(this);
1661 void GuiView::disconnectBufferView()
1663 if (d.current_work_area_)
1664 d.current_work_area_->bufferView().setGuiDelegate(0);
1668 void GuiView::errors(string const & error_type, bool from_master)
1670 BufferView const * const bv = currentBufferView();
1674 #if EXPORT_in_THREAD
1675 // We are called with from_master == false by default, so we
1676 // have to figure out whether that is the case or not.
1677 ErrorList & el = bv->buffer().errorList(error_type);
1679 el = bv->buffer().masterBuffer()->errorList(error_type);
1683 ErrorList const & el = from_master ?
1684 bv->buffer().masterBuffer()->errorList(error_type) :
1685 bv->buffer().errorList(error_type);
1691 string data = error_type;
1693 data = "from_master|" + error_type;
1694 showDialog("errorlist", data);
1698 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1700 d.toc_models_.updateItem(toqstr(type), dit);
1704 void GuiView::structureChanged()
1706 d.toc_models_.reset(documentBufferView());
1707 // Navigator needs more than a simple update in this case. It needs to be
1709 updateDialog("toc", "");
1713 void GuiView::updateDialog(string const & name, string const & data)
1715 if (!isDialogVisible(name))
1718 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1719 if (it == d.dialogs_.end())
1722 Dialog * const dialog = it->second.get();
1723 if (dialog->isVisibleView())
1724 dialog->initialiseParams(data);
1728 BufferView * GuiView::documentBufferView()
1730 return currentMainWorkArea()
1731 ? ¤tMainWorkArea()->bufferView()
1736 BufferView const * GuiView::documentBufferView() const
1738 return currentMainWorkArea()
1739 ? ¤tMainWorkArea()->bufferView()
1744 BufferView * GuiView::currentBufferView()
1746 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1750 BufferView const * GuiView::currentBufferView() const
1752 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1756 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1757 Buffer const * orig, Buffer * clone)
1759 bool const success = clone->autoSave();
1761 busyBuffers.remove(orig);
1763 ? _("Automatic save done.")
1764 : _("Automatic save failed!");
1768 void GuiView::autoSave()
1770 LYXERR(Debug::INFO, "Running autoSave()");
1772 Buffer * buffer = documentBufferView()
1773 ? &documentBufferView()->buffer() : 0;
1775 resetAutosaveTimers();
1779 GuiViewPrivate::busyBuffers.insert(buffer);
1780 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1781 buffer, buffer->cloneBufferOnly());
1782 d.autosave_watcher_.setFuture(f);
1783 resetAutosaveTimers();
1787 void GuiView::resetAutosaveTimers()
1790 d.autosave_timeout_.restart();
1794 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1797 Buffer * buf = currentBufferView()
1798 ? ¤tBufferView()->buffer() : 0;
1799 Buffer * doc_buffer = documentBufferView()
1800 ? &(documentBufferView()->buffer()) : 0;
1803 /* In LyX/Mac, when a dialog is open, the menus of the
1804 application can still be accessed without giving focus to
1805 the main window. In this case, we want to disable the menu
1806 entries that are buffer-related.
1807 This code must not be used on Linux and Windows, since it
1808 would disable buffer-related entries when hovering over the
1809 menu (see bug #9574).
1811 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1817 // Check whether we need a buffer
1818 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1819 // no, exit directly
1820 flag.message(from_utf8(N_("Command not allowed with"
1821 "out any document open")));
1822 flag.setEnabled(false);
1826 if (cmd.origin() == FuncRequest::TOC) {
1827 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1828 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1829 flag.setEnabled(false);
1833 switch(cmd.action()) {
1834 case LFUN_BUFFER_IMPORT:
1837 case LFUN_MASTER_BUFFER_UPDATE:
1838 case LFUN_MASTER_BUFFER_VIEW:
1840 && (doc_buffer->parent() != 0
1841 || doc_buffer->hasChildren())
1842 && !d.processing_thread_watcher_.isRunning();
1845 case LFUN_BUFFER_UPDATE:
1846 case LFUN_BUFFER_VIEW: {
1847 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1851 string format = to_utf8(cmd.argument());
1852 if (cmd.argument().empty())
1853 format = doc_buffer->params().getDefaultOutputFormat();
1854 enable = doc_buffer->params().isExportableFormat(format);
1858 case LFUN_BUFFER_RELOAD:
1859 enable = doc_buffer && !doc_buffer->isUnnamed()
1860 && doc_buffer->fileName().exists()
1861 && (!doc_buffer->isClean()
1862 || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1865 case LFUN_BUFFER_CHILD_OPEN:
1866 enable = doc_buffer != 0;
1869 case LFUN_BUFFER_WRITE:
1870 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1873 //FIXME: This LFUN should be moved to GuiApplication.
1874 case LFUN_BUFFER_WRITE_ALL: {
1875 // We enable the command only if there are some modified buffers
1876 Buffer * first = theBufferList().first();
1881 // We cannot use a for loop as the buffer list is a cycle.
1883 if (!b->isClean()) {
1887 b = theBufferList().next(b);
1888 } while (b != first);
1892 case LFUN_BUFFER_WRITE_AS:
1893 case LFUN_BUFFER_EXPORT_AS:
1894 enable = doc_buffer != 0;
1897 case LFUN_BUFFER_CLOSE:
1898 case LFUN_VIEW_CLOSE:
1899 enable = doc_buffer != 0;
1902 case LFUN_BUFFER_CLOSE_ALL:
1903 enable = theBufferList().last() != theBufferList().first();
1906 case LFUN_VIEW_SPLIT:
1907 if (cmd.getArg(0) == "vertical")
1908 enable = doc_buffer && (d.splitter_->count() == 1 ||
1909 d.splitter_->orientation() == Qt::Vertical);
1911 enable = doc_buffer && (d.splitter_->count() == 1 ||
1912 d.splitter_->orientation() == Qt::Horizontal);
1915 case LFUN_TAB_GROUP_CLOSE:
1916 enable = d.tabWorkAreaCount() > 1;
1919 case LFUN_TOOLBAR_TOGGLE: {
1920 string const name = cmd.getArg(0);
1921 if (GuiToolbar * t = toolbar(name))
1922 flag.setOnOff(t->isVisible());
1925 docstring const msg =
1926 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1932 case LFUN_DROP_LAYOUTS_CHOICE:
1936 case LFUN_UI_TOGGLE:
1937 flag.setOnOff(isFullScreen());
1940 case LFUN_DIALOG_DISCONNECT_INSET:
1943 case LFUN_DIALOG_HIDE:
1944 // FIXME: should we check if the dialog is shown?
1947 case LFUN_DIALOG_TOGGLE:
1948 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1949 // fall through to set "enable"
1950 case LFUN_DIALOG_SHOW: {
1951 string const name = cmd.getArg(0);
1953 enable = name == "aboutlyx"
1954 || name == "file" //FIXME: should be removed.
1956 || name == "texinfo"
1957 || name == "progress"
1958 || name == "compare";
1959 else if (name == "character" || name == "symbols"
1960 || name == "mathdelimiter" || name == "mathmatrix") {
1961 if (!buf || buf->isReadonly())
1964 Cursor const & cur = currentBufferView()->cursor();
1965 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1968 else if (name == "latexlog")
1969 enable = FileName(doc_buffer->logName()).isReadableFile();
1970 else if (name == "spellchecker")
1971 enable = theSpellChecker()
1972 && !doc_buffer->isReadonly()
1973 && !doc_buffer->text().empty();
1974 else if (name == "vclog")
1975 enable = doc_buffer->lyxvc().inUse();
1979 case LFUN_DIALOG_UPDATE: {
1980 string const name = cmd.getArg(0);
1982 enable = name == "prefs";
1986 case LFUN_COMMAND_EXECUTE:
1988 case LFUN_MENU_OPEN:
1989 // Nothing to check.
1992 case LFUN_COMPLETION_INLINE:
1993 if (!d.current_work_area_
1994 || !d.current_work_area_->completer().inlinePossible(
1995 currentBufferView()->cursor()))
1999 case LFUN_COMPLETION_POPUP:
2000 if (!d.current_work_area_
2001 || !d.current_work_area_->completer().popupPossible(
2002 currentBufferView()->cursor()))
2007 if (!d.current_work_area_
2008 || !d.current_work_area_->completer().inlinePossible(
2009 currentBufferView()->cursor()))
2013 case LFUN_COMPLETION_ACCEPT:
2014 if (!d.current_work_area_
2015 || (!d.current_work_area_->completer().popupVisible()
2016 && !d.current_work_area_->completer().inlineVisible()
2017 && !d.current_work_area_->completer().completionAvailable()))
2021 case LFUN_COMPLETION_CANCEL:
2022 if (!d.current_work_area_
2023 || (!d.current_work_area_->completer().popupVisible()
2024 && !d.current_work_area_->completer().inlineVisible()))
2028 case LFUN_BUFFER_ZOOM_OUT:
2029 enable = doc_buffer && lyxrc.zoom > 10;
2030 if (lyxrc.zoom <= 10)
2031 flag.message(_("Zoom level cannot be less than 10%."));
2034 case LFUN_BUFFER_ZOOM_IN:
2035 enable = doc_buffer != 0;
2038 case LFUN_BUFFER_MOVE_NEXT:
2039 case LFUN_BUFFER_MOVE_PREVIOUS:
2040 // we do not cycle when moving
2041 case LFUN_BUFFER_NEXT:
2042 case LFUN_BUFFER_PREVIOUS:
2043 // because we cycle, it doesn't matter whether on first or last
2044 enable = (d.currentTabWorkArea()->count() > 1);
2046 case LFUN_BUFFER_SWITCH:
2047 // toggle on the current buffer, but do not toggle off
2048 // the other ones (is that a good idea?)
2050 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2051 flag.setOnOff(true);
2054 case LFUN_VC_REGISTER:
2055 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2057 case LFUN_VC_RENAME:
2058 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2061 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2063 case LFUN_VC_CHECK_IN:
2064 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2066 case LFUN_VC_CHECK_OUT:
2067 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2069 case LFUN_VC_LOCKING_TOGGLE:
2070 enable = doc_buffer && !doc_buffer->isReadonly()
2071 && doc_buffer->lyxvc().lockingToggleEnabled();
2072 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2074 case LFUN_VC_REVERT:
2075 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2077 case LFUN_VC_UNDO_LAST:
2078 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2080 case LFUN_VC_REPO_UPDATE:
2081 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2083 case LFUN_VC_COMMAND: {
2084 if (cmd.argument().empty())
2086 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2090 case LFUN_VC_COMPARE:
2091 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2094 case LFUN_SERVER_GOTO_FILE_ROW:
2096 case LFUN_FORWARD_SEARCH:
2097 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2100 case LFUN_FILE_INSERT_PLAINTEXT:
2101 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2102 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2105 case LFUN_SPELLING_CONTINUOUSLY:
2106 flag.setOnOff(lyxrc.spellcheck_continuously);
2114 flag.setEnabled(false);
2120 static FileName selectTemplateFile()
2122 FileDialog dlg(qt_("Select template file"));
2123 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2124 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2126 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2127 QStringList(qt_("LyX Documents (*.lyx)")));
2129 if (result.first == FileDialog::Later)
2131 if (result.second.isEmpty())
2133 return FileName(fromqstr(result.second));
2137 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2141 Buffer * newBuffer = 0;
2143 newBuffer = checkAndLoadLyXFile(filename);
2144 } catch (ExceptionMessage const & e) {
2151 message(_("Document not loaded."));
2155 setBuffer(newBuffer);
2156 newBuffer->errors("Parse");
2159 theSession().lastFiles().add(filename);
2165 void GuiView::openDocument(string const & fname)
2167 string initpath = lyxrc.document_path;
2169 if (documentBufferView()) {
2170 string const trypath = documentBufferView()->buffer().filePath();
2171 // If directory is writeable, use this as default.
2172 if (FileName(trypath).isDirWritable())
2178 if (fname.empty()) {
2179 FileDialog dlg(qt_("Select document to open"));
2180 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2181 dlg.setButton2(qt_("Examples|#E#e"),
2182 toqstr(addPath(package().system_support().absFileName(), "examples")));
2184 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2185 FileDialog::Result result =
2186 dlg.open(toqstr(initpath), filter);
2188 if (result.first == FileDialog::Later)
2191 filename = fromqstr(result.second);
2193 // check selected filename
2194 if (filename.empty()) {
2195 message(_("Canceled."));
2201 // get absolute path of file and add ".lyx" to the filename if
2203 FileName const fullname =
2204 fileSearch(string(), filename, "lyx", support::may_not_exist);
2205 if (!fullname.empty())
2206 filename = fullname.absFileName();
2208 if (!fullname.onlyPath().isDirectory()) {
2209 Alert::warning(_("Invalid filename"),
2210 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2211 from_utf8(fullname.absFileName())));
2215 // if the file doesn't exist and isn't already open (bug 6645),
2216 // let the user create one
2217 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2218 !LyXVC::file_not_found_hook(fullname)) {
2219 // the user specifically chose this name. Believe him.
2220 Buffer * const b = newFile(filename, string(), true);
2226 docstring const disp_fn = makeDisplayPath(filename);
2227 message(bformat(_("Opening document %1$s..."), disp_fn));
2230 Buffer * buf = loadDocument(fullname);
2232 str2 = bformat(_("Document %1$s opened."), disp_fn);
2233 if (buf->lyxvc().inUse())
2234 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2235 " " + _("Version control detected.");
2237 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2242 // FIXME: clean that
2243 static bool import(GuiView * lv, FileName const & filename,
2244 string const & format, ErrorList & errorList)
2246 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2248 string loader_format;
2249 vector<string> loaders = theConverters().loaders();
2250 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2251 vector<string>::const_iterator it = loaders.begin();
2252 vector<string>::const_iterator en = loaders.end();
2253 for (; it != en; ++it) {
2254 if (!theConverters().isReachable(format, *it))
2257 string const tofile =
2258 support::changeExtension(filename.absFileName(),
2259 formats.extension(*it));
2260 if (!theConverters().convert(0, filename, FileName(tofile),
2261 filename, format, *it, errorList))
2263 loader_format = *it;
2266 if (loader_format.empty()) {
2267 frontend::Alert::error(_("Couldn't import file"),
2268 bformat(_("No information for importing the format %1$s."),
2269 formats.prettyName(format)));
2273 loader_format = format;
2275 if (loader_format == "lyx") {
2276 Buffer * buf = lv->loadDocument(lyxfile);
2280 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2284 bool as_paragraphs = loader_format == "textparagraph";
2285 string filename2 = (loader_format == format) ? filename.absFileName()
2286 : support::changeExtension(filename.absFileName(),
2287 formats.extension(loader_format));
2288 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2290 guiApp->setCurrentView(lv);
2291 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2298 void GuiView::importDocument(string const & argument)
2301 string filename = split(argument, format, ' ');
2303 LYXERR(Debug::INFO, format << " file: " << filename);
2305 // need user interaction
2306 if (filename.empty()) {
2307 string initpath = lyxrc.document_path;
2308 if (documentBufferView()) {
2309 string const trypath = documentBufferView()->buffer().filePath();
2310 // If directory is writeable, use this as default.
2311 if (FileName(trypath).isDirWritable())
2315 docstring const text = bformat(_("Select %1$s file to import"),
2316 formats.prettyName(format));
2318 FileDialog dlg(toqstr(text));
2319 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2320 dlg.setButton2(qt_("Examples|#E#e"),
2321 toqstr(addPath(package().system_support().absFileName(), "examples")));
2323 docstring filter = formats.prettyName(format);
2326 filter += from_utf8(formats.extensions(format));
2329 FileDialog::Result result =
2330 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2332 if (result.first == FileDialog::Later)
2335 filename = fromqstr(result.second);
2337 // check selected filename
2338 if (filename.empty())
2339 message(_("Canceled."));
2342 if (filename.empty())
2345 // get absolute path of file
2346 FileName const fullname(support::makeAbsPath(filename));
2348 // Can happen if the user entered a path into the dialog
2350 if (fullname.onlyFileName().empty()) {
2351 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2352 "Aborting import."),
2353 from_utf8(fullname.absFileName()));
2354 frontend::Alert::error(_("File name error"), msg);
2355 message(_("Canceled."));
2360 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2362 // Check if the document already is open
2363 Buffer * buf = theBufferList().getBuffer(lyxfile);
2366 if (!closeBuffer()) {
2367 message(_("Canceled."));
2372 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2374 // if the file exists already, and we didn't do
2375 // -i lyx thefile.lyx, warn
2376 if (lyxfile.exists() && fullname != lyxfile) {
2378 docstring text = bformat(_("The document %1$s already exists.\n\n"
2379 "Do you want to overwrite that document?"), displaypath);
2380 int const ret = Alert::prompt(_("Overwrite document?"),
2381 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2384 message(_("Canceled."));
2389 message(bformat(_("Importing %1$s..."), displaypath));
2390 ErrorList errorList;
2391 if (import(this, fullname, format, errorList))
2392 message(_("imported."));
2394 message(_("file not imported!"));
2396 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2400 void GuiView::newDocument(string const & filename, bool from_template)
2402 FileName initpath(lyxrc.document_path);
2403 if (documentBufferView()) {
2404 FileName const trypath(documentBufferView()->buffer().filePath());
2405 // If directory is writeable, use this as default.
2406 if (trypath.isDirWritable())
2410 string templatefile;
2411 if (from_template) {
2412 templatefile = selectTemplateFile().absFileName();
2413 if (templatefile.empty())
2418 if (filename.empty())
2419 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2421 b = newFile(filename, templatefile, true);
2426 // If no new document could be created, it is unsure
2427 // whether there is a valid BufferView.
2428 if (currentBufferView())
2429 // Ensure the cursor is correctly positioned on screen.
2430 currentBufferView()->showCursor();
2434 void GuiView::insertLyXFile(docstring const & fname)
2436 BufferView * bv = documentBufferView();
2441 FileName filename(to_utf8(fname));
2442 if (filename.empty()) {
2443 // Launch a file browser
2445 string initpath = lyxrc.document_path;
2446 string const trypath = bv->buffer().filePath();
2447 // If directory is writeable, use this as default.
2448 if (FileName(trypath).isDirWritable())
2452 FileDialog dlg(qt_("Select LyX document to insert"));
2453 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2454 dlg.setButton2(qt_("Examples|#E#e"),
2455 toqstr(addPath(package().system_support().absFileName(),
2458 FileDialog::Result result = dlg.open(toqstr(initpath),
2459 QStringList(qt_("LyX Documents (*.lyx)")));
2461 if (result.first == FileDialog::Later)
2465 filename.set(fromqstr(result.second));
2467 // check selected filename
2468 if (filename.empty()) {
2469 // emit message signal.
2470 message(_("Canceled."));
2475 bv->insertLyXFile(filename);
2476 bv->buffer().errors("Parse");
2480 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2482 FileName fname = b.fileName();
2483 FileName const oldname = fname;
2485 if (!newname.empty()) {
2487 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2489 // Switch to this Buffer.
2492 // No argument? Ask user through dialog.
2494 FileDialog dlg(qt_("Choose a filename to save document as"));
2495 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2496 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2498 if (!isLyXFileName(fname.absFileName()))
2499 fname.changeExtension(".lyx");
2501 FileDialog::Result result =
2502 dlg.save(toqstr(fname.onlyPath().absFileName()),
2503 QStringList(qt_("LyX Documents (*.lyx)")),
2504 toqstr(fname.onlyFileName()));
2506 if (result.first == FileDialog::Later)
2509 fname.set(fromqstr(result.second));
2514 if (!isLyXFileName(fname.absFileName()))
2515 fname.changeExtension(".lyx");
2518 // fname is now the new Buffer location.
2520 // if there is already a Buffer open with this name, we do not want
2521 // to have another one. (the second test makes sure we're not just
2522 // trying to overwrite ourselves, which is fine.)
2523 if (theBufferList().exists(fname) && fname != oldname
2524 && theBufferList().getBuffer(fname) != &b) {
2525 docstring const text =
2526 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2527 "Please close it before attempting to overwrite it.\n"
2528 "Do you want to choose a new filename?"),
2529 from_utf8(fname.absFileName()));
2530 int const ret = Alert::prompt(_("Chosen File Already Open"),
2531 text, 0, 1, _("&Rename"), _("&Cancel"));
2533 case 0: return renameBuffer(b, docstring(), kind);
2534 case 1: return false;
2539 bool const existsLocal = fname.exists();
2540 bool const existsInVC = LyXVC::fileInVC(fname);
2541 if (existsLocal || existsInVC) {
2542 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2543 if (kind != LV_WRITE_AS && existsInVC) {
2544 // renaming to a name that is already in VC
2546 docstring text = bformat(_("The document %1$s "
2547 "is already registered.\n\n"
2548 "Do you want to choose a new name?"),
2550 docstring const title = (kind == LV_VC_RENAME) ?
2551 _("Rename document?") : _("Copy document?");
2552 docstring const button = (kind == LV_VC_RENAME) ?
2553 _("&Rename") : _("&Copy");
2554 int const ret = Alert::prompt(title, text, 0, 1,
2555 button, _("&Cancel"));
2557 case 0: return renameBuffer(b, docstring(), kind);
2558 case 1: return false;
2563 docstring text = bformat(_("The document %1$s "
2564 "already exists.\n\n"
2565 "Do you want to overwrite that document?"),
2567 int const ret = Alert::prompt(_("Overwrite document?"),
2568 text, 0, 2, _("&Overwrite"),
2569 _("&Rename"), _("&Cancel"));
2572 case 1: return renameBuffer(b, docstring(), kind);
2573 case 2: return false;
2579 case LV_VC_RENAME: {
2580 string msg = b.lyxvc().rename(fname);
2583 message(from_utf8(msg));
2587 string msg = b.lyxvc().copy(fname);
2590 message(from_utf8(msg));
2596 // LyXVC created the file already in case of LV_VC_RENAME or
2597 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2598 // relative paths of included stuff right if we moved e.g. from
2599 // /a/b.lyx to /a/c/b.lyx.
2601 bool const saved = saveBuffer(b, fname);
2608 struct PrettyNameComparator
2610 bool operator()(Format const *first, Format const *second) const {
2611 return compare_no_case(translateIfPossible(from_ascii(first->prettyname())),
2612 translateIfPossible(from_ascii(second->prettyname()))) <= 0;
2617 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2619 FileName fname = b.fileName();
2621 FileDialog dlg(qt_("Choose a filename to export the document as"));
2622 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2625 QString const anyformat = qt_("Guess from extension (*.*)");
2627 Formats::const_iterator it = formats.begin();
2628 vector<Format const *> export_formats;
2629 for (; it != formats.end(); ++it)
2630 if (it->documentFormat())
2631 export_formats.push_back(&(*it));
2632 PrettyNameComparator cmp;
2633 sort(export_formats.begin(), export_formats.end(), cmp);
2634 vector<Format const *>::const_iterator fit = export_formats.begin();
2635 map<QString, string> fmap;
2638 for (; fit != export_formats.end(); ++fit) {
2639 docstring const loc_prettyname =
2640 translateIfPossible(from_utf8((*fit)->prettyname()));
2641 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2643 from_ascii((*fit)->extension())));
2644 types << loc_filter;
2645 fmap[loc_filter] = (*fit)->name();
2646 if (from_ascii((*fit)->name()) == iformat) {
2647 filter = loc_filter;
2648 ext = (*fit)->extension();
2651 string ofname = fname.onlyFileName();
2653 ofname = support::changeExtension(ofname, ext);
2654 FileDialog::Result result =
2655 dlg.save(toqstr(fname.onlyPath().absFileName()),
2659 if (result.first != FileDialog::Chosen)
2663 fname.set(fromqstr(result.second));
2664 if (filter == anyformat)
2665 fmt_name = formats.getFormatFromExtension(fname.extension());
2667 fmt_name = fmap[filter];
2668 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2669 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2671 if (fmt_name.empty() || fname.empty())
2674 // fname is now the new Buffer location.
2675 if (FileName(fname).exists()) {
2676 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2677 docstring text = bformat(_("The document %1$s already "
2678 "exists.\n\nDo you want to "
2679 "overwrite that document?"),
2681 int const ret = Alert::prompt(_("Overwrite document?"),
2682 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2685 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2686 case 2: return false;
2690 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2693 return dr.dispatched();
2697 bool GuiView::saveBuffer(Buffer & b)
2699 return saveBuffer(b, FileName());
2703 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2705 if (workArea(b) && workArea(b)->inDialogMode())
2708 if (fn.empty() && b.isUnnamed())
2709 return renameBuffer(b, docstring());
2711 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2713 theSession().lastFiles().add(b.fileName());
2717 // Switch to this Buffer.
2720 // FIXME: we don't tell the user *WHY* the save failed !!
2721 docstring const file = makeDisplayPath(b.absFileName(), 30);
2722 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2723 "Do you want to rename the document and "
2724 "try again?"), file);
2725 int const ret = Alert::prompt(_("Rename and save?"),
2726 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2729 if (!renameBuffer(b, docstring()))
2738 return saveBuffer(b, fn);
2742 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2744 return closeWorkArea(wa, false);
2748 // We only want to close the buffer if it is not visible in other workareas
2749 // of the same view, nor in other views, and if this is not a child
2750 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2752 Buffer & buf = wa->bufferView().buffer();
2754 bool last_wa = d.countWorkAreasOf(buf) == 1
2755 && !inOtherView(buf) && !buf.parent();
2757 bool close_buffer = last_wa;
2760 if (lyxrc.close_buffer_with_last_view == "yes")
2762 else if (lyxrc.close_buffer_with_last_view == "no")
2763 close_buffer = false;
2766 if (buf.isUnnamed())
2767 file = from_utf8(buf.fileName().onlyFileName());
2769 file = buf.fileName().displayName(30);
2770 docstring const text = bformat(
2771 _("Last view on document %1$s is being closed.\n"
2772 "Would you like to close or hide the document?\n"
2774 "Hidden documents can be displayed back through\n"
2775 "the menu: View->Hidden->...\n"
2777 "To remove this question, set your preference in:\n"
2778 " Tools->Preferences->Look&Feel->UserInterface\n"
2780 int ret = Alert::prompt(_("Close or hide document?"),
2781 text, 0, 1, _("&Close"), _("&Hide"));
2782 close_buffer = (ret == 0);
2786 return closeWorkArea(wa, close_buffer);
2790 bool GuiView::closeBuffer()
2792 GuiWorkArea * wa = currentMainWorkArea();
2793 setCurrentWorkArea(wa);
2794 Buffer & buf = wa->bufferView().buffer();
2795 return wa && closeWorkArea(wa, !buf.parent());
2799 void GuiView::writeSession() const {
2800 GuiWorkArea const * active_wa = currentMainWorkArea();
2801 for (int i = 0; i < d.splitter_->count(); ++i) {
2802 TabWorkArea * twa = d.tabWorkArea(i);
2803 for (int j = 0; j < twa->count(); ++j) {
2804 GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2805 Buffer & buf = wa->bufferView().buffer();
2806 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2812 bool GuiView::closeBufferAll()
2814 // Close the workareas in all other views
2815 QList<int> const ids = guiApp->viewIds();
2816 for (int i = 0; i != ids.size(); ++i) {
2817 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2821 // Close our own workareas
2822 if (!closeWorkAreaAll())
2825 // Now close the hidden buffers. We prevent hidden buffers from being
2826 // dirty, so we can just close them.
2827 theBufferList().closeAll();
2832 bool GuiView::closeWorkAreaAll()
2834 setCurrentWorkArea(currentMainWorkArea());
2836 // We might be in a situation that there is still a tabWorkArea, but
2837 // there are no tabs anymore. This can happen when we get here after a
2838 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2839 // many TabWorkArea's have no documents anymore.
2842 // We have to call count() each time, because it can happen that
2843 // more than one splitter will disappear in one iteration (bug 5998).
2844 while (d.splitter_->count() > empty_twa) {
2845 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2847 if (twa->count() == 0)
2850 setCurrentWorkArea(twa->currentWorkArea());
2851 if (!closeTabWorkArea(twa))
2859 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2864 Buffer & buf = wa->bufferView().buffer();
2866 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2867 Alert::warning(_("Close document"),
2868 _("Document could not be closed because it is being processed by LyX."));
2873 return closeBuffer(buf);
2875 if (!inMultiTabs(wa))
2876 if (!saveBufferIfNeeded(buf, true))
2884 bool GuiView::closeBuffer(Buffer & buf)
2886 // If we are in a close_event all children will be closed in some time,
2887 // so no need to do it here. This will ensure that the children end up
2888 // in the session file in the correct order. If we close the master
2889 // buffer, we can close or release the child buffers here too.
2890 bool success = true;
2892 ListOfBuffers clist = buf.getChildren();
2893 ListOfBuffers::const_iterator it = clist.begin();
2894 ListOfBuffers::const_iterator const bend = clist.end();
2895 for (; it != bend; ++it) {
2896 Buffer * child_buf = *it;
2897 if (theBufferList().isOthersChild(&buf, child_buf)) {
2898 child_buf->setParent(0);
2902 // FIXME: should we look in other tabworkareas?
2903 // ANSWER: I don't think so. I've tested, and if the child is
2904 // open in some other window, it closes without a problem.
2905 GuiWorkArea * child_wa = workArea(*child_buf);
2907 success = closeWorkArea(child_wa, true);
2911 // In this case the child buffer is open but hidden.
2912 // It therefore should not (MUST NOT) be dirty!
2913 LATTEST(child_buf->isClean());
2914 theBufferList().release(child_buf);
2919 // goto bookmark to update bookmark pit.
2920 // FIXME: we should update only the bookmarks related to this buffer!
2921 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2922 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2923 guiApp->gotoBookmark(i+1, false, false);
2925 if (saveBufferIfNeeded(buf, false)) {
2926 buf.removeAutosaveFile();
2927 theBufferList().release(&buf);
2931 // open all children again to avoid a crash because of dangling
2932 // pointers (bug 6603)
2938 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2940 while (twa == d.currentTabWorkArea()) {
2941 twa->setCurrentIndex(twa->count() - 1);
2943 GuiWorkArea * wa = twa->currentWorkArea();
2944 Buffer & b = wa->bufferView().buffer();
2946 // We only want to close the buffer if the same buffer is not visible
2947 // in another view, and if this is not a child and if we are closing
2948 // a view (not a tabgroup).
2949 bool const close_buffer =
2950 !inOtherView(b) && !b.parent() && closing_;
2952 if (!closeWorkArea(wa, close_buffer))
2959 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2961 if (buf.isClean() || buf.paragraphs().empty())
2964 // Switch to this Buffer.
2969 if (buf.isUnnamed())
2970 file = from_utf8(buf.fileName().onlyFileName());
2972 file = buf.fileName().displayName(30);
2974 // Bring this window to top before asking questions.
2979 if (hiding && buf.isUnnamed()) {
2980 docstring const text = bformat(_("The document %1$s has not been "
2981 "saved yet.\n\nDo you want to save "
2982 "the document?"), file);
2983 ret = Alert::prompt(_("Save new document?"),
2984 text, 0, 1, _("&Save"), _("&Cancel"));
2988 docstring const text = bformat(_("The document %1$s has unsaved changes."
2989 "\n\nDo you want to save the document or discard the changes?"), file);
2990 ret = Alert::prompt(_("Save changed document?"),
2991 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2996 if (!saveBuffer(buf))
3000 // If we crash after this we could have no autosave file
3001 // but I guess this is really improbable (Jug).
3002 // Sometimes improbable things happen:
3003 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3004 // buf.removeAutosaveFile();
3006 // revert all changes
3017 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3019 Buffer & buf = wa->bufferView().buffer();
3021 for (int i = 0; i != d.splitter_->count(); ++i) {
3022 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3023 if (wa_ && wa_ != wa)
3026 return inOtherView(buf);
3030 bool GuiView::inOtherView(Buffer & buf)
3032 QList<int> const ids = guiApp->viewIds();
3034 for (int i = 0; i != ids.size(); ++i) {
3038 if (guiApp->view(ids[i]).workArea(buf))
3045 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3047 if (!documentBufferView())
3050 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3051 Buffer * const curbuf = &documentBufferView()->buffer();
3052 int nwa = twa->count();
3053 for (int i = 0; i < nwa; ++i) {
3054 if (&workArea(i)->bufferView().buffer() == curbuf) {
3056 if (np == NEXTBUFFER)
3057 next_index = (i == nwa - 1 ? 0 : i + 1);
3059 next_index = (i == 0 ? nwa - 1 : i - 1);
3061 twa->moveTab(i, next_index);
3063 setBuffer(&workArea(next_index)->bufferView().buffer());
3071 /// make sure the document is saved
3072 static bool ensureBufferClean(Buffer * buffer)
3074 LASSERT(buffer, return false);
3075 if (buffer->isClean() && !buffer->isUnnamed())
3078 docstring const file = buffer->fileName().displayName(30);
3081 if (!buffer->isUnnamed()) {
3082 text = bformat(_("The document %1$s has unsaved "
3083 "changes.\n\nDo you want to save "
3084 "the document?"), file);
3085 title = _("Save changed document?");
3088 text = bformat(_("The document %1$s has not been "
3089 "saved yet.\n\nDo you want to save "
3090 "the document?"), file);
3091 title = _("Save new document?");
3093 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3096 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3098 return buffer->isClean() && !buffer->isUnnamed();
3102 bool GuiView::reloadBuffer(Buffer & buf)
3104 Buffer::ReadStatus status = buf.reload();
3105 return status == Buffer::ReadSuccess;
3109 void GuiView::checkExternallyModifiedBuffers()
3111 BufferList::iterator bit = theBufferList().begin();
3112 BufferList::iterator const bend = theBufferList().end();
3113 for (; bit != bend; ++bit) {
3114 Buffer * buf = *bit;
3115 if (buf->fileName().exists()
3116 && buf->isExternallyModified(Buffer::checksum_method)) {
3117 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3118 " Reload now? Any local changes will be lost."),
3119 from_utf8(buf->absFileName()));
3120 int const ret = Alert::prompt(_("Reload externally changed document?"),
3121 text, 0, 1, _("&Reload"), _("&Cancel"));
3129 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3131 Buffer * buffer = documentBufferView()
3132 ? &(documentBufferView()->buffer()) : 0;
3134 switch (cmd.action()) {
3135 case LFUN_VC_REGISTER:
3136 if (!buffer || !ensureBufferClean(buffer))
3138 if (!buffer->lyxvc().inUse()) {
3139 if (buffer->lyxvc().registrer()) {
3140 reloadBuffer(*buffer);
3141 dr.clearMessageUpdate();
3146 case LFUN_VC_RENAME:
3147 case LFUN_VC_COPY: {
3148 if (!buffer || !ensureBufferClean(buffer))
3150 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3151 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3152 // Some changes are not yet committed.
3153 // We test here and not in getStatus(), since
3154 // this test is expensive.
3156 LyXVC::CommandResult ret =
3157 buffer->lyxvc().checkIn(log);
3159 if (ret == LyXVC::ErrorCommand ||
3160 ret == LyXVC::VCSuccess)
3161 reloadBuffer(*buffer);
3162 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3163 frontend::Alert::error(
3164 _("Revision control error."),
3165 _("Document could not be checked in."));
3169 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3170 LV_VC_RENAME : LV_VC_COPY;
3171 renameBuffer(*buffer, cmd.argument(), kind);
3176 case LFUN_VC_CHECK_IN:
3177 if (!buffer || !ensureBufferClean(buffer))
3179 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3181 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3183 // Only skip reloading if the checkin was cancelled or
3184 // an error occurred before the real checkin VCS command
3185 // was executed, since the VCS might have changed the
3186 // file even if it could not checkin successfully.
3187 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3188 reloadBuffer(*buffer);
3192 case LFUN_VC_CHECK_OUT:
3193 if (!buffer || !ensureBufferClean(buffer))
3195 if (buffer->lyxvc().inUse()) {
3196 dr.setMessage(buffer->lyxvc().checkOut());
3197 reloadBuffer(*buffer);
3201 case LFUN_VC_LOCKING_TOGGLE:
3202 LASSERT(buffer, return);
3203 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3205 if (buffer->lyxvc().inUse()) {
3206 string res = buffer->lyxvc().lockingToggle();
3208 frontend::Alert::error(_("Revision control error."),
3209 _("Error when setting the locking property."));
3212 reloadBuffer(*buffer);
3217 case LFUN_VC_REVERT:
3218 LASSERT(buffer, return);
3219 if (buffer->lyxvc().revert()) {
3220 reloadBuffer(*buffer);
3221 dr.clearMessageUpdate();
3225 case LFUN_VC_UNDO_LAST:
3226 LASSERT(buffer, return);
3227 buffer->lyxvc().undoLast();
3228 reloadBuffer(*buffer);
3229 dr.clearMessageUpdate();
3232 case LFUN_VC_REPO_UPDATE:
3233 LASSERT(buffer, return);
3234 if (ensureBufferClean(buffer)) {
3235 dr.setMessage(buffer->lyxvc().repoUpdate());
3236 checkExternallyModifiedBuffers();
3240 case LFUN_VC_COMMAND: {
3241 string flag = cmd.getArg(0);
3242 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3245 if (contains(flag, 'M')) {
3246 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3249 string path = cmd.getArg(1);
3250 if (contains(path, "$$p") && buffer)
3251 path = subst(path, "$$p", buffer->filePath());
3252 LYXERR(Debug::LYXVC, "Directory: " << path);
3254 if (!pp.isReadableDirectory()) {
3255 lyxerr << _("Directory is not accessible.") << endl;
3258 support::PathChanger p(pp);
3260 string command = cmd.getArg(2);
3261 if (command.empty())
3264 command = subst(command, "$$i", buffer->absFileName());
3265 command = subst(command, "$$p", buffer->filePath());
3267 command = subst(command, "$$m", to_utf8(message));
3268 LYXERR(Debug::LYXVC, "Command: " << command);
3270 one.startscript(Systemcall::Wait, command);
3274 if (contains(flag, 'I'))
3275 buffer->markDirty();
3276 if (contains(flag, 'R'))
3277 reloadBuffer(*buffer);
3282 case LFUN_VC_COMPARE: {
3284 if (cmd.argument().empty()) {
3285 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3289 string rev1 = cmd.getArg(0);
3293 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3296 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3297 f2 = buffer->absFileName();
3299 string rev2 = cmd.getArg(1);
3303 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3307 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3308 f1 << "\n" << f2 << "\n" );
3309 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3310 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3320 void GuiView::openChildDocument(string const & fname)
3322 LASSERT(documentBufferView(), return);
3323 Buffer & buffer = documentBufferView()->buffer();
3324 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3325 documentBufferView()->saveBookmark(false);
3327 if (theBufferList().exists(filename)) {
3328 child = theBufferList().getBuffer(filename);
3331 message(bformat(_("Opening child document %1$s..."),
3332 makeDisplayPath(filename.absFileName())));
3333 child = loadDocument(filename, false);
3335 // Set the parent name of the child document.
3336 // This makes insertion of citations and references in the child work,
3337 // when the target is in the parent or another child document.
3339 child->setParent(&buffer);
3343 bool GuiView::goToFileRow(string const & argument)
3347 size_t i = argument.find_last_of(' ');
3348 if (i != string::npos) {
3349 file_name = os::internal_path(trim(argument.substr(0, i)));
3350 istringstream is(argument.substr(i + 1));
3355 if (i == string::npos) {
3356 LYXERR0("Wrong argument: " << argument);
3360 string const abstmp = package().temp_dir().absFileName();
3361 string const realtmp = package().temp_dir().realPath();
3362 // We have to use os::path_prefix_is() here, instead of
3363 // simply prefixIs(), because the file name comes from
3364 // an external application and may need case adjustment.
3365 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3366 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3367 // Needed by inverse dvi search. If it is a file
3368 // in tmpdir, call the apropriated function.
3369 // If tmpdir is a symlink, we may have the real
3370 // path passed back, so we correct for that.
3371 if (!prefixIs(file_name, abstmp))
3372 file_name = subst(file_name, realtmp, abstmp);
3373 buf = theBufferList().getBufferFromTmp(file_name);
3375 // Must replace extension of the file to be .lyx
3376 // and get full path
3377 FileName const s = fileSearch(string(),
3378 support::changeExtension(file_name, ".lyx"), "lyx");
3379 // Either change buffer or load the file
3380 if (theBufferList().exists(s))
3381 buf = theBufferList().getBuffer(s);
3382 else if (s.exists()) {
3383 buf = loadDocument(s);
3388 _("File does not exist: %1$s"),
3389 makeDisplayPath(file_name)));
3395 _("No buffer for file: %1$s."),
3396 makeDisplayPath(file_name))
3401 documentBufferView()->setCursorFromRow(row);
3407 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3409 Buffer::ExportStatus const status = func(format);
3411 // the cloning operation will have produced a clone of the entire set of
3412 // documents, starting from the master. so we must delete those.
3413 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3415 busyBuffers.remove(orig);
3420 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3422 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3423 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3427 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3429 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3430 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3434 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3436 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3437 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3441 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3442 string const & argument,
3443 Buffer const * used_buffer,
3444 docstring const & msg,
3445 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3446 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3447 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3452 string format = argument;
3454 format = used_buffer->params().getDefaultOutputFormat();
3455 processing_format = format;
3457 progress_->clearMessages();
3460 #if EXPORT_in_THREAD
3461 GuiViewPrivate::busyBuffers.insert(used_buffer);
3462 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3463 if (!cloned_buffer) {
3464 Alert::error(_("Export Error"),
3465 _("Error cloning the Buffer."));
3468 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3473 setPreviewFuture(f);
3474 last_export_format = used_buffer->params().bufferFormat();
3477 // We are asynchronous, so we don't know here anything about the success
3480 Buffer::ExportStatus status;
3482 // TODO check here if it breaks exporting with Qt < 4.4
3483 status = (used_buffer->*syncFunc)(format, true);
3484 } else if (previewFunc) {
3485 status = (used_buffer->*previewFunc)(format);
3488 handleExportStatus(gv_, status, format);
3490 return (status == Buffer::ExportSuccess
3491 || status == Buffer::PreviewSuccess);
3495 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3497 BufferView * bv = currentBufferView();
3498 LASSERT(bv, return);
3500 // Let the current BufferView dispatch its own actions.
3501 bv->dispatch(cmd, dr);
3502 if (dr.dispatched())
3505 // Try with the document BufferView dispatch if any.
3506 BufferView * doc_bv = documentBufferView();
3507 if (doc_bv && doc_bv != bv) {
3508 doc_bv->dispatch(cmd, dr);
3509 if (dr.dispatched())
3513 // Then let the current Cursor dispatch its own actions.
3514 bv->cursor().dispatch(cmd);
3516 // update completion. We do it here and not in
3517 // processKeySym to avoid another redraw just for a
3518 // changed inline completion
3519 if (cmd.origin() == FuncRequest::KEYBOARD) {
3520 if (cmd.action() == LFUN_SELF_INSERT
3521 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3522 updateCompletion(bv->cursor(), true, true);
3523 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3524 updateCompletion(bv->cursor(), false, true);
3526 updateCompletion(bv->cursor(), false, false);
3529 dr = bv->cursor().result();
3533 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3535 BufferView * bv = currentBufferView();
3536 // By default we won't need any update.
3537 dr.screenUpdate(Update::None);
3538 // assume cmd will be dispatched
3539 dr.dispatched(true);
3541 Buffer * doc_buffer = documentBufferView()
3542 ? &(documentBufferView()->buffer()) : 0;
3544 if (cmd.origin() == FuncRequest::TOC) {
3545 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3546 // FIXME: do we need to pass a DispatchResult object here?
3547 toc->doDispatch(bv->cursor(), cmd);
3551 string const argument = to_utf8(cmd.argument());
3553 switch(cmd.action()) {
3554 case LFUN_BUFFER_CHILD_OPEN:
3555 openChildDocument(to_utf8(cmd.argument()));
3558 case LFUN_BUFFER_IMPORT:
3559 importDocument(to_utf8(cmd.argument()));
3562 case LFUN_BUFFER_EXPORT: {
3565 FileName target_dir = doc_buffer->fileName().onlyPath();
3566 string const dest = cmd.getArg(1);
3567 if (!dest.empty() && FileName::isAbsolute(dest))
3568 target_dir = FileName(support::onlyPath(dest));
3569 // GCC only sees strfwd.h when building merged
3570 if (::lyx::operator==(cmd.argument(), "custom")) {
3571 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3574 if ((dest.empty() && doc_buffer->isUnnamed())
3575 || !target_dir.isDirWritable()) {
3576 exportBufferAs(*doc_buffer, cmd.argument());
3579 /* TODO/Review: Is it a problem to also export the children?
3580 See the update_unincluded flag */
3581 d.asyncBufferProcessing(argument,
3584 &GuiViewPrivate::exportAndDestroy,
3587 // TODO Inform user about success
3591 case LFUN_BUFFER_EXPORT_AS: {
3592 LASSERT(doc_buffer, break);
3593 docstring f = cmd.argument();
3595 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3596 exportBufferAs(*doc_buffer, f);
3600 case LFUN_BUFFER_UPDATE: {
3601 d.asyncBufferProcessing(argument,
3604 &GuiViewPrivate::compileAndDestroy,
3609 case LFUN_BUFFER_VIEW: {
3610 d.asyncBufferProcessing(argument,
3612 _("Previewing ..."),
3613 &GuiViewPrivate::previewAndDestroy,
3618 case LFUN_MASTER_BUFFER_UPDATE: {
3619 d.asyncBufferProcessing(argument,
3620 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3622 &GuiViewPrivate::compileAndDestroy,
3627 case LFUN_MASTER_BUFFER_VIEW: {
3628 d.asyncBufferProcessing(argument,
3629 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3631 &GuiViewPrivate::previewAndDestroy,
3632 0, &Buffer::preview);
3635 case LFUN_BUFFER_SWITCH: {
3636 string const file_name = to_utf8(cmd.argument());
3637 if (!FileName::isAbsolute(file_name)) {
3639 dr.setMessage(_("Absolute filename expected."));
3643 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3646 dr.setMessage(_("Document not loaded"));
3650 // Do we open or switch to the buffer in this view ?
3651 if (workArea(*buffer)
3652 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3657 // Look for the buffer in other views
3658 QList<int> const ids = guiApp->viewIds();
3660 for (; i != ids.size(); ++i) {
3661 GuiView & gv = guiApp->view(ids[i]);
3662 if (gv.workArea(*buffer)) {
3664 gv.activateWindow();
3666 gv.setBuffer(buffer);
3671 // If necessary, open a new window as a last resort
3672 if (i == ids.size()) {
3673 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3679 case LFUN_BUFFER_NEXT:
3680 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3683 case LFUN_BUFFER_MOVE_NEXT:
3684 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3687 case LFUN_BUFFER_PREVIOUS:
3688 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3691 case LFUN_BUFFER_MOVE_PREVIOUS:
3692 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3695 case LFUN_COMMAND_EXECUTE: {
3696 command_execute_ = true;
3697 minibuffer_focus_ = true;
3700 case LFUN_DROP_LAYOUTS_CHOICE:
3701 d.layout_->showPopup();
3704 case LFUN_MENU_OPEN:
3705 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3706 menu->exec(QCursor::pos());
3709 case LFUN_FILE_INSERT:
3710 insertLyXFile(cmd.argument());
3713 case LFUN_FILE_INSERT_PLAINTEXT:
3714 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3715 string const fname = to_utf8(cmd.argument());
3716 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3717 dr.setMessage(_("Absolute filename expected."));
3721 FileName filename(fname);
3722 if (fname.empty()) {
3723 FileDialog dlg(qt_("Select file to insert"));
3725 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3726 QStringList(qt_("All Files (*)")));
3728 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3729 dr.setMessage(_("Canceled."));
3733 filename.set(fromqstr(result.second));
3737 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3738 bv->dispatch(new_cmd, dr);
3743 case LFUN_BUFFER_RELOAD: {
3744 LASSERT(doc_buffer, break);
3747 if (!doc_buffer->isClean()) {
3748 docstring const file =
3749 makeDisplayPath(doc_buffer->absFileName(), 20);
3750 docstring text = bformat(_("Any changes will be lost. "
3751 "Are you sure you want to revert to the saved version "
3752 "of the document %1$s?"), file);
3753 ret = Alert::prompt(_("Revert to saved document?"),
3754 text, 1, 1, _("&Revert"), _("&Cancel"));
3758 doc_buffer->markClean();
3759 reloadBuffer(*doc_buffer);
3760 dr.forceBufferUpdate();
3765 case LFUN_BUFFER_WRITE:
3766 LASSERT(doc_buffer, break);
3767 saveBuffer(*doc_buffer);
3770 case LFUN_BUFFER_WRITE_AS:
3771 LASSERT(doc_buffer, break);
3772 renameBuffer(*doc_buffer, cmd.argument());
3775 case LFUN_BUFFER_WRITE_ALL: {
3776 Buffer * first = theBufferList().first();
3779 message(_("Saving all documents..."));
3780 // We cannot use a for loop as the buffer list cycles.
3783 if (!b->isClean()) {
3785 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3787 b = theBufferList().next(b);
3788 } while (b != first);
3789 dr.setMessage(_("All documents saved."));
3793 case LFUN_BUFFER_CLOSE:
3797 case LFUN_BUFFER_CLOSE_ALL:
3801 case LFUN_TOOLBAR_TOGGLE: {
3802 string const name = cmd.getArg(0);
3803 if (GuiToolbar * t = toolbar(name))
3808 case LFUN_DIALOG_UPDATE: {
3809 string const name = to_utf8(cmd.argument());
3810 if (name == "prefs" || name == "document")
3811 updateDialog(name, string());
3812 else if (name == "paragraph")
3813 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3814 else if (currentBufferView()) {
3815 Inset * inset = currentBufferView()->editedInset(name);
3816 // Can only update a dialog connected to an existing inset
3818 // FIXME: get rid of this indirection; GuiView ask the inset
3819 // if he is kind enough to update itself...
3820 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3821 //FIXME: pass DispatchResult here?
3822 inset->dispatch(currentBufferView()->cursor(), fr);
3828 case LFUN_DIALOG_TOGGLE: {
3829 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3830 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3831 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3835 case LFUN_DIALOG_DISCONNECT_INSET:
3836 disconnectDialog(to_utf8(cmd.argument()));
3839 case LFUN_DIALOG_HIDE: {
3840 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3844 case LFUN_DIALOG_SHOW: {
3845 string const name = cmd.getArg(0);
3846 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3848 if (name == "character") {
3849 data = freefont2string();
3851 showDialog("character", data);
3852 } else if (name == "latexlog") {
3853 Buffer::LogType type;
3854 string const logfile = doc_buffer->logName(&type);
3856 case Buffer::latexlog:
3859 case Buffer::buildlog:
3863 data += Lexer::quoteString(logfile);
3864 showDialog("log", data);
3865 } else if (name == "vclog") {
3866 string const data = "vc " +
3867 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3868 showDialog("log", data);
3869 } else if (name == "symbols") {
3870 data = bv->cursor().getEncoding()->name();
3872 showDialog("symbols", data);
3874 } else if (name == "prefs" && isFullScreen()) {
3875 lfunUiToggle("fullscreen");
3876 showDialog("prefs", data);
3878 showDialog(name, data);
3883 dr.setMessage(cmd.argument());
3886 case LFUN_UI_TOGGLE: {
3887 string arg = cmd.getArg(0);
3888 if (!lfunUiToggle(arg)) {
3889 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3890 dr.setMessage(bformat(msg, from_utf8(arg)));
3892 // Make sure the keyboard focus stays in the work area.
3897 case LFUN_VIEW_SPLIT: {
3898 LASSERT(doc_buffer, break);
3899 string const orientation = cmd.getArg(0);
3900 d.splitter_->setOrientation(orientation == "vertical"
3901 ? Qt::Vertical : Qt::Horizontal);
3902 TabWorkArea * twa = addTabWorkArea();
3903 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3904 setCurrentWorkArea(wa);
3907 case LFUN_TAB_GROUP_CLOSE:
3908 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3909 closeTabWorkArea(twa);
3910 d.current_work_area_ = 0;
3911 twa = d.currentTabWorkArea();
3912 // Switch to the next GuiWorkArea in the found TabWorkArea.
3914 // Make sure the work area is up to date.
3915 setCurrentWorkArea(twa->currentWorkArea());
3917 setCurrentWorkArea(0);
3922 case LFUN_VIEW_CLOSE:
3923 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3924 closeWorkArea(twa->currentWorkArea());
3925 d.current_work_area_ = 0;
3926 twa = d.currentTabWorkArea();
3927 // Switch to the next GuiWorkArea in the found TabWorkArea.
3929 // Make sure the work area is up to date.
3930 setCurrentWorkArea(twa->currentWorkArea());
3932 setCurrentWorkArea(0);
3937 case LFUN_COMPLETION_INLINE:
3938 if (d.current_work_area_)
3939 d.current_work_area_->completer().showInline();
3942 case LFUN_COMPLETION_POPUP:
3943 if (d.current_work_area_)
3944 d.current_work_area_->completer().showPopup();
3949 if (d.current_work_area_)
3950 d.current_work_area_->completer().tab();
3953 case LFUN_COMPLETION_CANCEL:
3954 if (d.current_work_area_) {
3955 if (d.current_work_area_->completer().popupVisible())
3956 d.current_work_area_->completer().hidePopup();
3958 d.current_work_area_->completer().hideInline();
3962 case LFUN_COMPLETION_ACCEPT:
3963 if (d.current_work_area_)
3964 d.current_work_area_->completer().activate();
3967 case LFUN_BUFFER_ZOOM_IN:
3968 case LFUN_BUFFER_ZOOM_OUT: {
3969 // use a signed temp to avoid overflow
3970 int zoom = lyxrc.zoom;
3971 if (cmd.argument().empty()) {
3972 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3977 zoom += convert<int>(cmd.argument());
3983 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
3985 // The global QPixmapCache is used in GuiPainter to cache text
3986 // painting so we must reset it.
3987 QPixmapCache::clear();
3988 guiApp->fontLoader().update();
3989 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3993 case LFUN_VC_REGISTER:
3994 case LFUN_VC_RENAME:
3996 case LFUN_VC_CHECK_IN:
3997 case LFUN_VC_CHECK_OUT:
3998 case LFUN_VC_REPO_UPDATE:
3999 case LFUN_VC_LOCKING_TOGGLE:
4000 case LFUN_VC_REVERT:
4001 case LFUN_VC_UNDO_LAST:
4002 case LFUN_VC_COMMAND:
4003 case LFUN_VC_COMPARE:
4004 dispatchVC(cmd, dr);
4007 case LFUN_SERVER_GOTO_FILE_ROW:
4008 goToFileRow(to_utf8(cmd.argument()));
4011 case LFUN_FORWARD_SEARCH: {
4012 Buffer const * doc_master = doc_buffer->masterBuffer();
4013 FileName const path(doc_master->temppath());
4014 string const texname = doc_master->isChild(doc_buffer)
4015 ? DocFileName(changeExtension(
4016 doc_buffer->absFileName(),
4017 "tex")).mangledFileName()
4018 : doc_buffer->latexName();
4019 string const fulltexname =
4020 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4021 string const mastername =
4022 removeExtension(doc_master->latexName());
4023 FileName const dviname(addName(path.absFileName(),
4024 addExtension(mastername, "dvi")));
4025 FileName const pdfname(addName(path.absFileName(),
4026 addExtension(mastername, "pdf")));
4027 bool const have_dvi = dviname.exists();
4028 bool const have_pdf = pdfname.exists();
4029 if (!have_dvi && !have_pdf) {
4030 dr.setMessage(_("Please, preview the document first."));
4033 string outname = dviname.onlyFileName();
4034 string command = lyxrc.forward_search_dvi;
4035 if (!have_dvi || (have_pdf &&
4036 pdfname.lastModified() > dviname.lastModified())) {
4037 outname = pdfname.onlyFileName();
4038 command = lyxrc.forward_search_pdf;
4041 DocIterator cur = bv->cursor();
4042 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4043 LYXERR(Debug::ACTION, "Forward search: row:" << row
4045 if (row == -1 || command.empty()) {
4046 dr.setMessage(_("Couldn't proceed."));
4049 string texrow = convert<string>(row);
4051 command = subst(command, "$$n", texrow);
4052 command = subst(command, "$$f", fulltexname);
4053 command = subst(command, "$$t", texname);
4054 command = subst(command, "$$o", outname);
4056 PathChanger p(path);
4058 one.startscript(Systemcall::DontWait, command);
4062 case LFUN_SPELLING_CONTINUOUSLY:
4063 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4064 dr.screenUpdate(Update::Force | Update::FitCursor);
4068 // The LFUN must be for one of BufferView, Buffer or Cursor;
4070 dispatchToBufferView(cmd, dr);
4074 // Part of automatic menu appearance feature.
4075 if (isFullScreen()) {
4076 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4080 // Need to update bv because many LFUNs here might have destroyed it
4081 bv = currentBufferView();
4083 // Clear non-empty selections
4084 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4086 Cursor & cur = bv->cursor();
4087 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4088 cur.clearSelection();
4094 bool GuiView::lfunUiToggle(string const & ui_component)
4096 if (ui_component == "scrollbar") {
4097 // hide() is of no help
4098 if (d.current_work_area_->verticalScrollBarPolicy() ==
4099 Qt::ScrollBarAlwaysOff)
4101 d.current_work_area_->setVerticalScrollBarPolicy(
4102 Qt::ScrollBarAsNeeded);
4104 d.current_work_area_->setVerticalScrollBarPolicy(
4105 Qt::ScrollBarAlwaysOff);
4106 } else if (ui_component == "statusbar") {
4107 statusBar()->setVisible(!statusBar()->isVisible());
4108 } else if (ui_component == "menubar") {
4109 menuBar()->setVisible(!menuBar()->isVisible());
4111 if (ui_component == "frame") {
4113 getContentsMargins(&l, &t, &r, &b);
4114 //are the frames in default state?
4115 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4117 setContentsMargins(-2, -2, -2, -2);
4119 setContentsMargins(0, 0, 0, 0);
4122 if (ui_component == "fullscreen") {
4130 void GuiView::toggleFullScreen()
4132 if (isFullScreen()) {
4133 for (int i = 0; i != d.splitter_->count(); ++i)
4134 d.tabWorkArea(i)->setFullScreen(false);
4135 setContentsMargins(0, 0, 0, 0);
4136 setWindowState(windowState() ^ Qt::WindowFullScreen);
4139 statusBar()->show();
4142 hideDialogs("prefs", 0);
4143 for (int i = 0; i != d.splitter_->count(); ++i)
4144 d.tabWorkArea(i)->setFullScreen(true);
4145 setContentsMargins(-2, -2, -2, -2);
4147 setWindowState(windowState() ^ Qt::WindowFullScreen);
4148 if (lyxrc.full_screen_statusbar)
4149 statusBar()->hide();
4150 if (lyxrc.full_screen_menubar)
4152 if (lyxrc.full_screen_toolbars) {
4153 ToolbarMap::iterator end = d.toolbars_.end();
4154 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4159 // give dialogs like the TOC a chance to adapt
4164 Buffer const * GuiView::updateInset(Inset const * inset)
4169 Buffer const * inset_buffer = &(inset->buffer());
4171 for (int i = 0; i != d.splitter_->count(); ++i) {
4172 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4175 Buffer const * buffer = &(wa->bufferView().buffer());
4176 if (inset_buffer == buffer)
4177 wa->scheduleRedraw();
4179 return inset_buffer;
4183 void GuiView::restartCursor()
4185 /* When we move around, or type, it's nice to be able to see
4186 * the cursor immediately after the keypress.
4188 if (d.current_work_area_)
4189 d.current_work_area_->startBlinkingCursor();
4191 // Take this occasion to update the other GUI elements.
4197 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4199 if (d.current_work_area_)
4200 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4205 // This list should be kept in sync with the list of insets in
4206 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4207 // dialog should have the same name as the inset.
4208 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4209 // docs in LyXAction.cpp.
4211 char const * const dialognames[] = {
4213 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4214 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4215 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4216 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4217 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4218 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4219 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4220 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4222 char const * const * const end_dialognames =
4223 dialognames + (sizeof(dialognames) / sizeof(char *));
4227 cmpCStr(char const * name) : name_(name) {}
4228 bool operator()(char const * other) {
4229 return strcmp(other, name_) == 0;
4236 bool isValidName(string const & name)
4238 return find_if(dialognames, end_dialognames,
4239 cmpCStr(name.c_str())) != end_dialognames;
4245 void GuiView::resetDialogs()
4247 // Make sure that no LFUN uses any GuiView.
4248 guiApp->setCurrentView(0);
4252 constructToolbars();
4253 guiApp->menus().fillMenuBar(menuBar(), this, false);
4254 d.layout_->updateContents(true);
4255 // Now update controls with current buffer.
4256 guiApp->setCurrentView(this);
4262 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4264 if (!isValidName(name))
4267 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4269 if (it != d.dialogs_.end()) {
4271 it->second->hideView();
4272 return it->second.get();
4275 Dialog * dialog = build(name);
4276 d.dialogs_[name].reset(dialog);
4277 if (lyxrc.allow_geometry_session)
4278 dialog->restoreSession();
4285 void GuiView::showDialog(string const & name, string const & data,
4288 triggerShowDialog(toqstr(name), toqstr(data), inset);
4292 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4298 const string name = fromqstr(qname);
4299 const string data = fromqstr(qdata);
4303 Dialog * dialog = findOrBuild(name, false);
4305 bool const visible = dialog->isVisibleView();
4306 dialog->showData(data);
4307 if (inset && currentBufferView())
4308 currentBufferView()->editInset(name, inset);
4309 // We only set the focus to the new dialog if it was not yet
4310 // visible in order not to change the existing previous behaviour
4312 // activateWindow is needed for floating dockviews
4313 dialog->asQWidget()->raise();
4314 dialog->asQWidget()->activateWindow();
4315 dialog->asQWidget()->setFocus();
4319 catch (ExceptionMessage const & ex) {
4327 bool GuiView::isDialogVisible(string const & name) const
4329 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4330 if (it == d.dialogs_.end())
4332 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4336 void GuiView::hideDialog(string const & name, Inset * inset)
4338 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4339 if (it == d.dialogs_.end())
4343 if (!currentBufferView())
4345 if (inset != currentBufferView()->editedInset(name))
4349 Dialog * const dialog = it->second.get();
4350 if (dialog->isVisibleView())
4352 if (currentBufferView())
4353 currentBufferView()->editInset(name, 0);
4357 void GuiView::disconnectDialog(string const & name)
4359 if (!isValidName(name))
4361 if (currentBufferView())
4362 currentBufferView()->editInset(name, 0);
4366 void GuiView::hideAll() const
4368 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4369 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4371 for(; it != end; ++it)
4372 it->second->hideView();
4376 void GuiView::updateDialogs()
4378 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4379 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4381 for(; it != end; ++it) {
4382 Dialog * dialog = it->second.get();
4384 if (dialog->needBufferOpen() && !documentBufferView())
4385 hideDialog(fromqstr(dialog->name()), 0);
4386 else if (dialog->isVisibleView())
4387 dialog->checkStatus();
4394 Dialog * createDialog(GuiView & lv, string const & name);
4396 // will be replaced by a proper factory...
4397 Dialog * createGuiAbout(GuiView & lv);
4398 Dialog * createGuiBibtex(GuiView & lv);
4399 Dialog * createGuiChanges(GuiView & lv);
4400 Dialog * createGuiCharacter(GuiView & lv);
4401 Dialog * createGuiCitation(GuiView & lv);
4402 Dialog * createGuiCompare(GuiView & lv);
4403 Dialog * createGuiCompareHistory(GuiView & lv);
4404 Dialog * createGuiDelimiter(GuiView & lv);
4405 Dialog * createGuiDocument(GuiView & lv);
4406 Dialog * createGuiErrorList(GuiView & lv);
4407 Dialog * createGuiExternal(GuiView & lv);
4408 Dialog * createGuiGraphics(GuiView & lv);
4409 Dialog * createGuiInclude(GuiView & lv);
4410 Dialog * createGuiIndex(GuiView & lv);
4411 Dialog * createGuiListings(GuiView & lv);
4412 Dialog * createGuiLog(GuiView & lv);
4413 Dialog * createGuiMathMatrix(GuiView & lv);
4414 Dialog * createGuiNote(GuiView & lv);
4415 Dialog * createGuiParagraph(GuiView & lv);
4416 Dialog * createGuiPhantom(GuiView & lv);
4417 Dialog * createGuiPreferences(GuiView & lv);
4418 Dialog * createGuiPrint(GuiView & lv);
4419 Dialog * createGuiPrintindex(GuiView & lv);
4420 Dialog * createGuiRef(GuiView & lv);
4421 Dialog * createGuiSearch(GuiView & lv);
4422 Dialog * createGuiSearchAdv(GuiView & lv);
4423 Dialog * createGuiSendTo(GuiView & lv);
4424 Dialog * createGuiShowFile(GuiView & lv);
4425 Dialog * createGuiSpellchecker(GuiView & lv);
4426 Dialog * createGuiSymbols(GuiView & lv);
4427 Dialog * createGuiTabularCreate(GuiView & lv);
4428 Dialog * createGuiTexInfo(GuiView & lv);
4429 Dialog * createGuiToc(GuiView & lv);
4430 Dialog * createGuiThesaurus(GuiView & lv);
4431 Dialog * createGuiViewSource(GuiView & lv);
4432 Dialog * createGuiWrap(GuiView & lv);
4433 Dialog * createGuiProgressView(GuiView & lv);
4437 Dialog * GuiView::build(string const & name)
4439 LASSERT(isValidName(name), return 0);
4441 Dialog * dialog = createDialog(*this, name);
4445 if (name == "aboutlyx")
4446 return createGuiAbout(*this);
4447 if (name == "bibtex")
4448 return createGuiBibtex(*this);
4449 if (name == "changes")
4450 return createGuiChanges(*this);
4451 if (name == "character")
4452 return createGuiCharacter(*this);
4453 if (name == "citation")
4454 return createGuiCitation(*this);
4455 if (name == "compare")
4456 return createGuiCompare(*this);
4457 if (name == "comparehistory")
4458 return createGuiCompareHistory(*this);
4459 if (name == "document")
4460 return createGuiDocument(*this);
4461 if (name == "errorlist")
4462 return createGuiErrorList(*this);
4463 if (name == "external")
4464 return createGuiExternal(*this);
4466 return createGuiShowFile(*this);
4467 if (name == "findreplace")
4468 return createGuiSearch(*this);
4469 if (name == "findreplaceadv")
4470 return createGuiSearchAdv(*this);
4471 if (name == "graphics")
4472 return createGuiGraphics(*this);
4473 if (name == "include")
4474 return createGuiInclude(*this);
4475 if (name == "index")
4476 return createGuiIndex(*this);
4477 if (name == "index_print")
4478 return createGuiPrintindex(*this);
4479 if (name == "listings")
4480 return createGuiListings(*this);
4482 return createGuiLog(*this);
4483 if (name == "mathdelimiter")
4484 return createGuiDelimiter(*this);
4485 if (name == "mathmatrix")
4486 return createGuiMathMatrix(*this);
4488 return createGuiNote(*this);
4489 if (name == "paragraph")
4490 return createGuiParagraph(*this);
4491 if (name == "phantom")
4492 return createGuiPhantom(*this);
4493 if (name == "prefs")
4494 return createGuiPreferences(*this);
4496 return createGuiRef(*this);
4497 if (name == "sendto")
4498 return createGuiSendTo(*this);
4499 if (name == "spellchecker")
4500 return createGuiSpellchecker(*this);
4501 if (name == "symbols")
4502 return createGuiSymbols(*this);
4503 if (name == "tabularcreate")
4504 return createGuiTabularCreate(*this);
4505 if (name == "texinfo")
4506 return createGuiTexInfo(*this);
4507 if (name == "thesaurus")
4508 return createGuiThesaurus(*this);
4510 return createGuiToc(*this);
4511 if (name == "view-source")
4512 return createGuiViewSource(*this);
4514 return createGuiWrap(*this);
4515 if (name == "progress")
4516 return createGuiProgressView(*this);
4522 } // namespace frontend
4525 #include "moc_GuiView.cpp"