3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
8 * \author Abdelrazak Younes
11 * Full author contact details are available in file CREDITS.
18 #include "DispatchResult.h"
19 #include "FileDialog.h"
20 #include "FontLoader.h"
21 #include "GuiApplication.h"
22 #include "GuiCommandBuffer.h"
23 #include "GuiCompleter.h"
24 #include "GuiKeySymbol.h"
26 #include "GuiToolbar.h"
27 #include "GuiWorkArea.h"
28 #include "GuiProgress.h"
29 #include "LayoutBox.h"
33 #include "qt_helpers.h"
34 #include "support/filetools.h"
36 #include "frontends/alert.h"
37 #include "frontends/KeySymbol.h"
39 #include "buffer_funcs.h"
41 #include "BufferList.h"
42 #include "BufferParams.h"
43 #include "BufferView.h"
45 #include "Converter.h"
47 #include "CutAndPaste.h"
49 #include "ErrorList.h"
51 #include "FuncStatus.h"
52 #include "FuncRequest.h"
56 #include "LyXAction.h"
60 #include "Paragraph.h"
61 #include "SpellChecker.h"
64 #include "TextClass.h"
69 #include "support/convert.h"
70 #include "support/debug.h"
71 #include "support/ExceptionMessage.h"
72 #include "support/FileName.h"
73 #include "support/filetools.h"
74 #include "support/gettext.h"
75 #include "support/filetools.h"
76 #include "support/ForkedCalls.h"
77 #include "support/lassert.h"
78 #include "support/lstrings.h"
79 #include "support/os.h"
80 #include "support/Package.h"
81 #include "support/PathChanger.h"
82 #include "support/Systemcall.h"
83 #include "support/Timeout.h"
84 #include "support/ProgressInterface.h"
87 #include <QApplication>
88 #include <QCloseEvent>
90 #include <QDesktopWidget>
91 #include <QDragEnterEvent>
94 #include <QFutureWatcher>
103 #include <QPixmapCache>
105 #include <QPushButton>
106 #include <QScrollBar>
108 #include <QShowEvent>
110 #include <QStackedWidget>
111 #include <QStatusBar>
112 #include <QSvgRenderer>
113 #include <QtConcurrentRun>
121 // sync with GuiAlert.cpp
122 #define EXPORT_in_THREAD 1
125 #include "support/bind.h"
129 #ifdef HAVE_SYS_TIME_H
130 # include <sys/time.h>
138 using namespace lyx::support;
142 using support::addExtension;
143 using support::changeExtension;
144 using support::removeExtension;
150 class BackgroundWidget : public QWidget
153 BackgroundWidget(int width, int height)
154 : width_(width), height_(height)
156 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
157 if (!lyxrc.show_banner)
159 /// The text to be written on top of the pixmap
160 QString const text = lyx_version ?
161 qt_("version ") + lyx_version : qt_("unknown version");
162 #if QT_VERSION >= 0x050000
163 QString imagedir = "images/";
164 FileName fname = imageLibFileSearch(imagedir, "banner", "svgz");
165 QSvgRenderer svgRenderer(toqstr(fname.absFileName()));
166 if (svgRenderer.isValid()) {
167 splash_ = QPixmap(splashSize());
168 QPainter painter(&splash_);
169 svgRenderer.render(&painter);
170 splash_.setDevicePixelRatio(pixelRatio());
172 splash_ = getPixmap("images/", "banner", "png");
175 splash_ = getPixmap("images/", "banner", "svgz,png");
178 QPainter pain(&splash_);
179 pain.setPen(QColor(0, 0, 0));
180 qreal const fsize = fontSize();
181 QPointF const position = textPosition();
183 "widget pixel ratio: " << pixelRatio() <<
184 " splash pixel ratio: " << splashPixelRatio() <<
185 " version text size,position: " << fsize << "@" << position.x() << "+" << position.y());
187 // The font used to display the version info
188 font.setStyleHint(QFont::SansSerif);
189 font.setWeight(QFont::Bold);
190 font.setPointSizeF(fsize);
192 pain.drawText(position, text);
193 setFocusPolicy(Qt::StrongFocus);
196 void paintEvent(QPaintEvent *)
198 int const w = width_;
199 int const h = height_;
200 int const x = (width() - w) / 2;
201 int const y = (height() - h) / 2;
203 "widget pixel ratio: " << pixelRatio() <<
204 " splash pixel ratio: " << splashPixelRatio() <<
205 " paint pixmap: " << w << "x" << h << "@" << x << "+" << y);
207 pain.drawPixmap(x, y, w, h, splash_);
210 void keyPressEvent(QKeyEvent * ev)
213 setKeySymbol(&sym, ev);
215 guiApp->processKeySym(sym, q_key_state(ev->modifiers()));
227 /// Current ratio between physical pixels and device-independent pixels
228 double pixelRatio() const {
229 #if QT_VERSION >= 0x050000
230 return qt_scale_factor * devicePixelRatio();
236 qreal fontSize() const {
237 return toqstr(lyxrc.font_sizes[FONT_SIZE_NORMAL]).toDouble();
240 QPointF textPosition() const {
241 return QPointF(width_/2 - 18, height_/2 + 45);
244 QSize splashSize() const {
246 static_cast<unsigned int>(width_ * pixelRatio()),
247 static_cast<unsigned int>(height_ * pixelRatio()));
250 /// Ratio between physical pixels and device-independent pixels of splash image
251 double splashPixelRatio() const {
252 #if QT_VERSION >= 0x050000
253 return splash_.devicePixelRatio();
261 /// Toolbar store providing access to individual toolbars by name.
262 typedef map<string, GuiToolbar *> ToolbarMap;
264 typedef shared_ptr<Dialog> DialogPtr;
269 class GuiView::GuiViewPrivate
272 GuiViewPrivate(GuiViewPrivate const &);
273 void operator=(GuiViewPrivate const &);
275 GuiViewPrivate(GuiView * gv)
276 : gv_(gv), current_work_area_(0), current_main_work_area_(0),
277 layout_(0), autosave_timeout_(5000),
280 // hardcode here the platform specific icon size
281 smallIconSize = 16; // scaling problems
282 normalIconSize = 20; // ok, default if iconsize.png is missing
283 bigIconSize = 26; // better for some math icons
284 hugeIconSize = 32; // better for hires displays
287 // if it exists, use width of iconsize.png as normal size
288 QString const dir = toqstr(addPath("images", lyxrc.icon_set));
289 FileName const fn = lyx::libFileSearch(dir, "iconsize.png");
291 QImage image(toqstr(fn.absFileName()));
292 if (image.width() < int(smallIconSize))
293 normalIconSize = smallIconSize;
294 else if (image.width() > int(giantIconSize))
295 normalIconSize = giantIconSize;
297 normalIconSize = image.width();
300 splitter_ = new QSplitter;
301 bg_widget_ = new BackgroundWidget(400, 250);
302 stack_widget_ = new QStackedWidget;
303 stack_widget_->addWidget(bg_widget_);
304 stack_widget_->addWidget(splitter_);
307 // TODO cleanup, remove the singleton, handle multiple Windows?
308 progress_ = ProgressInterface::instance();
309 if (!dynamic_cast<GuiProgress*>(progress_)) {
310 progress_ = new GuiProgress; // TODO who deletes it
311 ProgressInterface::setInstance(progress_);
314 dynamic_cast<GuiProgress*>(progress_),
315 SIGNAL(updateStatusBarMessage(QString const&)),
316 gv, SLOT(updateStatusBarMessage(QString const&)));
318 dynamic_cast<GuiProgress*>(progress_),
319 SIGNAL(clearMessageText()),
320 gv, SLOT(clearMessageText()));
327 delete stack_widget_;
332 stack_widget_->setCurrentWidget(bg_widget_);
333 bg_widget_->setUpdatesEnabled(true);
334 bg_widget_->setFocus();
337 int tabWorkAreaCount()
339 return splitter_->count();
342 TabWorkArea * tabWorkArea(int i)
344 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
347 TabWorkArea * currentTabWorkArea()
349 int areas = tabWorkAreaCount();
351 // The first TabWorkArea is always the first one, if any.
352 return tabWorkArea(0);
354 for (int i = 0; i != areas; ++i) {
355 TabWorkArea * twa = tabWorkArea(i);
356 if (current_main_work_area_ == twa->currentWorkArea())
360 // None has the focus so we just take the first one.
361 return tabWorkArea(0);
364 int countWorkAreasOf(Buffer & buf)
366 int areas = tabWorkAreaCount();
368 for (int i = 0; i != areas; ++i) {
369 TabWorkArea * twa = tabWorkArea(i);
370 if (twa->workArea(buf))
376 void setPreviewFuture(QFuture<Buffer::ExportStatus> const & f)
378 if (processing_thread_watcher_.isRunning()) {
379 // we prefer to cancel this preview in order to keep a snappy
383 processing_thread_watcher_.setFuture(f);
386 QSize iconSize(docstring const & icon_size)
389 if (icon_size == "small")
390 size = smallIconSize;
391 else if (icon_size == "normal")
392 size = normalIconSize;
393 else if (icon_size == "big")
395 else if (icon_size == "huge")
397 else if (icon_size == "giant")
398 size = giantIconSize;
400 size = icon_size.empty() ? normalIconSize : convert<int>(icon_size);
402 if (size < smallIconSize)
403 size = smallIconSize;
405 return QSize(size, size);
408 QSize iconSize(QString const & icon_size)
410 return iconSize(qstring_to_ucs4(icon_size));
413 string & iconSize(QSize const & qsize)
415 LATTEST(qsize.width() == qsize.height());
417 static string icon_size;
419 unsigned int size = qsize.width();
421 if (size < smallIconSize)
422 size = smallIconSize;
424 if (size == smallIconSize)
426 else if (size == normalIconSize)
427 icon_size = "normal";
428 else if (size == bigIconSize)
430 else if (size == hugeIconSize)
432 else if (size == giantIconSize)
435 icon_size = convert<string>(size);
442 GuiWorkArea * current_work_area_;
443 GuiWorkArea * current_main_work_area_;
444 QSplitter * splitter_;
445 QStackedWidget * stack_widget_;
446 BackgroundWidget * bg_widget_;
448 ToolbarMap toolbars_;
449 ProgressInterface* progress_;
450 /// The main layout box.
452 * \warning Don't Delete! The layout box is actually owned by
453 * whichever toolbar contains it. All the GuiView class needs is a
454 * means of accessing it.
456 * FIXME: replace that with a proper model so that we are not limited
457 * to only one dialog.
462 map<string, DialogPtr> dialogs_;
464 unsigned int smallIconSize;
465 unsigned int normalIconSize;
466 unsigned int bigIconSize;
467 unsigned int hugeIconSize;
468 unsigned int giantIconSize;
470 QTimer statusbar_timer_;
471 /// auto-saving of buffers
472 Timeout autosave_timeout_;
473 /// flag against a race condition due to multiclicks, see bug #1119
477 TocModels toc_models_;
480 QFutureWatcher<docstring> autosave_watcher_;
481 QFutureWatcher<Buffer::ExportStatus> processing_thread_watcher_;
483 string last_export_format;
484 string processing_format;
486 static QSet<Buffer const *> busyBuffers;
487 static Buffer::ExportStatus previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
488 static Buffer::ExportStatus exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
489 static Buffer::ExportStatus compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
490 static docstring autosaveAndDestroy(Buffer const * orig, Buffer * buffer);
493 static Buffer::ExportStatus runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format);
495 // TODO syncFunc/previewFunc: use bind
496 bool asyncBufferProcessing(string const & argument,
497 Buffer const * used_buffer,
498 docstring const & msg,
499 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
500 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
501 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const);
503 QVector<GuiWorkArea*> guiWorkAreas();
506 QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
509 GuiView::GuiView(int id)
510 : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0),
511 command_execute_(false), minibuffer_focus_(false)
513 connect(this, SIGNAL(bufferViewChanged()),
514 this, SLOT(onBufferViewChanged()));
516 // GuiToolbars *must* be initialised before the menu bar.
517 setIconSize(QSize(d.normalIconSize, d.normalIconSize)); // 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 // set custom application bars context menu, e.g. tool bar and menu bar
610 setContextMenuPolicy(Qt::CustomContextMenu);
611 connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
612 SLOT(toolBarPopup(const QPoint &)));
614 // Forbid too small unresizable window because it can happen
615 // with some window manager under X11.
616 setMinimumSize(300, 200);
618 if (lyxrc.allow_geometry_session) {
619 // Now take care of session management.
624 // no session handling, default to a sane size.
625 setGeometry(50, 50, 690, 510);
628 // clear session data if any.
630 settings.remove("views");
640 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
642 QVector<GuiWorkArea*> areas;
643 for (int i = 0; i < tabWorkAreaCount(); i++) {
644 TabWorkArea* ta = tabWorkArea(i);
645 for (int u = 0; u < ta->count(); u++) {
646 areas << ta->workArea(u);
652 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
653 string const & format)
655 docstring const fmt = theFormats().prettyName(format);
658 case Buffer::ExportSuccess:
659 msg = bformat(_("Successful export to format: %1$s"), fmt);
661 case Buffer::ExportCancel:
662 msg = _("Document export cancelled.");
664 case Buffer::ExportError:
665 case Buffer::ExportNoPathToFormat:
666 case Buffer::ExportTexPathHasSpaces:
667 case Buffer::ExportConverterError:
668 msg = bformat(_("Error while exporting format: %1$s"), fmt);
670 case Buffer::PreviewSuccess:
671 msg = bformat(_("Successful preview of format: %1$s"), fmt);
673 case Buffer::PreviewError:
674 msg = bformat(_("Error while previewing format: %1$s"), fmt);
681 void GuiView::processingThreadStarted()
686 void GuiView::processingThreadFinished()
688 QFutureWatcher<Buffer::ExportStatus> const * watcher =
689 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
691 Buffer::ExportStatus const status = watcher->result();
692 handleExportStatus(this, status, d.processing_format);
695 BufferView const * const bv = currentBufferView();
696 if (bv && !bv->buffer().errorList("Export").empty()) {
700 errors(d.last_export_format);
704 void GuiView::autoSaveThreadFinished()
706 QFutureWatcher<docstring> const * watcher =
707 static_cast<QFutureWatcher<docstring> const *>(sender());
708 message(watcher->result());
713 void GuiView::saveLayout() const
716 settings.setValue("zoom", lyxrc.currentZoom);
717 settings.beginGroup("views");
718 settings.beginGroup(QString::number(id_));
719 #if defined(Q_WS_X11) || defined(QPA_XCB)
720 settings.setValue("pos", pos());
721 settings.setValue("size", size());
723 settings.setValue("geometry", saveGeometry());
725 settings.setValue("layout", saveState(0));
726 settings.setValue("icon_size", toqstr(d.iconSize(iconSize())));
730 void GuiView::saveUISettings() const
732 // Save the toolbar private states
733 ToolbarMap::iterator end = d.toolbars_.end();
734 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
735 it->second->saveSession();
736 // Now take care of all other dialogs
737 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
738 for (; it!= d.dialogs_.end(); ++it)
739 it->second->saveSession();
743 bool GuiView::restoreLayout()
746 lyxrc.currentZoom = settings.value("zoom", lyxrc.zoom).toInt();
747 lyx::dispatch(FuncRequest(LFUN_BUFFER_ZOOM, convert<docstring>(lyxrc.currentZoom)));
748 settings.beginGroup("views");
749 settings.beginGroup(QString::number(id_));
750 QString const icon_key = "icon_size";
751 if (!settings.contains(icon_key))
754 //code below is skipped when when ~/.config/LyX is (re)created
755 setIconSize(d.iconSize(settings.value(icon_key).toString()));
757 #if defined(Q_WS_X11) || defined(QPA_XCB)
758 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
759 QSize size = settings.value("size", QSize(690, 510)).toSize();
763 // Work-around for bug #6034: the window ends up in an undetermined
764 // state when trying to restore a maximized window when it is
765 // already maximized.
766 if (!(windowState() & Qt::WindowMaximized))
767 if (!restoreGeometry(settings.value("geometry").toByteArray()))
768 setGeometry(50, 50, 690, 510);
770 // Make sure layout is correctly oriented.
771 setLayoutDirection(qApp->layoutDirection());
773 // Allow the toc and view-source dock widget to be restored if needed.
775 if ((dialog = findOrBuild("toc", true)))
776 // see bug 5082. At least setup title and enabled state.
777 // Visibility will be adjusted by restoreState below.
778 dialog->prepareView();
779 if ((dialog = findOrBuild("view-source", true)))
780 dialog->prepareView();
781 if ((dialog = findOrBuild("progress", true)))
782 dialog->prepareView();
784 if (!restoreState(settings.value("layout").toByteArray(), 0))
787 // init the toolbars that have not been restored
788 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
789 Toolbars::Infos::iterator end = guiApp->toolbars().end();
790 for (; cit != end; ++cit) {
791 GuiToolbar * tb = toolbar(cit->name);
792 if (tb && !tb->isRestored())
793 initToolbar(cit->name);
801 GuiToolbar * GuiView::toolbar(string const & name)
803 ToolbarMap::iterator it = d.toolbars_.find(name);
804 if (it != d.toolbars_.end())
807 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
812 void GuiView::constructToolbars()
814 ToolbarMap::iterator it = d.toolbars_.begin();
815 for (; it != d.toolbars_.end(); ++it)
819 // I don't like doing this here, but the standard toolbar
820 // destroys this object when it's destroyed itself (vfr)
821 d.layout_ = new LayoutBox(*this);
822 d.stack_widget_->addWidget(d.layout_);
823 d.layout_->move(0,0);
825 // extracts the toolbars from the backend
826 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
827 Toolbars::Infos::iterator end = guiApp->toolbars().end();
828 for (; cit != end; ++cit)
829 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
833 void GuiView::initToolbars()
835 // extracts the toolbars from the backend
836 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
837 Toolbars::Infos::iterator end = guiApp->toolbars().end();
838 for (; cit != end; ++cit)
839 initToolbar(cit->name);
843 void GuiView::initToolbar(string const & name)
845 GuiToolbar * tb = toolbar(name);
848 int const visibility = guiApp->toolbars().defaultVisibility(name);
849 bool newline = !(visibility & Toolbars::SAMEROW);
850 tb->setVisible(false);
851 tb->setVisibility(visibility);
853 if (visibility & Toolbars::TOP) {
855 addToolBarBreak(Qt::TopToolBarArea);
856 addToolBar(Qt::TopToolBarArea, tb);
859 if (visibility & Toolbars::BOTTOM) {
861 addToolBarBreak(Qt::BottomToolBarArea);
862 addToolBar(Qt::BottomToolBarArea, tb);
865 if (visibility & Toolbars::LEFT) {
867 addToolBarBreak(Qt::LeftToolBarArea);
868 addToolBar(Qt::LeftToolBarArea, tb);
871 if (visibility & Toolbars::RIGHT) {
873 addToolBarBreak(Qt::RightToolBarArea);
874 addToolBar(Qt::RightToolBarArea, tb);
877 if (visibility & Toolbars::ON)
878 tb->setVisible(true);
882 TocModels & GuiView::tocModels()
884 return d.toc_models_;
888 void GuiView::setFocus()
890 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
891 QMainWindow::setFocus();
895 bool GuiView::hasFocus() const
897 if (currentWorkArea())
898 return currentWorkArea()->hasFocus();
899 if (currentMainWorkArea())
900 return currentMainWorkArea()->hasFocus();
901 return d.bg_widget_->hasFocus();
905 void GuiView::focusInEvent(QFocusEvent * e)
907 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
908 QMainWindow::focusInEvent(e);
909 // Make sure guiApp points to the correct view.
910 guiApp->setCurrentView(this);
911 if (currentWorkArea())
912 currentWorkArea()->setFocus();
913 else if (currentMainWorkArea())
914 currentMainWorkArea()->setFocus();
916 d.bg_widget_->setFocus();
920 void GuiView::showEvent(QShowEvent * e)
922 LYXERR(Debug::GUI, "Passed Geometry "
923 << size().height() << "x" << size().width()
924 << "+" << pos().x() << "+" << pos().y());
926 if (d.splitter_->count() == 0)
927 // No work area, switch to the background widget.
931 QMainWindow::showEvent(e);
935 bool GuiView::closeScheduled()
942 bool GuiView::prepareAllBuffersForLogout()
944 Buffer * first = theBufferList().first();
948 // First, iterate over all buffers and ask the users if unsaved
949 // changes should be saved.
950 // We cannot use a for loop as the buffer list cycles.
953 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
955 b = theBufferList().next(b);
956 } while (b != first);
958 // Next, save session state
959 // When a view/window was closed before without quitting LyX, there
960 // are already entries in the lastOpened list.
961 theSession().lastOpened().clear();
968 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
969 ** is responsibility of the container (e.g., dialog)
971 void GuiView::closeEvent(QCloseEvent * close_event)
973 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
975 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
976 Alert::warning(_("Exit LyX"),
977 _("LyX could not be closed because documents are being processed by LyX."));
978 close_event->setAccepted(false);
982 // If the user pressed the x (so we didn't call closeView
983 // programmatically), we want to clear all existing entries.
985 theSession().lastOpened().clear();
990 // it can happen that this event arrives without selecting the view,
991 // e.g. when clicking the close button on a background window.
993 if (!closeWorkAreaAll()) {
995 close_event->ignore();
999 // Make sure that nothing will use this to be closed View.
1000 guiApp->unregisterView(this);
1002 if (isFullScreen()) {
1003 // Switch off fullscreen before closing.
1008 // Make sure the timer time out will not trigger a statusbar update.
1009 d.statusbar_timer_.stop();
1011 // Saving fullscreen requires additional tweaks in the toolbar code.
1012 // It wouldn't also work under linux natively.
1013 if (lyxrc.allow_geometry_session) {
1018 close_event->accept();
1022 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1024 if (event->mimeData()->hasUrls())
1026 /// \todo Ask lyx-devel is this is enough:
1027 /// if (event->mimeData()->hasFormat("text/plain"))
1028 /// event->acceptProposedAction();
1032 void GuiView::dropEvent(QDropEvent * event)
1034 QList<QUrl> files = event->mimeData()->urls();
1035 if (files.isEmpty())
1038 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1039 for (int i = 0; i != files.size(); ++i) {
1040 string const file = os::internal_path(fromqstr(
1041 files.at(i).toLocalFile()));
1045 string const ext = support::getExtension(file);
1046 vector<const Format *> found_formats;
1048 // Find all formats that have the correct extension.
1049 vector<const Format *> const & import_formats
1050 = theConverters().importableFormats();
1051 vector<const Format *>::const_iterator it = import_formats.begin();
1052 for (; it != import_formats.end(); ++it)
1053 if ((*it)->hasExtension(ext))
1054 found_formats.push_back(*it);
1057 if (found_formats.size() >= 1) {
1058 if (found_formats.size() > 1) {
1059 //FIXME: show a dialog to choose the correct importable format
1060 LYXERR(Debug::FILES,
1061 "Multiple importable formats found, selecting first");
1063 string const arg = found_formats[0]->name() + " " + file;
1064 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1067 //FIXME: do we have to explicitly check whether it's a lyx file?
1068 LYXERR(Debug::FILES,
1069 "No formats found, trying to open it as a lyx file");
1070 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1072 // add the functions to the queue
1073 guiApp->addToFuncRequestQueue(cmd);
1076 // now process the collected functions. We perform the events
1077 // asynchronously. This prevents potential problems in case the
1078 // BufferView is closed within an event.
1079 guiApp->processFuncRequestQueueAsync();
1083 void GuiView::message(docstring const & str)
1085 if (ForkedProcess::iAmAChild())
1088 // call is moved to GUI-thread by GuiProgress
1089 d.progress_->appendMessage(toqstr(str));
1093 void GuiView::clearMessageText()
1095 message(docstring());
1099 void GuiView::updateStatusBarMessage(QString const & str)
1101 statusBar()->showMessage(str);
1102 d.statusbar_timer_.stop();
1103 d.statusbar_timer_.start(3000);
1107 void GuiView::clearMessage()
1109 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1110 // the hasFocus function mostly returns false, even if the focus is on
1111 // a workarea in this view.
1115 d.statusbar_timer_.stop();
1119 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1121 if (wa != d.current_work_area_
1122 || wa->bufferView().buffer().isInternal())
1124 Buffer const & buf = wa->bufferView().buffer();
1125 // Set the windows title
1126 docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
1127 if (buf.notifiesExternalModification()) {
1128 title = bformat(_("%1$s (modified externally)"), title);
1129 // If the external modification status has changed, then maybe the status of
1130 // buffer-save has changed too.
1134 title += from_ascii(" - LyX");
1136 setWindowTitle(toqstr(title));
1137 // Sets the path for the window: this is used by OSX to
1138 // allow a context click on the title bar showing a menu
1139 // with the path up to the file
1140 setWindowFilePath(toqstr(buf.absFileName()));
1141 // Tell Qt whether the current document is changed
1142 setWindowModified(!buf.isClean());
1144 if (buf.hasReadonlyFlag())
1149 if (buf.lyxvc().inUse()) {
1150 version_control_->show();
1151 version_control_->setText(toqstr(buf.lyxvc().vcstatus()));
1153 version_control_->hide();
1157 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1159 if (d.current_work_area_)
1160 // disconnect the current work area from all slots
1161 QObject::disconnect(d.current_work_area_, 0, this, 0);
1163 disconnectBufferView();
1164 connectBufferView(wa->bufferView());
1165 connectBuffer(wa->bufferView().buffer());
1166 d.current_work_area_ = wa;
1167 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1168 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1169 QObject::connect(wa, SIGNAL(busy(bool)),
1170 this, SLOT(setBusy(bool)));
1171 // connection of a signal to a signal
1172 QObject::connect(wa, SIGNAL(bufferViewChanged()),
1173 this, SIGNAL(bufferViewChanged()));
1174 Q_EMIT updateWindowTitle(wa);
1175 Q_EMIT bufferViewChanged();
1179 void GuiView::onBufferViewChanged()
1182 // Buffer-dependent dialogs must be updated. This is done here because
1183 // some dialogs require buffer()->text.
1188 void GuiView::on_lastWorkAreaRemoved()
1191 // We already are in a close event. Nothing more to do.
1194 if (d.splitter_->count() > 1)
1195 // We have a splitter so don't close anything.
1198 // Reset and updates the dialogs.
1199 Q_EMIT bufferViewChanged();
1204 if (lyxrc.open_buffers_in_tabs)
1205 // Nothing more to do, the window should stay open.
1208 if (guiApp->viewIds().size() > 1) {
1214 // On Mac we also close the last window because the application stay
1215 // resident in memory. On other platforms we don't close the last
1216 // window because this would quit the application.
1222 void GuiView::updateStatusBar()
1224 // let the user see the explicit message
1225 if (d.statusbar_timer_.isActive())
1232 void GuiView::showMessage()
1236 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1237 if (msg.isEmpty()) {
1238 BufferView const * bv = currentBufferView();
1240 msg = toqstr(bv->cursor().currentState());
1242 msg = qt_("Welcome to LyX!");
1244 statusBar()->showMessage(msg);
1248 bool GuiView::event(QEvent * e)
1252 // Useful debug code:
1253 //case QEvent::ActivationChange:
1254 //case QEvent::WindowDeactivate:
1255 //case QEvent::Paint:
1256 //case QEvent::Enter:
1257 //case QEvent::Leave:
1258 //case QEvent::HoverEnter:
1259 //case QEvent::HoverLeave:
1260 //case QEvent::HoverMove:
1261 //case QEvent::StatusTip:
1262 //case QEvent::DragEnter:
1263 //case QEvent::DragLeave:
1264 //case QEvent::Drop:
1267 case QEvent::WindowActivate: {
1268 GuiView * old_view = guiApp->currentView();
1269 if (this == old_view) {
1271 return QMainWindow::event(e);
1273 if (old_view && old_view->currentBufferView()) {
1274 // save current selection to the selection buffer to allow
1275 // middle-button paste in this window.
1276 cap::saveSelection(old_view->currentBufferView()->cursor());
1278 guiApp->setCurrentView(this);
1279 if (d.current_work_area_)
1280 on_currentWorkAreaChanged(d.current_work_area_);
1284 return QMainWindow::event(e);
1287 case QEvent::ShortcutOverride: {
1289 if (isFullScreen() && menuBar()->isHidden()) {
1290 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1291 // FIXME: we should also try to detect special LyX shortcut such as
1292 // Alt-P and Alt-M. Right now there is a hack in
1293 // GuiWorkArea::processKeySym() that hides again the menubar for
1295 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1297 return QMainWindow::event(e);
1300 return QMainWindow::event(e);
1304 return QMainWindow::event(e);
1308 void GuiView::resetWindowTitle()
1310 setWindowTitle(qt_("LyX"));
1313 bool GuiView::focusNextPrevChild(bool /*next*/)
1320 bool GuiView::busy() const
1326 void GuiView::setBusy(bool busy)
1328 bool const busy_before = busy_ > 0;
1329 busy ? ++busy_ : --busy_;
1330 if ((busy_ > 0) == busy_before)
1331 // busy state didn't change
1335 QApplication::setOverrideCursor(Qt::WaitCursor);
1338 QApplication::restoreOverrideCursor();
1343 void GuiView::resetCommandExecute()
1345 command_execute_ = false;
1350 double GuiView::pixelRatio() const
1352 #if QT_VERSION >= 0x050000
1353 return qt_scale_factor * devicePixelRatio();
1360 GuiWorkArea * GuiView::workArea(int index)
1362 if (TabWorkArea * twa = d.currentTabWorkArea())
1363 if (index < twa->count())
1364 return twa->workArea(index);
1369 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1371 if (currentWorkArea()
1372 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1373 return currentWorkArea();
1374 if (TabWorkArea * twa = d.currentTabWorkArea())
1375 return twa->workArea(buffer);
1380 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1382 // Automatically create a TabWorkArea if there are none yet.
1383 TabWorkArea * tab_widget = d.splitter_->count()
1384 ? d.currentTabWorkArea() : addTabWorkArea();
1385 return tab_widget->addWorkArea(buffer, *this);
1389 TabWorkArea * GuiView::addTabWorkArea()
1391 TabWorkArea * twa = new TabWorkArea;
1392 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1393 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1394 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1395 this, SLOT(on_lastWorkAreaRemoved()));
1397 d.splitter_->addWidget(twa);
1398 d.stack_widget_->setCurrentWidget(d.splitter_);
1403 GuiWorkArea const * GuiView::currentWorkArea() const
1405 return d.current_work_area_;
1409 GuiWorkArea * GuiView::currentWorkArea()
1411 return d.current_work_area_;
1415 GuiWorkArea const * GuiView::currentMainWorkArea() const
1417 if (!d.currentTabWorkArea())
1419 return d.currentTabWorkArea()->currentWorkArea();
1423 GuiWorkArea * GuiView::currentMainWorkArea()
1425 if (!d.currentTabWorkArea())
1427 return d.currentTabWorkArea()->currentWorkArea();
1431 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1433 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1435 d.current_work_area_ = 0;
1437 Q_EMIT bufferViewChanged();
1441 // FIXME: I've no clue why this is here and why it accesses
1442 // theGuiApp()->currentView, which might be 0 (bug 6464).
1443 // See also 27525 (vfr).
1444 if (theGuiApp()->currentView() == this
1445 && theGuiApp()->currentView()->currentWorkArea() == wa)
1448 if (currentBufferView())
1449 cap::saveSelection(currentBufferView()->cursor());
1451 theGuiApp()->setCurrentView(this);
1452 d.current_work_area_ = wa;
1454 // We need to reset this now, because it will need to be
1455 // right if the tabWorkArea gets reset in the for loop. We
1456 // will change it back if we aren't in that case.
1457 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1458 d.current_main_work_area_ = wa;
1460 for (int i = 0; i != d.splitter_->count(); ++i) {
1461 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1462 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1463 << ", Current main wa: " << currentMainWorkArea());
1468 d.current_main_work_area_ = old_cmwa;
1470 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1471 on_currentWorkAreaChanged(wa);
1472 BufferView & bv = wa->bufferView();
1473 bv.cursor().fixIfBroken();
1475 wa->setUpdatesEnabled(true);
1476 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1480 void GuiView::removeWorkArea(GuiWorkArea * wa)
1482 LASSERT(wa, return);
1483 if (wa == d.current_work_area_) {
1485 disconnectBufferView();
1486 d.current_work_area_ = 0;
1487 d.current_main_work_area_ = 0;
1490 bool found_twa = false;
1491 for (int i = 0; i != d.splitter_->count(); ++i) {
1492 TabWorkArea * twa = d.tabWorkArea(i);
1493 if (twa->removeWorkArea(wa)) {
1494 // Found in this tab group, and deleted the GuiWorkArea.
1496 if (twa->count() != 0) {
1497 if (d.current_work_area_ == 0)
1498 // This means that we are closing the current GuiWorkArea, so
1499 // switch to the next GuiWorkArea in the found TabWorkArea.
1500 setCurrentWorkArea(twa->currentWorkArea());
1502 // No more WorkAreas in this tab group, so delete it.
1509 // It is not a tabbed work area (i.e., the search work area), so it
1510 // should be deleted by other means.
1511 LASSERT(found_twa, return);
1513 if (d.current_work_area_ == 0) {
1514 if (d.splitter_->count() != 0) {
1515 TabWorkArea * twa = d.currentTabWorkArea();
1516 setCurrentWorkArea(twa->currentWorkArea());
1518 // No more work areas, switch to the background widget.
1519 setCurrentWorkArea(0);
1525 LayoutBox * GuiView::getLayoutDialog() const
1531 void GuiView::updateLayoutList()
1534 d.layout_->updateContents(false);
1538 void GuiView::updateToolbars()
1540 ToolbarMap::iterator end = d.toolbars_.end();
1541 if (d.current_work_area_) {
1543 if (d.current_work_area_->bufferView().cursor().inMathed()
1544 && !d.current_work_area_->bufferView().cursor().inRegexped())
1545 context |= Toolbars::MATH;
1546 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1547 context |= Toolbars::TABLE;
1548 if (currentBufferView()->buffer().areChangesPresent()
1549 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1550 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1551 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1552 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1553 context |= Toolbars::REVIEW;
1554 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1555 context |= Toolbars::MATHMACROTEMPLATE;
1556 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1557 context |= Toolbars::IPA;
1558 if (command_execute_)
1559 context |= Toolbars::MINIBUFFER;
1560 if (minibuffer_focus_) {
1561 context |= Toolbars::MINIBUFFER_FOCUS;
1562 minibuffer_focus_ = false;
1565 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1566 it->second->update(context);
1568 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1569 it->second->update();
1573 void GuiView::setBuffer(Buffer * newBuffer)
1575 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1576 LASSERT(newBuffer, return);
1578 GuiWorkArea * wa = workArea(*newBuffer);
1581 newBuffer->masterBuffer()->updateBuffer();
1583 wa = addWorkArea(*newBuffer);
1584 // scroll to the position when the BufferView was last closed
1585 if (lyxrc.use_lastfilepos) {
1586 LastFilePosSection::FilePos filepos =
1587 theSession().lastFilePos().load(newBuffer->fileName());
1588 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1591 //Disconnect the old buffer...there's no new one.
1594 connectBuffer(*newBuffer);
1595 connectBufferView(wa->bufferView());
1596 setCurrentWorkArea(wa);
1600 void GuiView::connectBuffer(Buffer & buf)
1602 buf.setGuiDelegate(this);
1606 void GuiView::disconnectBuffer()
1608 if (d.current_work_area_)
1609 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1613 void GuiView::connectBufferView(BufferView & bv)
1615 bv.setGuiDelegate(this);
1619 void GuiView::disconnectBufferView()
1621 if (d.current_work_area_)
1622 d.current_work_area_->bufferView().setGuiDelegate(0);
1626 void GuiView::errors(string const & error_type, bool from_master)
1628 BufferView const * const bv = currentBufferView();
1632 #if EXPORT_in_THREAD
1633 // We are called with from_master == false by default, so we
1634 // have to figure out whether that is the case or not.
1635 ErrorList & el = bv->buffer().errorList(error_type);
1637 el = bv->buffer().masterBuffer()->errorList(error_type);
1641 ErrorList const & el = from_master ?
1642 bv->buffer().masterBuffer()->errorList(error_type) :
1643 bv->buffer().errorList(error_type);
1649 string data = error_type;
1651 data = "from_master|" + error_type;
1652 showDialog("errorlist", data);
1656 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1658 d.toc_models_.updateItem(toqstr(type), dit);
1662 void GuiView::structureChanged()
1664 // This is called from the Buffer, which has no way to ensure that cursors
1665 // in BufferView remain valid.
1666 if (documentBufferView())
1667 documentBufferView()->cursor().sanitize();
1668 // FIXME: This is slightly expensive, though less than the tocBackend update
1669 // (#9880). This also resets the view in the Toc Widget (#6675).
1670 d.toc_models_.reset(documentBufferView());
1671 // Navigator needs more than a simple update in this case. It needs to be
1673 updateDialog("toc", "");
1677 void GuiView::updateDialog(string const & name, string const & data)
1679 if (!isDialogVisible(name))
1682 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1683 if (it == d.dialogs_.end())
1686 Dialog * const dialog = it->second.get();
1687 if (dialog->isVisibleView())
1688 dialog->initialiseParams(data);
1692 BufferView * GuiView::documentBufferView()
1694 return currentMainWorkArea()
1695 ? ¤tMainWorkArea()->bufferView()
1700 BufferView const * GuiView::documentBufferView() const
1702 return currentMainWorkArea()
1703 ? ¤tMainWorkArea()->bufferView()
1708 BufferView * GuiView::currentBufferView()
1710 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1714 BufferView const * GuiView::currentBufferView() const
1716 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1720 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1721 Buffer const * orig, Buffer * clone)
1723 bool const success = clone->autoSave();
1725 busyBuffers.remove(orig);
1727 ? _("Automatic save done.")
1728 : _("Automatic save failed!");
1732 void GuiView::autoSave()
1734 LYXERR(Debug::INFO, "Running autoSave()");
1736 Buffer * buffer = documentBufferView()
1737 ? &documentBufferView()->buffer() : 0;
1739 resetAutosaveTimers();
1743 GuiViewPrivate::busyBuffers.insert(buffer);
1744 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1745 buffer, buffer->cloneBufferOnly());
1746 d.autosave_watcher_.setFuture(f);
1747 resetAutosaveTimers();
1751 void GuiView::resetAutosaveTimers()
1754 d.autosave_timeout_.restart();
1758 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1761 Buffer * buf = currentBufferView()
1762 ? ¤tBufferView()->buffer() : 0;
1763 Buffer * doc_buffer = documentBufferView()
1764 ? &(documentBufferView()->buffer()) : 0;
1767 /* In LyX/Mac, when a dialog is open, the menus of the
1768 application can still be accessed without giving focus to
1769 the main window. In this case, we want to disable the menu
1770 entries that are buffer-related.
1771 This code must not be used on Linux and Windows, since it
1772 would disable buffer-related entries when hovering over the
1773 menu (see bug #9574).
1775 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1781 // Check whether we need a buffer
1782 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1783 // no, exit directly
1784 flag.message(from_utf8(N_("Command not allowed with"
1785 "out any document open")));
1786 flag.setEnabled(false);
1790 if (cmd.origin() == FuncRequest::TOC) {
1791 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1792 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1793 flag.setEnabled(false);
1797 switch(cmd.action()) {
1798 case LFUN_BUFFER_IMPORT:
1801 case LFUN_MASTER_BUFFER_UPDATE:
1802 case LFUN_MASTER_BUFFER_VIEW:
1804 && (doc_buffer->parent() != 0
1805 || doc_buffer->hasChildren())
1806 && !d.processing_thread_watcher_.isRunning();
1809 case LFUN_BUFFER_UPDATE:
1810 case LFUN_BUFFER_VIEW: {
1811 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1815 string format = to_utf8(cmd.argument());
1816 if (cmd.argument().empty())
1817 format = doc_buffer->params().getDefaultOutputFormat();
1818 enable = doc_buffer->params().isExportable(format, true);
1822 case LFUN_BUFFER_RELOAD:
1823 enable = doc_buffer && !doc_buffer->isUnnamed()
1824 && doc_buffer->fileName().exists() && !doc_buffer->isClean();
1827 case LFUN_BUFFER_CHILD_OPEN:
1828 enable = doc_buffer != 0;
1831 case LFUN_BUFFER_WRITE:
1832 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1835 //FIXME: This LFUN should be moved to GuiApplication.
1836 case LFUN_BUFFER_WRITE_ALL: {
1837 // We enable the command only if there are some modified buffers
1838 Buffer * first = theBufferList().first();
1843 // We cannot use a for loop as the buffer list is a cycle.
1845 if (!b->isClean()) {
1849 b = theBufferList().next(b);
1850 } while (b != first);
1854 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
1855 enable = doc_buffer && doc_buffer->notifiesExternalModification();
1858 case LFUN_BUFFER_WRITE_AS:
1859 case LFUN_BUFFER_EXPORT_AS:
1860 enable = doc_buffer != 0;
1863 case LFUN_BUFFER_CLOSE:
1864 case LFUN_VIEW_CLOSE:
1865 enable = doc_buffer != 0;
1868 case LFUN_BUFFER_CLOSE_ALL:
1869 enable = theBufferList().last() != theBufferList().first();
1872 case LFUN_VIEW_SPLIT:
1873 if (cmd.getArg(0) == "vertical")
1874 enable = doc_buffer && (d.splitter_->count() == 1 ||
1875 d.splitter_->orientation() == Qt::Vertical);
1877 enable = doc_buffer && (d.splitter_->count() == 1 ||
1878 d.splitter_->orientation() == Qt::Horizontal);
1881 case LFUN_TAB_GROUP_CLOSE:
1882 enable = d.tabWorkAreaCount() > 1;
1885 case LFUN_TOOLBAR_TOGGLE: {
1886 string const name = cmd.getArg(0);
1887 if (GuiToolbar * t = toolbar(name))
1888 flag.setOnOff(t->isVisible());
1891 docstring const msg =
1892 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1898 case LFUN_ICON_SIZE:
1899 flag.setOnOff(d.iconSize(cmd.argument()) == iconSize());
1902 case LFUN_DROP_LAYOUTS_CHOICE:
1906 case LFUN_UI_TOGGLE:
1907 flag.setOnOff(isFullScreen());
1910 case LFUN_DIALOG_DISCONNECT_INSET:
1913 case LFUN_DIALOG_HIDE:
1914 // FIXME: should we check if the dialog is shown?
1917 case LFUN_DIALOG_TOGGLE:
1918 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1919 // fall through to set "enable"
1920 case LFUN_DIALOG_SHOW: {
1921 string const name = cmd.getArg(0);
1923 enable = name == "aboutlyx"
1924 || name == "file" //FIXME: should be removed.
1926 || name == "texinfo"
1927 || name == "progress"
1928 || name == "compare";
1929 else if (name == "character" || name == "symbols"
1930 || name == "mathdelimiter" || name == "mathmatrix") {
1931 if (!buf || buf->isReadonly())
1934 Cursor const & cur = currentBufferView()->cursor();
1935 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1938 else if (name == "latexlog")
1939 enable = FileName(doc_buffer->logName()).isReadableFile();
1940 else if (name == "spellchecker")
1941 enable = theSpellChecker()
1942 && !doc_buffer->isReadonly()
1943 && !doc_buffer->text().empty();
1944 else if (name == "vclog")
1945 enable = doc_buffer->lyxvc().inUse();
1949 case LFUN_DIALOG_UPDATE: {
1950 string const name = cmd.getArg(0);
1952 enable = name == "prefs";
1956 case LFUN_COMMAND_EXECUTE:
1958 case LFUN_MENU_OPEN:
1959 // Nothing to check.
1962 case LFUN_COMPLETION_INLINE:
1963 if (!d.current_work_area_
1964 || !d.current_work_area_->completer().inlinePossible(
1965 currentBufferView()->cursor()))
1969 case LFUN_COMPLETION_POPUP:
1970 if (!d.current_work_area_
1971 || !d.current_work_area_->completer().popupPossible(
1972 currentBufferView()->cursor()))
1977 if (!d.current_work_area_
1978 || !d.current_work_area_->completer().inlinePossible(
1979 currentBufferView()->cursor()))
1983 case LFUN_COMPLETION_ACCEPT:
1984 if (!d.current_work_area_
1985 || (!d.current_work_area_->completer().popupVisible()
1986 && !d.current_work_area_->completer().inlineVisible()
1987 && !d.current_work_area_->completer().completionAvailable()))
1991 case LFUN_COMPLETION_CANCEL:
1992 if (!d.current_work_area_
1993 || (!d.current_work_area_->completer().popupVisible()
1994 && !d.current_work_area_->completer().inlineVisible()))
1998 case LFUN_BUFFER_ZOOM_OUT:
1999 case LFUN_BUFFER_ZOOM_IN: {
2000 // only diff between these two is that the default for ZOOM_OUT
2002 bool const neg_zoom =
2003 convert<int>(cmd.argument()) < 0 ||
2004 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2005 if (lyxrc.currentZoom <= zoom_min_ && neg_zoom) {
2006 docstring const msg =
2007 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2011 enable = doc_buffer;
2015 case LFUN_BUFFER_ZOOM: {
2016 bool const less_than_min_zoom =
2017 !cmd.argument().empty() && convert<int>(cmd.argument()) < zoom_min_;
2018 if (lyxrc.currentZoom <= zoom_min_ && less_than_min_zoom) {
2019 docstring const msg =
2020 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2025 enable = doc_buffer;
2029 case LFUN_BUFFER_MOVE_NEXT:
2030 case LFUN_BUFFER_MOVE_PREVIOUS:
2031 // we do not cycle when moving
2032 case LFUN_BUFFER_NEXT:
2033 case LFUN_BUFFER_PREVIOUS:
2034 // because we cycle, it doesn't matter whether on first or last
2035 enable = (d.currentTabWorkArea()->count() > 1);
2037 case LFUN_BUFFER_SWITCH:
2038 // toggle on the current buffer, but do not toggle off
2039 // the other ones (is that a good idea?)
2041 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2042 flag.setOnOff(true);
2045 case LFUN_VC_REGISTER:
2046 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2048 case LFUN_VC_RENAME:
2049 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2052 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2054 case LFUN_VC_CHECK_IN:
2055 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2057 case LFUN_VC_CHECK_OUT:
2058 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2060 case LFUN_VC_LOCKING_TOGGLE:
2061 enable = doc_buffer && !doc_buffer->hasReadonlyFlag()
2062 && doc_buffer->lyxvc().lockingToggleEnabled();
2063 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2065 case LFUN_VC_REVERT:
2066 enable = doc_buffer && doc_buffer->lyxvc().inUse()
2067 && !doc_buffer->hasReadonlyFlag();
2069 case LFUN_VC_UNDO_LAST:
2070 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2072 case LFUN_VC_REPO_UPDATE:
2073 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2075 case LFUN_VC_COMMAND: {
2076 if (cmd.argument().empty())
2078 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2082 case LFUN_VC_COMPARE:
2083 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2086 case LFUN_SERVER_GOTO_FILE_ROW:
2087 case LFUN_LYX_ACTIVATE:
2089 case LFUN_FORWARD_SEARCH:
2090 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2093 case LFUN_FILE_INSERT_PLAINTEXT:
2094 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2095 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2098 case LFUN_SPELLING_CONTINUOUSLY:
2099 flag.setOnOff(lyxrc.spellcheck_continuously);
2107 flag.setEnabled(false);
2113 static FileName selectTemplateFile()
2115 FileDialog dlg(qt_("Select template file"));
2116 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2117 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2119 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2120 QStringList(qt_("LyX Documents (*.lyx)")));
2122 if (result.first == FileDialog::Later)
2124 if (result.second.isEmpty())
2126 return FileName(fromqstr(result.second));
2130 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2134 Buffer * newBuffer = 0;
2136 newBuffer = checkAndLoadLyXFile(filename);
2137 } catch (ExceptionMessage const & e) {
2144 message(_("Document not loaded."));
2148 setBuffer(newBuffer);
2149 newBuffer->errors("Parse");
2152 theSession().lastFiles().add(filename);
2158 void GuiView::openDocument(string const & fname)
2160 string initpath = lyxrc.document_path;
2162 if (documentBufferView()) {
2163 string const trypath = documentBufferView()->buffer().filePath();
2164 // If directory is writeable, use this as default.
2165 if (FileName(trypath).isDirWritable())
2171 if (fname.empty()) {
2172 FileDialog dlg(qt_("Select document to open"));
2173 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2174 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2176 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2177 FileDialog::Result result =
2178 dlg.open(toqstr(initpath), filter);
2180 if (result.first == FileDialog::Later)
2183 filename = fromqstr(result.second);
2185 // check selected filename
2186 if (filename.empty()) {
2187 message(_("Canceled."));
2193 // get absolute path of file and add ".lyx" to the filename if
2195 FileName const fullname =
2196 fileSearch(string(), filename, "lyx", support::may_not_exist);
2197 if (!fullname.empty())
2198 filename = fullname.absFileName();
2200 if (!fullname.onlyPath().isDirectory()) {
2201 Alert::warning(_("Invalid filename"),
2202 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2203 from_utf8(fullname.absFileName())));
2207 // if the file doesn't exist and isn't already open (bug 6645),
2208 // let the user create one
2209 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2210 !LyXVC::file_not_found_hook(fullname)) {
2211 // the user specifically chose this name. Believe him.
2212 Buffer * const b = newFile(filename, string(), true);
2218 docstring const disp_fn = makeDisplayPath(filename);
2219 message(bformat(_("Opening document %1$s..."), disp_fn));
2222 Buffer * buf = loadDocument(fullname);
2224 str2 = bformat(_("Document %1$s opened."), disp_fn);
2225 if (buf->lyxvc().inUse())
2226 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2227 " " + _("Version control detected.");
2229 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2234 // FIXME: clean that
2235 static bool import(GuiView * lv, FileName const & filename,
2236 string const & format, ErrorList & errorList)
2238 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2240 string loader_format;
2241 vector<string> loaders = theConverters().loaders();
2242 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2243 vector<string>::const_iterator it = loaders.begin();
2244 vector<string>::const_iterator en = loaders.end();
2245 for (; it != en; ++it) {
2246 if (!theConverters().isReachable(format, *it))
2249 string const tofile =
2250 support::changeExtension(filename.absFileName(),
2251 theFormats().extension(*it));
2252 if (!theConverters().convert(0, filename, FileName(tofile),
2253 filename, format, *it, errorList))
2255 loader_format = *it;
2258 if (loader_format.empty()) {
2259 frontend::Alert::error(_("Couldn't import file"),
2260 bformat(_("No information for importing the format %1$s."),
2261 theFormats().prettyName(format)));
2265 loader_format = format;
2267 if (loader_format == "lyx") {
2268 Buffer * buf = lv->loadDocument(lyxfile);
2272 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2276 bool as_paragraphs = loader_format == "textparagraph";
2277 string filename2 = (loader_format == format) ? filename.absFileName()
2278 : support::changeExtension(filename.absFileName(),
2279 theFormats().extension(loader_format));
2280 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2282 guiApp->setCurrentView(lv);
2283 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2290 void GuiView::importDocument(string const & argument)
2293 string filename = split(argument, format, ' ');
2295 LYXERR(Debug::INFO, format << " file: " << filename);
2297 // need user interaction
2298 if (filename.empty()) {
2299 string initpath = lyxrc.document_path;
2300 if (documentBufferView()) {
2301 string const trypath = documentBufferView()->buffer().filePath();
2302 // If directory is writeable, use this as default.
2303 if (FileName(trypath).isDirWritable())
2307 docstring const text = bformat(_("Select %1$s file to import"),
2308 theFormats().prettyName(format));
2310 FileDialog dlg(toqstr(text));
2311 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2312 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2314 docstring filter = theFormats().prettyName(format);
2317 filter += from_utf8(theFormats().extensions(format));
2320 FileDialog::Result result =
2321 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2323 if (result.first == FileDialog::Later)
2326 filename = fromqstr(result.second);
2328 // check selected filename
2329 if (filename.empty())
2330 message(_("Canceled."));
2333 if (filename.empty())
2336 // get absolute path of file
2337 FileName const fullname(support::makeAbsPath(filename));
2339 // Can happen if the user entered a path into the dialog
2341 if (fullname.onlyFileName().empty()) {
2342 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2343 "Aborting import."),
2344 from_utf8(fullname.absFileName()));
2345 frontend::Alert::error(_("File name error"), msg);
2346 message(_("Canceled."));
2351 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2353 // Check if the document already is open
2354 Buffer * buf = theBufferList().getBuffer(lyxfile);
2357 if (!closeBuffer()) {
2358 message(_("Canceled."));
2363 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2365 // if the file exists already, and we didn't do
2366 // -i lyx thefile.lyx, warn
2367 if (lyxfile.exists() && fullname != lyxfile) {
2369 docstring text = bformat(_("The document %1$s already exists.\n\n"
2370 "Do you want to overwrite that document?"), displaypath);
2371 int const ret = Alert::prompt(_("Overwrite document?"),
2372 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2375 message(_("Canceled."));
2380 message(bformat(_("Importing %1$s..."), displaypath));
2381 ErrorList errorList;
2382 if (import(this, fullname, format, errorList))
2383 message(_("imported."));
2385 message(_("file not imported!"));
2387 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2391 void GuiView::newDocument(string const & filename, bool from_template)
2393 FileName initpath(lyxrc.document_path);
2394 if (documentBufferView()) {
2395 FileName const trypath(documentBufferView()->buffer().filePath());
2396 // If directory is writeable, use this as default.
2397 if (trypath.isDirWritable())
2401 string templatefile;
2402 if (from_template) {
2403 templatefile = selectTemplateFile().absFileName();
2404 if (templatefile.empty())
2409 if (filename.empty())
2410 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2412 b = newFile(filename, templatefile, true);
2417 // If no new document could be created, it is unsure
2418 // whether there is a valid BufferView.
2419 if (currentBufferView())
2420 // Ensure the cursor is correctly positioned on screen.
2421 currentBufferView()->showCursor();
2425 void GuiView::insertLyXFile(docstring const & fname)
2427 BufferView * bv = documentBufferView();
2432 FileName filename(to_utf8(fname));
2433 if (filename.empty()) {
2434 // Launch a file browser
2436 string initpath = lyxrc.document_path;
2437 string const trypath = bv->buffer().filePath();
2438 // If directory is writeable, use this as default.
2439 if (FileName(trypath).isDirWritable())
2443 FileDialog dlg(qt_("Select LyX document to insert"));
2444 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2445 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2447 FileDialog::Result result = dlg.open(toqstr(initpath),
2448 QStringList(qt_("LyX Documents (*.lyx)")));
2450 if (result.first == FileDialog::Later)
2454 filename.set(fromqstr(result.second));
2456 // check selected filename
2457 if (filename.empty()) {
2458 // emit message signal.
2459 message(_("Canceled."));
2464 bv->insertLyXFile(filename);
2465 bv->buffer().errors("Parse");
2469 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2471 FileName fname = b.fileName();
2472 FileName const oldname = fname;
2474 if (!newname.empty()) {
2476 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2478 // Switch to this Buffer.
2481 // No argument? Ask user through dialog.
2483 FileDialog dlg(qt_("Choose a filename to save document as"));
2484 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2485 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2487 if (!isLyXFileName(fname.absFileName()))
2488 fname.changeExtension(".lyx");
2490 FileDialog::Result result =
2491 dlg.save(toqstr(fname.onlyPath().absFileName()),
2492 QStringList(qt_("LyX Documents (*.lyx)")),
2493 toqstr(fname.onlyFileName()));
2495 if (result.first == FileDialog::Later)
2498 fname.set(fromqstr(result.second));
2503 if (!isLyXFileName(fname.absFileName()))
2504 fname.changeExtension(".lyx");
2507 // fname is now the new Buffer location.
2509 // if there is already a Buffer open with this name, we do not want
2510 // to have another one. (the second test makes sure we're not just
2511 // trying to overwrite ourselves, which is fine.)
2512 if (theBufferList().exists(fname) && fname != oldname
2513 && theBufferList().getBuffer(fname) != &b) {
2514 docstring const text =
2515 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2516 "Please close it before attempting to overwrite it.\n"
2517 "Do you want to choose a new filename?"),
2518 from_utf8(fname.absFileName()));
2519 int const ret = Alert::prompt(_("Chosen File Already Open"),
2520 text, 0, 1, _("&Rename"), _("&Cancel"));
2522 case 0: return renameBuffer(b, docstring(), kind);
2523 case 1: return false;
2528 bool const existsLocal = fname.exists();
2529 bool const existsInVC = LyXVC::fileInVC(fname);
2530 if (existsLocal || existsInVC) {
2531 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2532 if (kind != LV_WRITE_AS && existsInVC) {
2533 // renaming to a name that is already in VC
2535 docstring text = bformat(_("The document %1$s "
2536 "is already registered.\n\n"
2537 "Do you want to choose a new name?"),
2539 docstring const title = (kind == LV_VC_RENAME) ?
2540 _("Rename document?") : _("Copy document?");
2541 docstring const button = (kind == LV_VC_RENAME) ?
2542 _("&Rename") : _("&Copy");
2543 int const ret = Alert::prompt(title, text, 0, 1,
2544 button, _("&Cancel"));
2546 case 0: return renameBuffer(b, docstring(), kind);
2547 case 1: return false;
2552 docstring text = bformat(_("The document %1$s "
2553 "already exists.\n\n"
2554 "Do you want to overwrite that document?"),
2556 int const ret = Alert::prompt(_("Overwrite document?"),
2557 text, 0, 2, _("&Overwrite"),
2558 _("&Rename"), _("&Cancel"));
2561 case 1: return renameBuffer(b, docstring(), kind);
2562 case 2: return false;
2568 case LV_VC_RENAME: {
2569 string msg = b.lyxvc().rename(fname);
2572 message(from_utf8(msg));
2576 string msg = b.lyxvc().copy(fname);
2579 message(from_utf8(msg));
2585 // LyXVC created the file already in case of LV_VC_RENAME or
2586 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2587 // relative paths of included stuff right if we moved e.g. from
2588 // /a/b.lyx to /a/c/b.lyx.
2590 bool const saved = saveBuffer(b, fname);
2597 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2599 FileName fname = b.fileName();
2601 FileDialog dlg(qt_("Choose a filename to export the document as"));
2602 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2605 QString const anyformat = qt_("Guess from extension (*.*)");
2608 vector<Format const *> export_formats;
2609 for (Format const & f : theFormats())
2610 if (f.documentFormat())
2611 export_formats.push_back(&f);
2612 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2613 map<QString, string> fmap;
2616 for (Format const * f : export_formats) {
2617 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2618 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2620 from_ascii(f->extension())));
2621 types << loc_filter;
2622 fmap[loc_filter] = f->name();
2623 if (from_ascii(f->name()) == iformat) {
2624 filter = loc_filter;
2625 ext = f->extension();
2628 string ofname = fname.onlyFileName();
2630 ofname = support::changeExtension(ofname, ext);
2631 FileDialog::Result result =
2632 dlg.save(toqstr(fname.onlyPath().absFileName()),
2636 if (result.first != FileDialog::Chosen)
2640 fname.set(fromqstr(result.second));
2641 if (filter == anyformat)
2642 fmt_name = theFormats().getFormatFromExtension(fname.extension());
2644 fmt_name = fmap[filter];
2645 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2646 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2648 if (fmt_name.empty() || fname.empty())
2651 // fname is now the new Buffer location.
2652 if (FileName(fname).exists()) {
2653 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2654 docstring text = bformat(_("The document %1$s already "
2655 "exists.\n\nDo you want to "
2656 "overwrite that document?"),
2658 int const ret = Alert::prompt(_("Overwrite document?"),
2659 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2662 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2663 case 2: return false;
2667 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2670 return dr.dispatched();
2674 bool GuiView::saveBuffer(Buffer & b)
2676 return saveBuffer(b, FileName());
2680 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2682 if (workArea(b) && workArea(b)->inDialogMode())
2685 if (fn.empty() && b.isUnnamed())
2686 return renameBuffer(b, docstring());
2688 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2690 theSession().lastFiles().add(b.fileName());
2694 // Switch to this Buffer.
2697 // FIXME: we don't tell the user *WHY* the save failed !!
2698 docstring const file = makeDisplayPath(b.absFileName(), 30);
2699 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2700 "Do you want to rename the document and "
2701 "try again?"), file);
2702 int const ret = Alert::prompt(_("Rename and save?"),
2703 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2706 if (!renameBuffer(b, docstring()))
2715 return saveBuffer(b, fn);
2719 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2721 return closeWorkArea(wa, false);
2725 // We only want to close the buffer if it is not visible in other workareas
2726 // of the same view, nor in other views, and if this is not a child
2727 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2729 Buffer & buf = wa->bufferView().buffer();
2731 bool last_wa = d.countWorkAreasOf(buf) == 1
2732 && !inOtherView(buf) && !buf.parent();
2734 bool close_buffer = last_wa;
2737 if (lyxrc.close_buffer_with_last_view == "yes")
2739 else if (lyxrc.close_buffer_with_last_view == "no")
2740 close_buffer = false;
2743 if (buf.isUnnamed())
2744 file = from_utf8(buf.fileName().onlyFileName());
2746 file = buf.fileName().displayName(30);
2747 docstring const text = bformat(
2748 _("Last view on document %1$s is being closed.\n"
2749 "Would you like to close or hide the document?\n"
2751 "Hidden documents can be displayed back through\n"
2752 "the menu: View->Hidden->...\n"
2754 "To remove this question, set your preference in:\n"
2755 " Tools->Preferences->Look&Feel->UserInterface\n"
2757 int ret = Alert::prompt(_("Close or hide document?"),
2758 text, 0, 1, _("&Close"), _("&Hide"));
2759 close_buffer = (ret == 0);
2763 return closeWorkArea(wa, close_buffer);
2767 bool GuiView::closeBuffer()
2769 GuiWorkArea * wa = currentMainWorkArea();
2770 // coverity complained about this
2771 // it seems unnecessary, but perhaps is worth the check
2772 LASSERT(wa, return false);
2774 setCurrentWorkArea(wa);
2775 Buffer & buf = wa->bufferView().buffer();
2776 return closeWorkArea(wa, !buf.parent());
2780 void GuiView::writeSession() const {
2781 GuiWorkArea const * active_wa = currentMainWorkArea();
2782 for (int i = 0; i < d.splitter_->count(); ++i) {
2783 TabWorkArea * twa = d.tabWorkArea(i);
2784 for (int j = 0; j < twa->count(); ++j) {
2785 GuiWorkArea * wa = twa->workArea(j);
2786 Buffer & buf = wa->bufferView().buffer();
2787 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2793 bool GuiView::closeBufferAll()
2795 // Close the workareas in all other views
2796 QList<int> const ids = guiApp->viewIds();
2797 for (int i = 0; i != ids.size(); ++i) {
2798 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2802 // Close our own workareas
2803 if (!closeWorkAreaAll())
2806 // Now close the hidden buffers. We prevent hidden buffers from being
2807 // dirty, so we can just close them.
2808 theBufferList().closeAll();
2813 bool GuiView::closeWorkAreaAll()
2815 setCurrentWorkArea(currentMainWorkArea());
2817 // We might be in a situation that there is still a tabWorkArea, but
2818 // there are no tabs anymore. This can happen when we get here after a
2819 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2820 // many TabWorkArea's have no documents anymore.
2823 // We have to call count() each time, because it can happen that
2824 // more than one splitter will disappear in one iteration (bug 5998).
2825 while (d.splitter_->count() > empty_twa) {
2826 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2828 if (twa->count() == 0)
2831 setCurrentWorkArea(twa->currentWorkArea());
2832 if (!closeTabWorkArea(twa))
2840 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2845 Buffer & buf = wa->bufferView().buffer();
2847 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2848 Alert::warning(_("Close document"),
2849 _("Document could not be closed because it is being processed by LyX."));
2854 return closeBuffer(buf);
2856 if (!inMultiTabs(wa))
2857 if (!saveBufferIfNeeded(buf, true))
2865 bool GuiView::closeBuffer(Buffer & buf)
2867 // If we are in a close_event all children will be closed in some time,
2868 // so no need to do it here. This will ensure that the children end up
2869 // in the session file in the correct order. If we close the master
2870 // buffer, we can close or release the child buffers here too.
2871 bool success = true;
2873 ListOfBuffers clist = buf.getChildren();
2874 ListOfBuffers::const_iterator it = clist.begin();
2875 ListOfBuffers::const_iterator const bend = clist.end();
2876 for (; it != bend; ++it) {
2877 Buffer * child_buf = *it;
2878 if (theBufferList().isOthersChild(&buf, child_buf)) {
2879 child_buf->setParent(0);
2883 // FIXME: should we look in other tabworkareas?
2884 // ANSWER: I don't think so. I've tested, and if the child is
2885 // open in some other window, it closes without a problem.
2886 GuiWorkArea * child_wa = workArea(*child_buf);
2888 success = closeWorkArea(child_wa, true);
2892 // In this case the child buffer is open but hidden.
2893 // It therefore should not (MUST NOT) be dirty!
2894 LATTEST(child_buf->isClean());
2895 theBufferList().release(child_buf);
2900 // goto bookmark to update bookmark pit.
2901 // FIXME: we should update only the bookmarks related to this buffer!
2902 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2903 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2904 guiApp->gotoBookmark(i+1, false, false);
2906 if (saveBufferIfNeeded(buf, false)) {
2907 buf.removeAutosaveFile();
2908 theBufferList().release(&buf);
2912 // open all children again to avoid a crash because of dangling
2913 // pointers (bug 6603)
2919 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2921 while (twa == d.currentTabWorkArea()) {
2922 twa->setCurrentIndex(twa->count() - 1);
2924 GuiWorkArea * wa = twa->currentWorkArea();
2925 Buffer & b = wa->bufferView().buffer();
2927 // We only want to close the buffer if the same buffer is not visible
2928 // in another view, and if this is not a child and if we are closing
2929 // a view (not a tabgroup).
2930 bool const close_buffer =
2931 !inOtherView(b) && !b.parent() && closing_;
2933 if (!closeWorkArea(wa, close_buffer))
2940 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2942 if (buf.isClean() || buf.paragraphs().empty())
2945 // Switch to this Buffer.
2950 if (buf.isUnnamed())
2951 file = from_utf8(buf.fileName().onlyFileName());
2953 file = buf.fileName().displayName(30);
2955 // Bring this window to top before asking questions.
2960 if (hiding && buf.isUnnamed()) {
2961 docstring const text = bformat(_("The document %1$s has not been "
2962 "saved yet.\n\nDo you want to save "
2963 "the document?"), file);
2964 ret = Alert::prompt(_("Save new document?"),
2965 text, 0, 1, _("&Save"), _("&Cancel"));
2969 docstring const text = bformat(_("The document %1$s has unsaved changes."
2970 "\n\nDo you want to save the document or discard the changes?"), file);
2971 ret = Alert::prompt(_("Save changed document?"),
2972 text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2977 if (!saveBuffer(buf))
2981 // If we crash after this we could have no autosave file
2982 // but I guess this is really improbable (Jug).
2983 // Sometimes improbable things happen:
2984 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
2985 // buf.removeAutosaveFile();
2987 // revert all changes
2998 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3000 Buffer & buf = wa->bufferView().buffer();
3002 for (int i = 0; i != d.splitter_->count(); ++i) {
3003 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3004 if (wa_ && wa_ != wa)
3007 return inOtherView(buf);
3011 bool GuiView::inOtherView(Buffer & buf)
3013 QList<int> const ids = guiApp->viewIds();
3015 for (int i = 0; i != ids.size(); ++i) {
3019 if (guiApp->view(ids[i]).workArea(buf))
3026 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3028 if (!documentBufferView())
3031 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3032 Buffer * const curbuf = &documentBufferView()->buffer();
3033 int nwa = twa->count();
3034 for (int i = 0; i < nwa; ++i) {
3035 if (&workArea(i)->bufferView().buffer() == curbuf) {
3037 if (np == NEXTBUFFER)
3038 next_index = (i == nwa - 1 ? 0 : i + 1);
3040 next_index = (i == 0 ? nwa - 1 : i - 1);
3042 twa->moveTab(i, next_index);
3044 setBuffer(&workArea(next_index)->bufferView().buffer());
3052 /// make sure the document is saved
3053 static bool ensureBufferClean(Buffer * buffer)
3055 LASSERT(buffer, return false);
3056 if (buffer->isClean() && !buffer->isUnnamed())
3059 docstring const file = buffer->fileName().displayName(30);
3062 if (!buffer->isUnnamed()) {
3063 text = bformat(_("The document %1$s has unsaved "
3064 "changes.\n\nDo you want to save "
3065 "the document?"), file);
3066 title = _("Save changed document?");
3069 text = bformat(_("The document %1$s has not been "
3070 "saved yet.\n\nDo you want to save "
3071 "the document?"), file);
3072 title = _("Save new document?");
3074 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3077 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3079 return buffer->isClean() && !buffer->isUnnamed();
3083 bool GuiView::reloadBuffer(Buffer & buf)
3085 Buffer::ReadStatus status = buf.reload();
3086 return status == Buffer::ReadSuccess;
3090 void GuiView::checkExternallyModifiedBuffers()
3092 BufferList::iterator bit = theBufferList().begin();
3093 BufferList::iterator const bend = theBufferList().end();
3094 for (; bit != bend; ++bit) {
3095 Buffer * buf = *bit;
3096 if (buf->fileName().exists() && buf->isChecksumModified()) {
3097 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3098 " Reload now? Any local changes will be lost."),
3099 from_utf8(buf->absFileName()));
3100 int const ret = Alert::prompt(_("Reload externally changed document?"),
3101 text, 0, 1, _("&Reload"), _("&Cancel"));
3109 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3111 Buffer * buffer = documentBufferView()
3112 ? &(documentBufferView()->buffer()) : 0;
3114 switch (cmd.action()) {
3115 case LFUN_VC_REGISTER:
3116 if (!buffer || !ensureBufferClean(buffer))
3118 if (!buffer->lyxvc().inUse()) {
3119 if (buffer->lyxvc().registrer()) {
3120 reloadBuffer(*buffer);
3121 dr.clearMessageUpdate();
3126 case LFUN_VC_RENAME:
3127 case LFUN_VC_COPY: {
3128 if (!buffer || !ensureBufferClean(buffer))
3130 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3131 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3132 // Some changes are not yet committed.
3133 // We test here and not in getStatus(), since
3134 // this test is expensive.
3136 LyXVC::CommandResult ret =
3137 buffer->lyxvc().checkIn(log);
3139 if (ret == LyXVC::ErrorCommand ||
3140 ret == LyXVC::VCSuccess)
3141 reloadBuffer(*buffer);
3142 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3143 frontend::Alert::error(
3144 _("Revision control error."),
3145 _("Document could not be checked in."));
3149 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3150 LV_VC_RENAME : LV_VC_COPY;
3151 renameBuffer(*buffer, cmd.argument(), kind);
3156 case LFUN_VC_CHECK_IN:
3157 if (!buffer || !ensureBufferClean(buffer))
3159 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3161 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3163 // Only skip reloading if the checkin was cancelled or
3164 // an error occurred before the real checkin VCS command
3165 // was executed, since the VCS might have changed the
3166 // file even if it could not checkin successfully.
3167 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3168 reloadBuffer(*buffer);
3172 case LFUN_VC_CHECK_OUT:
3173 if (!buffer || !ensureBufferClean(buffer))
3175 if (buffer->lyxvc().inUse()) {
3176 dr.setMessage(buffer->lyxvc().checkOut());
3177 reloadBuffer(*buffer);
3181 case LFUN_VC_LOCKING_TOGGLE:
3182 LASSERT(buffer, return);
3183 if (!ensureBufferClean(buffer) || buffer->hasReadonlyFlag())
3185 if (buffer->lyxvc().inUse()) {
3186 string res = buffer->lyxvc().lockingToggle();
3188 frontend::Alert::error(_("Revision control error."),
3189 _("Error when setting the locking property."));
3192 reloadBuffer(*buffer);
3197 case LFUN_VC_REVERT:
3198 LASSERT(buffer, return);
3199 if (buffer->lyxvc().revert()) {
3200 reloadBuffer(*buffer);
3201 dr.clearMessageUpdate();
3205 case LFUN_VC_UNDO_LAST:
3206 LASSERT(buffer, return);
3207 buffer->lyxvc().undoLast();
3208 reloadBuffer(*buffer);
3209 dr.clearMessageUpdate();
3212 case LFUN_VC_REPO_UPDATE:
3213 LASSERT(buffer, return);
3214 if (ensureBufferClean(buffer)) {
3215 dr.setMessage(buffer->lyxvc().repoUpdate());
3216 checkExternallyModifiedBuffers();
3220 case LFUN_VC_COMMAND: {
3221 string flag = cmd.getArg(0);
3222 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3225 if (contains(flag, 'M')) {
3226 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3229 string path = cmd.getArg(1);
3230 if (contains(path, "$$p") && buffer)
3231 path = subst(path, "$$p", buffer->filePath());
3232 LYXERR(Debug::LYXVC, "Directory: " << path);
3234 if (!pp.isReadableDirectory()) {
3235 lyxerr << _("Directory is not accessible.") << endl;
3238 support::PathChanger p(pp);
3240 string command = cmd.getArg(2);
3241 if (command.empty())
3244 command = subst(command, "$$i", buffer->absFileName());
3245 command = subst(command, "$$p", buffer->filePath());
3247 command = subst(command, "$$m", to_utf8(message));
3248 LYXERR(Debug::LYXVC, "Command: " << command);
3250 one.startscript(Systemcall::Wait, command);
3254 if (contains(flag, 'I'))
3255 buffer->markDirty();
3256 if (contains(flag, 'R'))
3257 reloadBuffer(*buffer);
3262 case LFUN_VC_COMPARE: {
3263 if (cmd.argument().empty()) {
3264 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3268 string rev1 = cmd.getArg(0);
3273 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3276 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3277 f2 = buffer->absFileName();
3279 string rev2 = cmd.getArg(1);
3283 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3287 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3288 f1 << "\n" << f2 << "\n" );
3289 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3290 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3300 void GuiView::openChildDocument(string const & fname)
3302 LASSERT(documentBufferView(), return);
3303 Buffer & buffer = documentBufferView()->buffer();
3304 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3305 documentBufferView()->saveBookmark(false);
3307 if (theBufferList().exists(filename)) {
3308 child = theBufferList().getBuffer(filename);
3311 message(bformat(_("Opening child document %1$s..."),
3312 makeDisplayPath(filename.absFileName())));
3313 child = loadDocument(filename, false);
3315 // Set the parent name of the child document.
3316 // This makes insertion of citations and references in the child work,
3317 // when the target is in the parent or another child document.
3319 child->setParent(&buffer);
3323 bool GuiView::goToFileRow(string const & argument)
3327 size_t i = argument.find_last_of(' ');
3328 if (i != string::npos) {
3329 file_name = os::internal_path(trim(argument.substr(0, i)));
3330 istringstream is(argument.substr(i + 1));
3335 if (i == string::npos) {
3336 LYXERR0("Wrong argument: " << argument);
3340 string const abstmp = package().temp_dir().absFileName();
3341 string const realtmp = package().temp_dir().realPath();
3342 // We have to use os::path_prefix_is() here, instead of
3343 // simply prefixIs(), because the file name comes from
3344 // an external application and may need case adjustment.
3345 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3346 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3347 // Needed by inverse dvi search. If it is a file
3348 // in tmpdir, call the apropriated function.
3349 // If tmpdir is a symlink, we may have the real
3350 // path passed back, so we correct for that.
3351 if (!prefixIs(file_name, abstmp))
3352 file_name = subst(file_name, realtmp, abstmp);
3353 buf = theBufferList().getBufferFromTmp(file_name);
3355 // Must replace extension of the file to be .lyx
3356 // and get full path
3357 FileName const s = fileSearch(string(),
3358 support::changeExtension(file_name, ".lyx"), "lyx");
3359 // Either change buffer or load the file
3360 if (theBufferList().exists(s))
3361 buf = theBufferList().getBuffer(s);
3362 else if (s.exists()) {
3363 buf = loadDocument(s);
3368 _("File does not exist: %1$s"),
3369 makeDisplayPath(file_name)));
3375 _("No buffer for file: %1$s."),
3376 makeDisplayPath(file_name))
3381 bool success = documentBufferView()->setCursorFromRow(row);
3383 LYXERR(Debug::LATEX,
3384 "setCursorFromRow: invalid position for row " << row);
3385 frontend::Alert::error(_("Inverse Search Failed"),
3386 _("Invalid position requested by inverse search.\n"
3387 "You may need to update the viewed document."));
3393 void GuiView::toolBarPopup(const QPoint & /*pos*/)
3395 QMenu * menu = new QMenu;
3396 menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
3397 menu->exec(QCursor::pos());
3402 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3404 Buffer::ExportStatus const status = func(format);
3406 // the cloning operation will have produced a clone of the entire set of
3407 // documents, starting from the master. so we must delete those.
3408 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3410 busyBuffers.remove(orig);
3415 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3417 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3418 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3422 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3424 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3425 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3429 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3431 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3432 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3436 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3437 string const & argument,
3438 Buffer const * used_buffer,
3439 docstring const & msg,
3440 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3441 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3442 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3447 string format = argument;
3449 format = used_buffer->params().getDefaultOutputFormat();
3450 processing_format = format;
3452 progress_->clearMessages();
3455 #if EXPORT_in_THREAD
3456 GuiViewPrivate::busyBuffers.insert(used_buffer);
3457 Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3458 if (!cloned_buffer) {
3459 Alert::error(_("Export Error"),
3460 _("Error cloning the Buffer."));
3463 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3468 setPreviewFuture(f);
3469 last_export_format = used_buffer->params().bufferFormat();
3472 // We are asynchronous, so we don't know here anything about the success
3475 Buffer::ExportStatus status;
3477 status = (used_buffer->*syncFunc)(format, true);
3478 } else if (previewFunc) {
3479 status = (used_buffer->*previewFunc)(format);
3482 handleExportStatus(gv_, status, format);
3484 return (status == Buffer::ExportSuccess
3485 || status == Buffer::PreviewSuccess);
3489 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3491 BufferView * bv = currentBufferView();
3492 LASSERT(bv, return);
3494 // Let the current BufferView dispatch its own actions.
3495 bv->dispatch(cmd, dr);
3496 if (dr.dispatched())
3499 // Try with the document BufferView dispatch if any.
3500 BufferView * doc_bv = documentBufferView();
3501 if (doc_bv && doc_bv != bv) {
3502 doc_bv->dispatch(cmd, dr);
3503 if (dr.dispatched())
3507 // Then let the current Cursor dispatch its own actions.
3508 bv->cursor().dispatch(cmd);
3510 // update completion. We do it here and not in
3511 // processKeySym to avoid another redraw just for a
3512 // changed inline completion
3513 if (cmd.origin() == FuncRequest::KEYBOARD) {
3514 if (cmd.action() == LFUN_SELF_INSERT
3515 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3516 updateCompletion(bv->cursor(), true, true);
3517 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3518 updateCompletion(bv->cursor(), false, true);
3520 updateCompletion(bv->cursor(), false, false);
3523 dr = bv->cursor().result();
3527 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3529 BufferView * bv = currentBufferView();
3530 // By default we won't need any update.
3531 dr.screenUpdate(Update::None);
3532 // assume cmd will be dispatched
3533 dr.dispatched(true);
3535 Buffer * doc_buffer = documentBufferView()
3536 ? &(documentBufferView()->buffer()) : 0;
3538 if (cmd.origin() == FuncRequest::TOC) {
3539 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3540 // FIXME: do we need to pass a DispatchResult object here?
3541 toc->doDispatch(bv->cursor(), cmd);
3545 string const argument = to_utf8(cmd.argument());
3547 switch(cmd.action()) {
3548 case LFUN_BUFFER_CHILD_OPEN:
3549 openChildDocument(to_utf8(cmd.argument()));
3552 case LFUN_BUFFER_IMPORT:
3553 importDocument(to_utf8(cmd.argument()));
3556 case LFUN_BUFFER_EXPORT: {
3559 // GCC only sees strfwd.h when building merged
3560 if (::lyx::operator==(cmd.argument(), "custom")) {
3561 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3565 string const dest = cmd.getArg(1);
3566 FileName target_dir;
3567 if (!dest.empty() && FileName::isAbsolute(dest))
3568 target_dir = FileName(support::onlyPath(dest));
3570 target_dir = doc_buffer->fileName().onlyPath();
3572 string const format = (argument.empty() || argument == "default") ?
3573 doc_buffer->params().getDefaultOutputFormat() : argument;
3575 if ((dest.empty() && doc_buffer->isUnnamed())
3576 || !target_dir.isDirWritable()) {
3577 exportBufferAs(*doc_buffer, from_utf8(format));
3580 /* TODO/Review: Is it a problem to also export the children?
3581 See the update_unincluded flag */
3582 d.asyncBufferProcessing(format,
3585 &GuiViewPrivate::exportAndDestroy,
3588 // TODO Inform user about success
3592 case LFUN_BUFFER_EXPORT_AS: {
3593 LASSERT(doc_buffer, break);
3594 docstring f = cmd.argument();
3596 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3597 exportBufferAs(*doc_buffer, f);
3601 case LFUN_BUFFER_UPDATE: {
3602 d.asyncBufferProcessing(argument,
3605 &GuiViewPrivate::compileAndDestroy,
3610 case LFUN_BUFFER_VIEW: {
3611 d.asyncBufferProcessing(argument,
3613 _("Previewing ..."),
3614 &GuiViewPrivate::previewAndDestroy,
3619 case LFUN_MASTER_BUFFER_UPDATE: {
3620 d.asyncBufferProcessing(argument,
3621 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3623 &GuiViewPrivate::compileAndDestroy,
3628 case LFUN_MASTER_BUFFER_VIEW: {
3629 d.asyncBufferProcessing(argument,
3630 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3632 &GuiViewPrivate::previewAndDestroy,
3633 0, &Buffer::preview);
3636 case LFUN_BUFFER_SWITCH: {
3637 string const file_name = to_utf8(cmd.argument());
3638 if (!FileName::isAbsolute(file_name)) {
3640 dr.setMessage(_("Absolute filename expected."));
3644 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3647 dr.setMessage(_("Document not loaded"));
3651 // Do we open or switch to the buffer in this view ?
3652 if (workArea(*buffer)
3653 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3658 // Look for the buffer in other views
3659 QList<int> const ids = guiApp->viewIds();
3661 for (; i != ids.size(); ++i) {
3662 GuiView & gv = guiApp->view(ids[i]);
3663 if (gv.workArea(*buffer)) {
3665 gv.activateWindow();
3667 gv.setBuffer(buffer);
3672 // If necessary, open a new window as a last resort
3673 if (i == ids.size()) {
3674 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3680 case LFUN_BUFFER_NEXT:
3681 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3684 case LFUN_BUFFER_MOVE_NEXT:
3685 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3688 case LFUN_BUFFER_PREVIOUS:
3689 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3692 case LFUN_BUFFER_MOVE_PREVIOUS:
3693 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3696 case LFUN_COMMAND_EXECUTE: {
3697 command_execute_ = true;
3698 minibuffer_focus_ = true;
3701 case LFUN_DROP_LAYOUTS_CHOICE:
3702 d.layout_->showPopup();
3705 case LFUN_MENU_OPEN:
3706 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3707 menu->exec(QCursor::pos());
3710 case LFUN_FILE_INSERT:
3711 insertLyXFile(cmd.argument());
3714 case LFUN_FILE_INSERT_PLAINTEXT:
3715 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3716 string const fname = to_utf8(cmd.argument());
3717 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3718 dr.setMessage(_("Absolute filename expected."));
3722 FileName filename(fname);
3723 if (fname.empty()) {
3724 FileDialog dlg(qt_("Select file to insert"));
3726 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3727 QStringList(qt_("All Files (*)")));
3729 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3730 dr.setMessage(_("Canceled."));
3734 filename.set(fromqstr(result.second));
3738 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3739 bv->dispatch(new_cmd, dr);
3744 case LFUN_BUFFER_RELOAD: {
3745 LASSERT(doc_buffer, break);
3748 if (!doc_buffer->isClean()) {
3749 docstring const file =
3750 makeDisplayPath(doc_buffer->absFileName(), 20);
3751 docstring text = doc_buffer->notifiesExternalModification() ?
3752 _("Any changes will be lost. "
3753 "Are you sure you want to load the version on disk "
3754 "of the document %1$s?")
3755 : _("Any changes will be lost. "
3756 "Are you sure you want to revert to the saved version "
3757 "of the document %1$s?");
3758 ret = Alert::prompt(_("Revert to file on disk?"),
3759 bformat(text, file), 1, 1, _("&Revert"), _("&Cancel"));
3763 doc_buffer->markClean();
3764 reloadBuffer(*doc_buffer);
3765 dr.forceBufferUpdate();
3770 case LFUN_BUFFER_WRITE:
3771 LASSERT(doc_buffer, break);
3772 saveBuffer(*doc_buffer);
3775 case LFUN_BUFFER_WRITE_AS:
3776 LASSERT(doc_buffer, break);
3777 renameBuffer(*doc_buffer, cmd.argument());
3780 case LFUN_BUFFER_WRITE_ALL: {
3781 Buffer * first = theBufferList().first();
3784 message(_("Saving all documents..."));
3785 // We cannot use a for loop as the buffer list cycles.
3788 if (!b->isClean()) {
3790 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3792 b = theBufferList().next(b);
3793 } while (b != first);
3794 dr.setMessage(_("All documents saved."));
3798 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
3799 LASSERT(doc_buffer, break);
3800 doc_buffer->clearExternalModification();
3803 case LFUN_BUFFER_CLOSE:
3807 case LFUN_BUFFER_CLOSE_ALL:
3811 case LFUN_TOOLBAR_TOGGLE: {
3812 string const name = cmd.getArg(0);
3813 if (GuiToolbar * t = toolbar(name))
3818 case LFUN_ICON_SIZE: {
3819 QSize size = d.iconSize(cmd.argument());
3821 dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
3822 size.width(), size.height()));
3826 case LFUN_DIALOG_UPDATE: {
3827 string const name = to_utf8(cmd.argument());
3828 if (name == "prefs" || name == "document")
3829 updateDialog(name, string());
3830 else if (name == "paragraph")
3831 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3832 else if (currentBufferView()) {
3833 Inset * inset = currentBufferView()->editedInset(name);
3834 // Can only update a dialog connected to an existing inset
3836 // FIXME: get rid of this indirection; GuiView ask the inset
3837 // if he is kind enough to update itself...
3838 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3839 //FIXME: pass DispatchResult here?
3840 inset->dispatch(currentBufferView()->cursor(), fr);
3846 case LFUN_DIALOG_TOGGLE: {
3847 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3848 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3849 dispatch(FuncRequest(func_code, cmd.argument()), dr);
3853 case LFUN_DIALOG_DISCONNECT_INSET:
3854 disconnectDialog(to_utf8(cmd.argument()));
3857 case LFUN_DIALOG_HIDE: {
3858 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3862 case LFUN_DIALOG_SHOW: {
3863 string const name = cmd.getArg(0);
3864 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3866 if (name == "character") {
3867 data = freefont2string();
3869 showDialog("character", data);
3870 } else if (name == "latexlog") {
3871 // getStatus checks that
3872 LATTEST(doc_buffer);
3873 Buffer::LogType type;
3874 string const logfile = doc_buffer->logName(&type);
3876 case Buffer::latexlog:
3879 case Buffer::buildlog:
3883 data += Lexer::quoteString(logfile);
3884 showDialog("log", data);
3885 } else if (name == "vclog") {
3886 // getStatus checks that
3887 LATTEST(doc_buffer);
3888 string const data = "vc " +
3889 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3890 showDialog("log", data);
3891 } else if (name == "symbols") {
3892 data = bv->cursor().getEncoding()->name();
3894 showDialog("symbols", data);
3896 } else if (name == "prefs" && isFullScreen()) {
3897 lfunUiToggle("fullscreen");
3898 showDialog("prefs", data);
3900 showDialog(name, data);
3905 dr.setMessage(cmd.argument());
3908 case LFUN_UI_TOGGLE: {
3909 string arg = cmd.getArg(0);
3910 if (!lfunUiToggle(arg)) {
3911 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3912 dr.setMessage(bformat(msg, from_utf8(arg)));
3914 // Make sure the keyboard focus stays in the work area.
3919 case LFUN_VIEW_SPLIT: {
3920 LASSERT(doc_buffer, break);
3921 string const orientation = cmd.getArg(0);
3922 d.splitter_->setOrientation(orientation == "vertical"
3923 ? Qt::Vertical : Qt::Horizontal);
3924 TabWorkArea * twa = addTabWorkArea();
3925 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3926 setCurrentWorkArea(wa);
3929 case LFUN_TAB_GROUP_CLOSE:
3930 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3931 closeTabWorkArea(twa);
3932 d.current_work_area_ = 0;
3933 twa = d.currentTabWorkArea();
3934 // Switch to the next GuiWorkArea in the found TabWorkArea.
3936 // Make sure the work area is up to date.
3937 setCurrentWorkArea(twa->currentWorkArea());
3939 setCurrentWorkArea(0);
3944 case LFUN_VIEW_CLOSE:
3945 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3946 closeWorkArea(twa->currentWorkArea());
3947 d.current_work_area_ = 0;
3948 twa = d.currentTabWorkArea();
3949 // Switch to the next GuiWorkArea in the found TabWorkArea.
3951 // Make sure the work area is up to date.
3952 setCurrentWorkArea(twa->currentWorkArea());
3954 setCurrentWorkArea(0);
3959 case LFUN_COMPLETION_INLINE:
3960 if (d.current_work_area_)
3961 d.current_work_area_->completer().showInline();
3964 case LFUN_COMPLETION_POPUP:
3965 if (d.current_work_area_)
3966 d.current_work_area_->completer().showPopup();
3971 if (d.current_work_area_)
3972 d.current_work_area_->completer().tab();
3975 case LFUN_COMPLETION_CANCEL:
3976 if (d.current_work_area_) {
3977 if (d.current_work_area_->completer().popupVisible())
3978 d.current_work_area_->completer().hidePopup();
3980 d.current_work_area_->completer().hideInline();
3984 case LFUN_COMPLETION_ACCEPT:
3985 if (d.current_work_area_)
3986 d.current_work_area_->completer().activate();
3989 case LFUN_BUFFER_ZOOM_IN:
3990 case LFUN_BUFFER_ZOOM_OUT:
3991 case LFUN_BUFFER_ZOOM: {
3992 // use a signed temp to avoid overflow
3993 int zoom = lyxrc.currentZoom;
3994 if (cmd.argument().empty()) {
3995 if (cmd.action() == LFUN_BUFFER_ZOOM)
3997 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4002 if (cmd.action() == LFUN_BUFFER_ZOOM)
4003 zoom = convert<int>(cmd.argument());
4004 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4005 zoom += convert<int>(cmd.argument());
4007 zoom -= convert<int>(cmd.argument());
4010 if (zoom < static_cast<int>(zoom_min_))
4013 lyxrc.currentZoom = zoom;
4015 dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.currentZoom));
4017 // The global QPixmapCache is used in GuiPainter to cache text
4018 // painting so we must reset it.
4019 QPixmapCache::clear();
4020 guiApp->fontLoader().update();
4021 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
4025 case LFUN_VC_REGISTER:
4026 case LFUN_VC_RENAME:
4028 case LFUN_VC_CHECK_IN:
4029 case LFUN_VC_CHECK_OUT:
4030 case LFUN_VC_REPO_UPDATE:
4031 case LFUN_VC_LOCKING_TOGGLE:
4032 case LFUN_VC_REVERT:
4033 case LFUN_VC_UNDO_LAST:
4034 case LFUN_VC_COMMAND:
4035 case LFUN_VC_COMPARE:
4036 dispatchVC(cmd, dr);
4039 case LFUN_SERVER_GOTO_FILE_ROW:
4040 if(goToFileRow(to_utf8(cmd.argument())))
4041 dr.screenUpdate(Update::Force | Update::FitCursor);
4044 case LFUN_LYX_ACTIVATE:
4048 case LFUN_FORWARD_SEARCH: {
4049 // it seems safe to assume we have a document buffer, since
4050 // getStatus wants one.
4051 LATTEST(doc_buffer);
4052 Buffer const * doc_master = doc_buffer->masterBuffer();
4053 FileName const path(doc_master->temppath());
4054 string const texname = doc_master->isChild(doc_buffer)
4055 ? DocFileName(changeExtension(
4056 doc_buffer->absFileName(),
4057 "tex")).mangledFileName()
4058 : doc_buffer->latexName();
4059 string const fulltexname =
4060 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4061 string const mastername =
4062 removeExtension(doc_master->latexName());
4063 FileName const dviname(addName(path.absFileName(),
4064 addExtension(mastername, "dvi")));
4065 FileName const pdfname(addName(path.absFileName(),
4066 addExtension(mastername, "pdf")));
4067 bool const have_dvi = dviname.exists();
4068 bool const have_pdf = pdfname.exists();
4069 if (!have_dvi && !have_pdf) {
4070 dr.setMessage(_("Please, preview the document first."));
4073 string outname = dviname.onlyFileName();
4074 string command = lyxrc.forward_search_dvi;
4075 if (!have_dvi || (have_pdf &&
4076 pdfname.lastModified() > dviname.lastModified())) {
4077 outname = pdfname.onlyFileName();
4078 command = lyxrc.forward_search_pdf;
4081 DocIterator cur = bv->cursor();
4082 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4083 LYXERR(Debug::ACTION, "Forward search: row:" << row
4085 if (row == -1 || command.empty()) {
4086 dr.setMessage(_("Couldn't proceed."));
4089 string texrow = convert<string>(row);
4091 command = subst(command, "$$n", texrow);
4092 command = subst(command, "$$f", fulltexname);
4093 command = subst(command, "$$t", texname);
4094 command = subst(command, "$$o", outname);
4096 PathChanger p(path);
4098 one.startscript(Systemcall::DontWait, command);
4102 case LFUN_SPELLING_CONTINUOUSLY:
4103 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4104 dr.screenUpdate(Update::Force);
4108 // The LFUN must be for one of BufferView, Buffer or Cursor;
4110 dispatchToBufferView(cmd, dr);
4114 // Part of automatic menu appearance feature.
4115 if (isFullScreen()) {
4116 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4120 // Need to update bv because many LFUNs here might have destroyed it
4121 bv = currentBufferView();
4123 // Clear non-empty selections
4124 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4126 Cursor & cur = bv->cursor();
4127 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4128 cur.clearSelection();
4134 bool GuiView::lfunUiToggle(string const & ui_component)
4136 if (ui_component == "scrollbar") {
4137 // hide() is of no help
4138 if (d.current_work_area_->verticalScrollBarPolicy() ==
4139 Qt::ScrollBarAlwaysOff)
4141 d.current_work_area_->setVerticalScrollBarPolicy(
4142 Qt::ScrollBarAsNeeded);
4144 d.current_work_area_->setVerticalScrollBarPolicy(
4145 Qt::ScrollBarAlwaysOff);
4146 } else if (ui_component == "statusbar") {
4147 statusBar()->setVisible(!statusBar()->isVisible());
4148 } else if (ui_component == "menubar") {
4149 menuBar()->setVisible(!menuBar()->isVisible());
4151 if (ui_component == "frame") {
4153 getContentsMargins(&l, &t, &r, &b);
4154 //are the frames in default state?
4155 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4157 setContentsMargins(-2, -2, -2, -2);
4159 setContentsMargins(0, 0, 0, 0);
4162 if (ui_component == "fullscreen") {
4170 void GuiView::toggleFullScreen()
4172 if (isFullScreen()) {
4173 for (int i = 0; i != d.splitter_->count(); ++i)
4174 d.tabWorkArea(i)->setFullScreen(false);
4175 setContentsMargins(0, 0, 0, 0);
4176 setWindowState(windowState() ^ Qt::WindowFullScreen);
4179 statusBar()->show();
4182 hideDialogs("prefs", 0);
4183 for (int i = 0; i != d.splitter_->count(); ++i)
4184 d.tabWorkArea(i)->setFullScreen(true);
4185 setContentsMargins(-2, -2, -2, -2);
4187 setWindowState(windowState() ^ Qt::WindowFullScreen);
4188 if (lyxrc.full_screen_statusbar)
4189 statusBar()->hide();
4190 if (lyxrc.full_screen_menubar)
4192 if (lyxrc.full_screen_toolbars) {
4193 ToolbarMap::iterator end = d.toolbars_.end();
4194 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4199 // give dialogs like the TOC a chance to adapt
4204 Buffer const * GuiView::updateInset(Inset const * inset)
4209 Buffer const * inset_buffer = &(inset->buffer());
4211 for (int i = 0; i != d.splitter_->count(); ++i) {
4212 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4215 Buffer const * buffer = &(wa->bufferView().buffer());
4216 if (inset_buffer == buffer)
4217 wa->scheduleRedraw();
4219 return inset_buffer;
4223 void GuiView::restartCursor()
4225 /* When we move around, or type, it's nice to be able to see
4226 * the cursor immediately after the keypress.
4228 if (d.current_work_area_)
4229 d.current_work_area_->startBlinkingCursor();
4231 // Take this occasion to update the other GUI elements.
4237 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4239 if (d.current_work_area_)
4240 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4245 // This list should be kept in sync with the list of insets in
4246 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4247 // dialog should have the same name as the inset.
4248 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4249 // docs in LyXAction.cpp.
4251 char const * const dialognames[] = {
4253 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4254 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4255 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4256 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4257 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4258 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4259 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4260 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4262 char const * const * const end_dialognames =
4263 dialognames + (sizeof(dialognames) / sizeof(char *));
4267 cmpCStr(char const * name) : name_(name) {}
4268 bool operator()(char const * other) {
4269 return strcmp(other, name_) == 0;
4276 bool isValidName(string const & name)
4278 return find_if(dialognames, end_dialognames,
4279 cmpCStr(name.c_str())) != end_dialognames;
4285 void GuiView::resetDialogs()
4287 // Make sure that no LFUN uses any GuiView.
4288 guiApp->setCurrentView(0);
4292 constructToolbars();
4293 guiApp->menus().fillMenuBar(menuBar(), this, false);
4294 d.layout_->updateContents(true);
4295 // Now update controls with current buffer.
4296 guiApp->setCurrentView(this);
4302 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4304 if (!isValidName(name))
4307 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4309 if (it != d.dialogs_.end()) {
4311 it->second->hideView();
4312 return it->second.get();
4315 Dialog * dialog = build(name);
4316 d.dialogs_[name].reset(dialog);
4317 if (lyxrc.allow_geometry_session)
4318 dialog->restoreSession();
4325 void GuiView::showDialog(string const & name, string const & data,
4328 triggerShowDialog(toqstr(name), toqstr(data), inset);
4332 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4338 const string name = fromqstr(qname);
4339 const string data = fromqstr(qdata);
4343 Dialog * dialog = findOrBuild(name, false);
4345 bool const visible = dialog->isVisibleView();
4346 dialog->showData(data);
4347 if (inset && currentBufferView())
4348 currentBufferView()->editInset(name, inset);
4349 // We only set the focus to the new dialog if it was not yet
4350 // visible in order not to change the existing previous behaviour
4352 // activateWindow is needed for floating dockviews
4353 dialog->asQWidget()->raise();
4354 dialog->asQWidget()->activateWindow();
4355 dialog->asQWidget()->setFocus();
4359 catch (ExceptionMessage const & ex) {
4367 bool GuiView::isDialogVisible(string const & name) const
4369 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4370 if (it == d.dialogs_.end())
4372 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4376 void GuiView::hideDialog(string const & name, Inset * inset)
4378 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4379 if (it == d.dialogs_.end())
4383 if (!currentBufferView())
4385 if (inset != currentBufferView()->editedInset(name))
4389 Dialog * const dialog = it->second.get();
4390 if (dialog->isVisibleView())
4392 if (currentBufferView())
4393 currentBufferView()->editInset(name, 0);
4397 void GuiView::disconnectDialog(string const & name)
4399 if (!isValidName(name))
4401 if (currentBufferView())
4402 currentBufferView()->editInset(name, 0);
4406 void GuiView::hideAll() const
4408 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4409 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4411 for(; it != end; ++it)
4412 it->second->hideView();
4416 void GuiView::updateDialogs()
4418 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4419 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4421 for(; it != end; ++it) {
4422 Dialog * dialog = it->second.get();
4424 if (dialog->needBufferOpen() && !documentBufferView())
4425 hideDialog(fromqstr(dialog->name()), 0);
4426 else if (dialog->isVisibleView())
4427 dialog->checkStatus();
4434 Dialog * createDialog(GuiView & lv, string const & name);
4436 // will be replaced by a proper factory...
4437 Dialog * createGuiAbout(GuiView & lv);
4438 Dialog * createGuiBibtex(GuiView & lv);
4439 Dialog * createGuiChanges(GuiView & lv);
4440 Dialog * createGuiCharacter(GuiView & lv);
4441 Dialog * createGuiCitation(GuiView & lv);
4442 Dialog * createGuiCompare(GuiView & lv);
4443 Dialog * createGuiCompareHistory(GuiView & lv);
4444 Dialog * createGuiDelimiter(GuiView & lv);
4445 Dialog * createGuiDocument(GuiView & lv);
4446 Dialog * createGuiErrorList(GuiView & lv);
4447 Dialog * createGuiExternal(GuiView & lv);
4448 Dialog * createGuiGraphics(GuiView & lv);
4449 Dialog * createGuiInclude(GuiView & lv);
4450 Dialog * createGuiIndex(GuiView & lv);
4451 Dialog * createGuiListings(GuiView & lv);
4452 Dialog * createGuiLog(GuiView & lv);
4453 Dialog * createGuiMathMatrix(GuiView & lv);
4454 Dialog * createGuiNote(GuiView & lv);
4455 Dialog * createGuiParagraph(GuiView & lv);
4456 Dialog * createGuiPhantom(GuiView & lv);
4457 Dialog * createGuiPreferences(GuiView & lv);
4458 Dialog * createGuiPrint(GuiView & lv);
4459 Dialog * createGuiPrintindex(GuiView & lv);
4460 Dialog * createGuiRef(GuiView & lv);
4461 Dialog * createGuiSearch(GuiView & lv);
4462 Dialog * createGuiSearchAdv(GuiView & lv);
4463 Dialog * createGuiSendTo(GuiView & lv);
4464 Dialog * createGuiShowFile(GuiView & lv);
4465 Dialog * createGuiSpellchecker(GuiView & lv);
4466 Dialog * createGuiSymbols(GuiView & lv);
4467 Dialog * createGuiTabularCreate(GuiView & lv);
4468 Dialog * createGuiTexInfo(GuiView & lv);
4469 Dialog * createGuiToc(GuiView & lv);
4470 Dialog * createGuiThesaurus(GuiView & lv);
4471 Dialog * createGuiViewSource(GuiView & lv);
4472 Dialog * createGuiWrap(GuiView & lv);
4473 Dialog * createGuiProgressView(GuiView & lv);
4477 Dialog * GuiView::build(string const & name)
4479 LASSERT(isValidName(name), return 0);
4481 Dialog * dialog = createDialog(*this, name);
4485 if (name == "aboutlyx")
4486 return createGuiAbout(*this);
4487 if (name == "bibtex")
4488 return createGuiBibtex(*this);
4489 if (name == "changes")
4490 return createGuiChanges(*this);
4491 if (name == "character")
4492 return createGuiCharacter(*this);
4493 if (name == "citation")
4494 return createGuiCitation(*this);
4495 if (name == "compare")
4496 return createGuiCompare(*this);
4497 if (name == "comparehistory")
4498 return createGuiCompareHistory(*this);
4499 if (name == "document")
4500 return createGuiDocument(*this);
4501 if (name == "errorlist")
4502 return createGuiErrorList(*this);
4503 if (name == "external")
4504 return createGuiExternal(*this);
4506 return createGuiShowFile(*this);
4507 if (name == "findreplace")
4508 return createGuiSearch(*this);
4509 if (name == "findreplaceadv")
4510 return createGuiSearchAdv(*this);
4511 if (name == "graphics")
4512 return createGuiGraphics(*this);
4513 if (name == "include")
4514 return createGuiInclude(*this);
4515 if (name == "index")
4516 return createGuiIndex(*this);
4517 if (name == "index_print")
4518 return createGuiPrintindex(*this);
4519 if (name == "listings")
4520 return createGuiListings(*this);
4522 return createGuiLog(*this);
4523 if (name == "mathdelimiter")
4524 return createGuiDelimiter(*this);
4525 if (name == "mathmatrix")
4526 return createGuiMathMatrix(*this);
4528 return createGuiNote(*this);
4529 if (name == "paragraph")
4530 return createGuiParagraph(*this);
4531 if (name == "phantom")
4532 return createGuiPhantom(*this);
4533 if (name == "prefs")
4534 return createGuiPreferences(*this);
4536 return createGuiRef(*this);
4537 if (name == "sendto")
4538 return createGuiSendTo(*this);
4539 if (name == "spellchecker")
4540 return createGuiSpellchecker(*this);
4541 if (name == "symbols")
4542 return createGuiSymbols(*this);
4543 if (name == "tabularcreate")
4544 return createGuiTabularCreate(*this);
4545 if (name == "texinfo")
4546 return createGuiTexInfo(*this);
4547 if (name == "thesaurus")
4548 return createGuiThesaurus(*this);
4550 return createGuiToc(*this);
4551 if (name == "view-source")
4552 return createGuiViewSource(*this);
4554 return createGuiWrap(*this);
4555 if (name == "progress")
4556 return createGuiProgressView(*this);
4562 } // namespace frontend
4565 #include "moc_GuiView.cpp"