1 /**
2 * \file GuiView.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
5 *
6 * \author Lars Gullik Bjønnes
7 * \author John Levon
8 * \author Abdelrazak Younes
9 * \author Peter Kümmel
10 *
11 * Full author contact details are available in file CREDITS.
12 */
13
14 #include <config.h>
15
16 #include "GuiView.h"
17
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"
25 #include "GuiToc.h"
26 #include "GuiToolbar.h"
27 #include "GuiWorkArea.h"
28 #include "GuiProgress.h"
29 #include "LayoutBox.h"
30 #include "Menus.h"
31 #include "TocModel.h"
32
33 #include "qt_helpers.h"
34 #include "support/filetools.h"
35
36 #include "frontends/alert.h"
37 #include "frontends/KeySymbol.h"
38
39 #include "buffer_funcs.h"
40 #include "Buffer.h"
41 #include "BufferList.h"
42 #include "BufferParams.h"
43 #include "BufferView.h"
44 #include "Compare.h"
45 #include "Converter.h"
46 #include "Cursor.h"
47 #include "CutAndPaste.h"
48 #include "Encoding.h"
49 #include "ErrorList.h"
50 #include "Format.h"
51 #include "FuncStatus.h"
52 #include "FuncRequest.h"
53 #include "Intl.h"
54 #include "Layout.h"
55 #include "Lexer.h"
56 #include "LyXAction.h"
57 #include "LyX.h"
58 #include "LyXRC.h"
59 #include "LyXVC.h"
60 #include "Paragraph.h"
61 #include "SpellChecker.h"
62 #include "Session.h"
63 #include "TexRow.h"
64 #include "TextClass.h"
65 #include "Text.h"
66 #include "Toolbars.h"
67 #include "version.h"
68
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"
85
86 #include <QAction>
87 #include <QApplication>
88 #include <QCloseEvent>
89 #include <QDebug>
90 #include <QDesktopWidget>
91 #include <QDragEnterEvent>
92 #include <QDropEvent>
93 #include <QFuture>
94 #include <QFutureWatcher>
95 #include <QLabel>
96 #include <QList>
97 #include <QMenu>
98 #include <QMenuBar>
99 #include <QMimeData>
100 #include <QMovie>
101 #include <QPainter>
102 #include <QPixmap>
103 #include <QPixmapCache>
104 #include <QPoint>
105 #include <QPushButton>
106 #include <QScrollBar>
107 #include <QSettings>
108 #include <QShowEvent>
109 #include <QSplitter>
110 #include <QStackedWidget>
111 #include <QStatusBar>
112 #include <QSvgRenderer>
113 #include <QtConcurrentRun>
114 #include <QTime>
115 #include <QTimer>
116 #include <QToolBar>
117 #include <QUrl>
118
119
120
121 // sync with GuiAlert.cpp
122 #define EXPORT_in_THREAD 1
123
124
125 #include "support/bind.h"
126
127 #include <sstream>
128
129 #ifdef HAVE_SYS_TIME_H
130 # include <sys/time.h>
131 #endif
132 #ifdef HAVE_UNISTD_H
133 # include <unistd.h>
134 #endif
135
136
137 using namespace std;
138 using namespace lyx::support;
139
140 namespace lyx {
141
142 using support::addExtension;
143 using support::changeExtension;
144 using support::removeExtension;
145
146 namespace frontend {
147
148 namespace {
149
150 class BackgroundWidget : public QWidget
151 {
152 public:
BackgroundWidget(int width,int height)153 BackgroundWidget(int width, int height)
154 : width_(width), height_(height)
155 {
156 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
157 if (!lyxrc.show_banner)
158 return;
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());
171 } else {
172 splash_ = getPixmap("images/", "banner", "png");
173 }
174 #else
175 splash_ = getPixmap("images/", "banner", "svgz,png");
176 #endif
177
178 QPainter pain(&splash_);
179 pain.setPen(QColor(0, 0, 0));
180 qreal const fsize = fontSize();
181 QPointF const position = textPosition();
182 LYXERR(Debug::GUI,
183 "widget pixel ratio: " << pixelRatio() <<
184 " splash pixel ratio: " << splashPixelRatio() <<
185 " version text size,position: " << fsize << "@" << position.x() << "+" << position.y());
186 QFont font;
187 // The font used to display the version info
188 font.setStyleHint(QFont::SansSerif);
189 font.setWeight(QFont::Bold);
190 font.setPointSizeF(fsize);
191 pain.setFont(font);
192 pain.drawText(position, text);
193 setFocusPolicy(Qt::StrongFocus);
194 }
195
paintEvent(QPaintEvent *)196 void paintEvent(QPaintEvent *)
197 {
198 int const w = width_;
199 int const h = height_;
200 int const x = (width() - w) / 2;
201 int const y = (height() - h) / 2;
202 LYXERR(Debug::GUI,
203 "widget pixel ratio: " << pixelRatio() <<
204 " splash pixel ratio: " << splashPixelRatio() <<
205 " paint pixmap: " << w << "x" << h << "@" << x << "+" << y);
206 QPainter pain(this);
207 pain.drawPixmap(x, y, w, h, splash_);
208 }
209
keyPressEvent(QKeyEvent * ev)210 void keyPressEvent(QKeyEvent * ev)
211 {
212 KeySymbol sym;
213 setKeySymbol(&sym, ev);
214 if (sym.isOK()) {
215 guiApp->processKeySym(sym, q_key_state(ev->modifiers()));
216 ev->accept();
217 } else {
218 ev->ignore();
219 }
220 }
221
222 private:
223 QPixmap splash_;
224 int const width_;
225 int const height_;
226
227 /// Current ratio between physical pixels and device-independent pixels
pixelRatio() const228 double pixelRatio() const {
229 #if QT_VERSION >= 0x050000
230 return qt_scale_factor * devicePixelRatio();
231 #else
232 return 1.0;
233 #endif
234 }
235
fontSize() const236 qreal fontSize() const {
237 return toqstr(lyxrc.font_sizes[FONT_SIZE_NORMAL]).toDouble();
238 }
239
textPosition() const240 QPointF textPosition() const {
241 return QPointF(width_/2 - 18, height_/2 + 45);
242 }
243
splashSize() const244 QSize splashSize() const {
245 return QSize(
246 static_cast<unsigned int>(width_ * pixelRatio()),
247 static_cast<unsigned int>(height_ * pixelRatio()));
248 }
249
250 /// Ratio between physical pixels and device-independent pixels of splash image
splashPixelRatio() const251 double splashPixelRatio() const {
252 #if QT_VERSION >= 0x050000
253 return splash_.devicePixelRatio();
254 #else
255 return 1.0;
256 #endif
257 }
258 };
259
260
261 /// Toolbar store providing access to individual toolbars by name.
262 typedef map<string, GuiToolbar *> ToolbarMap;
263
264 typedef shared_ptr<Dialog> DialogPtr;
265
266 } // namespace
267
268
269 class GuiView::GuiViewPrivate
270 {
271 /// noncopyable
272 GuiViewPrivate(GuiViewPrivate const &);
273 void operator=(GuiViewPrivate const &);
274 public:
GuiViewPrivate(GuiView * gv)275 GuiViewPrivate(GuiView * gv)
276 : gv_(gv), current_work_area_(0), current_main_work_area_(0),
277 layout_(0), autosave_timeout_(5000),
278 in_show_(false)
279 {
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
285 giantIconSize = 48;
286
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");
290 if (!fn.empty()) {
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;
296 else
297 normalIconSize = image.width();
298 }
299
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_);
305 setBackground();
306
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_);
312 }
313 QObject::connect(
314 dynamic_cast<GuiProgress*>(progress_),
315 SIGNAL(updateStatusBarMessage(QString const&)),
316 gv, SLOT(updateStatusBarMessage(QString const&)));
317 QObject::connect(
318 dynamic_cast<GuiProgress*>(progress_),
319 SIGNAL(clearMessageText()),
320 gv, SLOT(clearMessageText()));
321 }
322
~GuiViewPrivate()323 ~GuiViewPrivate()
324 {
325 delete splitter_;
326 delete bg_widget_;
327 delete stack_widget_;
328 }
329
setBackground()330 void setBackground()
331 {
332 stack_widget_->setCurrentWidget(bg_widget_);
333 bg_widget_->setUpdatesEnabled(true);
334 bg_widget_->setFocus();
335 }
336
tabWorkAreaCount()337 int tabWorkAreaCount()
338 {
339 return splitter_->count();
340 }
341
tabWorkArea(int i)342 TabWorkArea * tabWorkArea(int i)
343 {
344 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
345 }
346
currentTabWorkArea()347 TabWorkArea * currentTabWorkArea()
348 {
349 int areas = tabWorkAreaCount();
350 if (areas == 1)
351 // The first TabWorkArea is always the first one, if any.
352 return tabWorkArea(0);
353
354 for (int i = 0; i != areas; ++i) {
355 TabWorkArea * twa = tabWorkArea(i);
356 if (current_main_work_area_ == twa->currentWorkArea())
357 return twa;
358 }
359
360 // None has the focus so we just take the first one.
361 return tabWorkArea(0);
362 }
363
countWorkAreasOf(Buffer & buf)364 int countWorkAreasOf(Buffer & buf)
365 {
366 int areas = tabWorkAreaCount();
367 int count = 0;
368 for (int i = 0; i != areas; ++i) {
369 TabWorkArea * twa = tabWorkArea(i);
370 if (twa->workArea(buf))
371 ++count;
372 }
373 return count;
374 }
375
setPreviewFuture(QFuture<Buffer::ExportStatus> const & f)376 void setPreviewFuture(QFuture<Buffer::ExportStatus> const & f)
377 {
378 if (processing_thread_watcher_.isRunning()) {
379 // we prefer to cancel this preview in order to keep a snappy
380 // interface.
381 return;
382 }
383 processing_thread_watcher_.setFuture(f);
384 }
385
iconSize(docstring const & icon_size)386 QSize iconSize(docstring const & icon_size)
387 {
388 unsigned int size;
389 if (icon_size == "small")
390 size = smallIconSize;
391 else if (icon_size == "normal")
392 size = normalIconSize;
393 else if (icon_size == "big")
394 size = bigIconSize;
395 else if (icon_size == "huge")
396 size = hugeIconSize;
397 else if (icon_size == "giant")
398 size = giantIconSize;
399 else
400 size = icon_size.empty() ? normalIconSize : convert<int>(icon_size);
401
402 if (size < smallIconSize)
403 size = smallIconSize;
404
405 return QSize(size, size);
406 }
407
iconSize(QString const & icon_size)408 QSize iconSize(QString const & icon_size)
409 {
410 return iconSize(qstring_to_ucs4(icon_size));
411 }
412
iconSize(QSize const & qsize)413 string & iconSize(QSize const & qsize)
414 {
415 LATTEST(qsize.width() == qsize.height());
416
417 static string icon_size;
418
419 unsigned int size = qsize.width();
420
421 if (size < smallIconSize)
422 size = smallIconSize;
423
424 if (size == smallIconSize)
425 icon_size = "small";
426 else if (size == normalIconSize)
427 icon_size = "normal";
428 else if (size == bigIconSize)
429 icon_size = "big";
430 else if (size == hugeIconSize)
431 icon_size = "huge";
432 else if (size == giantIconSize)
433 icon_size = "giant";
434 else
435 icon_size = convert<string>(size);
436
437 return icon_size;
438 }
439
440 public:
441 GuiView * gv_;
442 GuiWorkArea * current_work_area_;
443 GuiWorkArea * current_main_work_area_;
444 QSplitter * splitter_;
445 QStackedWidget * stack_widget_;
446 BackgroundWidget * bg_widget_;
447 /// view's toolbars
448 ToolbarMap toolbars_;
449 ProgressInterface* progress_;
450 /// The main layout box.
451 /**
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.
455 *
456 * FIXME: replace that with a proper model so that we are not limited
457 * to only one dialog.
458 */
459 LayoutBox * layout_;
460
461 ///
462 map<string, DialogPtr> dialogs_;
463
464 unsigned int smallIconSize;
465 unsigned int normalIconSize;
466 unsigned int bigIconSize;
467 unsigned int hugeIconSize;
468 unsigned int giantIconSize;
469 ///
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
474 bool in_show_;
475
476 ///
477 TocModels toc_models_;
478
479 ///
480 QFutureWatcher<docstring> autosave_watcher_;
481 QFutureWatcher<Buffer::ExportStatus> processing_thread_watcher_;
482 ///
483 string last_export_format;
484 string processing_format;
485
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);
491
492 template<class T>
493 static Buffer::ExportStatus runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format);
494
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,
502 bool allow_async);
503
504 QVector<GuiWorkArea*> guiWorkAreas();
505 };
506
507 QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
508
509
GuiView(int id)510 GuiView::GuiView(int id)
511 : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0),
512 command_execute_(false), minibuffer_focus_(false), toolbarsMovable_(true),
513 devel_mode_(false)
514 {
515 connect(this, SIGNAL(bufferViewChanged()),
516 this, SLOT(onBufferViewChanged()));
517
518 // GuiToolbars *must* be initialised before the menu bar.
519 setIconSize(QSize(d.normalIconSize, d.normalIconSize)); // at least on Mac the default is 32 otherwise, which is huge
520 constructToolbars();
521
522 // set ourself as the current view. This is needed for the menu bar
523 // filling, at least for the static special menu item on Mac. Otherwise
524 // they are greyed out.
525 guiApp->setCurrentView(this);
526
527 // Fill up the menu bar.
528 guiApp->menus().fillMenuBar(menuBar(), this, true);
529
530 setCentralWidget(d.stack_widget_);
531
532 // Start autosave timer
533 if (lyxrc.autosave) {
534 // The connection is closed when this is destroyed.
535 d.autosave_timeout_.timeout.connect([this](){ autoSave();});
536 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
537 d.autosave_timeout_.start();
538 }
539 connect(&d.statusbar_timer_, SIGNAL(timeout()),
540 this, SLOT(clearMessage()));
541
542 // We don't want to keep the window in memory if it is closed.
543 setAttribute(Qt::WA_DeleteOnClose, true);
544
545 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
546 // QIcon::fromTheme was introduced in Qt 4.6
547 #if (QT_VERSION >= 0x040600)
548 // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
549 // since the icon is provided in the application bundle. We use a themed
550 // version when available and use the bundled one as fallback.
551 setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
552 #else
553 setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
554 #endif
555
556 #endif
557 resetWindowTitle();
558
559 // use tabbed dock area for multiple docks
560 // (such as "source" and "messages")
561 setDockOptions(QMainWindow::ForceTabbedDocks);
562
563 // For Drag&Drop.
564 setAcceptDrops(true);
565
566 // add busy indicator to statusbar
567 QLabel * busylabel = new QLabel(statusBar());
568 statusBar()->addPermanentWidget(busylabel);
569 search_mode mode = theGuiApp()->imageSearchMode();
570 QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
571 QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
572 busylabel->setMovie(busyanim);
573 busyanim->start();
574 busylabel->hide();
575
576 connect(&d.processing_thread_watcher_, SIGNAL(started()),
577 busylabel, SLOT(show()));
578 connect(&d.processing_thread_watcher_, SIGNAL(finished()),
579 busylabel, SLOT(hide()));
580
581 QFontMetrics const fm(statusBar()->fontMetrics());
582 int const iconheight = max(int(d.normalIconSize), fm.height());
583 QSize const iconsize(iconheight, iconheight);
584
585 QPixmap shellescape = QIcon(getPixmap("images/", "emblem-shellescape", "svgz,png")).pixmap(iconsize);
586 shell_escape_ = new QLabel(statusBar());
587 shell_escape_->setPixmap(shellescape);
588 shell_escape_->setScaledContents(true);
589 shell_escape_->setAlignment(Qt::AlignCenter);
590 shell_escape_->setContextMenuPolicy(Qt::CustomContextMenu);
591 shell_escape_->setToolTip(qt_("WARNING: LaTeX is allowed to execute "
592 "external commands for this document. "
593 "Right click to change."));
594 SEMenu * menu = new SEMenu(this);
595 connect(shell_escape_, SIGNAL(customContextMenuRequested(QPoint)),
596 menu, SLOT(showMenu(QPoint)));
597 shell_escape_->hide();
598 statusBar()->addPermanentWidget(shell_escape_);
599
600 QPixmap readonly = QIcon(getPixmap("images/", "emblem-readonly", "svgz,png")).pixmap(iconsize);
601 read_only_ = new QLabel(statusBar());
602 read_only_->setPixmap(readonly);
603 read_only_->setScaledContents(true);
604 read_only_->setAlignment(Qt::AlignCenter);
605 read_only_->hide();
606 statusBar()->addPermanentWidget(read_only_);
607
608 version_control_ = new QLabel(statusBar());
609 version_control_->setAlignment(Qt::AlignCenter);
610 version_control_->setFrameStyle(QFrame::StyledPanel);
611 version_control_->hide();
612 statusBar()->addPermanentWidget(version_control_);
613
614 statusBar()->setSizeGripEnabled(true);
615 updateStatusBar();
616
617 connect(&d.autosave_watcher_, SIGNAL(finished()), this,
618 SLOT(autoSaveThreadFinished()));
619
620 connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
621 SLOT(processingThreadStarted()));
622 connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
623 SLOT(processingThreadFinished()));
624
625 connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
626 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
627
628 // set custom application bars context menu, e.g. tool bar and menu bar
629 setContextMenuPolicy(Qt::CustomContextMenu);
630 connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
631 SLOT(toolBarPopup(const QPoint &)));
632
633 // Forbid too small unresizable window because it can happen
634 // with some window manager under X11.
635 setMinimumSize(300, 200);
636
637 if (lyxrc.allow_geometry_session) {
638 // Now take care of session management.
639 if (restoreLayout())
640 return;
641 }
642
643 // no session handling, default to a sane size.
644 setGeometry(50, 50, 690, 510);
645 initToolbars();
646
647 // clear session data if any.
648 QSettings settings;
649 settings.remove("views");
650 }
651
652
~GuiView()653 GuiView::~GuiView()
654 {
655 delete &d;
656 }
657
658
disableShellEscape()659 void GuiView::disableShellEscape()
660 {
661 BufferView * bv = documentBufferView();
662 if (!bv)
663 return;
664 theSession().shellescapeFiles().remove(bv->buffer().absFileName());
665 bv->buffer().params().shell_escape = false;
666 bv->processUpdateFlags(Update::Force);
667 }
668
669
guiWorkAreas()670 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
671 {
672 QVector<GuiWorkArea*> areas;
673 for (int i = 0; i < tabWorkAreaCount(); i++) {
674 TabWorkArea* ta = tabWorkArea(i);
675 for (int u = 0; u < ta->count(); u++) {
676 areas << ta->workArea(u);
677 }
678 }
679 return areas;
680 }
681
handleExportStatus(GuiView * view,Buffer::ExportStatus status,string const & format)682 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
683 string const & format)
684 {
685 docstring const fmt = theFormats().prettyName(format);
686 docstring msg;
687 switch (status) {
688 case Buffer::ExportSuccess:
689 msg = bformat(_("Successful export to format: %1$s"), fmt);
690 break;
691 case Buffer::ExportCancel:
692 msg = _("Document export cancelled.");
693 break;
694 case Buffer::ExportError:
695 case Buffer::ExportNoPathToFormat:
696 case Buffer::ExportTexPathHasSpaces:
697 case Buffer::ExportConverterError:
698 msg = bformat(_("Error while exporting format: %1$s"), fmt);
699 break;
700 case Buffer::PreviewSuccess:
701 msg = bformat(_("Successful preview of format: %1$s"), fmt);
702 break;
703 case Buffer::PreviewError:
704 msg = bformat(_("Error while previewing format: %1$s"), fmt);
705 break;
706 }
707 view->message(msg);
708 }
709
710
processingThreadStarted()711 void GuiView::processingThreadStarted()
712 {
713 }
714
715
processingThreadFinished()716 void GuiView::processingThreadFinished()
717 {
718 QFutureWatcher<Buffer::ExportStatus> const * watcher =
719 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
720
721 Buffer::ExportStatus const status = watcher->result();
722 handleExportStatus(this, status, d.processing_format);
723
724 updateToolbars();
725 BufferView const * const bv = currentBufferView();
726 if (bv && !bv->buffer().errorList("Export").empty()) {
727 errors("Export");
728 return;
729 }
730 if (status != Buffer::ExportSuccess && status != Buffer::PreviewSuccess &&
731 status != Buffer::ExportCancel) {
732 errors(d.last_export_format);
733 }
734 }
735
736
autoSaveThreadFinished()737 void GuiView::autoSaveThreadFinished()
738 {
739 QFutureWatcher<docstring> const * watcher =
740 static_cast<QFutureWatcher<docstring> const *>(sender());
741 message(watcher->result());
742 updateToolbars();
743 }
744
745
saveLayout() const746 void GuiView::saveLayout() const
747 {
748 QSettings settings;
749 settings.setValue("zoom_ratio", zoom_ratio_);
750 settings.setValue("devel_mode", devel_mode_);
751 settings.beginGroup("views");
752 settings.beginGroup(QString::number(id_));
753 #if defined(Q_WS_X11) || defined(QPA_XCB)
754 settings.setValue("pos", pos());
755 settings.setValue("size", size());
756 #else
757 settings.setValue("geometry", saveGeometry());
758 #endif
759 settings.setValue("layout", saveState(0));
760 settings.setValue("icon_size", toqstr(d.iconSize(iconSize())));
761 }
762
763
saveUISettings() const764 void GuiView::saveUISettings() const
765 {
766 QSettings settings;
767
768 // Save the toolbar private states
769 ToolbarMap::iterator end = d.toolbars_.end();
770 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
771 it->second->saveSession(settings);
772 // Now take care of all other dialogs
773 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
774 for (; it!= d.dialogs_.end(); ++it)
775 it->second->saveSession(settings);
776 }
777
778
restoreLayout()779 bool GuiView::restoreLayout()
780 {
781 QSettings settings;
782 zoom_ratio_ = settings.value("zoom_ratio", 1.0).toDouble();
783 // Actual zoom value: default zoom + fractional offset
784 int zoom = lyxrc.defaultZoom * zoom_ratio_;
785 if (zoom < static_cast<int>(zoom_min_))
786 zoom = zoom_min_;
787 lyxrc.currentZoom = zoom;
788 devel_mode_ = settings.value("devel_mode", devel_mode_).toBool();
789 settings.beginGroup("views");
790 settings.beginGroup(QString::number(id_));
791 QString const icon_key = "icon_size";
792 if (!settings.contains(icon_key))
793 return false;
794
795 //code below is skipped when when ~/.config/LyX is (re)created
796 setIconSize(d.iconSize(settings.value(icon_key).toString()));
797
798 #if defined(Q_WS_X11) || defined(QPA_XCB)
799 QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
800 QSize size = settings.value("size", QSize(690, 510)).toSize();
801 resize(size);
802 move(pos);
803 #else
804 // Work-around for bug #6034: the window ends up in an undetermined
805 // state when trying to restore a maximized window when it is
806 // already maximized.
807 if (!(windowState() & Qt::WindowMaximized))
808 if (!restoreGeometry(settings.value("geometry").toByteArray()))
809 setGeometry(50, 50, 690, 510);
810 #endif
811 // Make sure layout is correctly oriented.
812 setLayoutDirection(qApp->layoutDirection());
813
814 // Allow the toc and view-source dock widget to be restored if needed.
815 Dialog * dialog;
816 if ((dialog = findOrBuild("toc", true)))
817 // see bug 5082. At least setup title and enabled state.
818 // Visibility will be adjusted by restoreState below.
819 dialog->prepareView();
820 if ((dialog = findOrBuild("view-source", true)))
821 dialog->prepareView();
822 if ((dialog = findOrBuild("progress", true)))
823 dialog->prepareView();
824
825 if (!restoreState(settings.value("layout").toByteArray(), 0))
826 initToolbars();
827
828 // init the toolbars that have not been restored
829 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
830 Toolbars::Infos::iterator end = guiApp->toolbars().end();
831 for (; cit != end; ++cit) {
832 GuiToolbar * tb = toolbar(cit->name);
833 if (tb && !tb->isRestored())
834 initToolbar(cit->name);
835 }
836
837 // update lock (all) toolbars positions
838 updateLockToolbars();
839
840 updateDialogs();
841 return true;
842 }
843
844
toolbar(string const & name)845 GuiToolbar * GuiView::toolbar(string const & name)
846 {
847 ToolbarMap::iterator it = d.toolbars_.find(name);
848 if (it != d.toolbars_.end())
849 return it->second;
850
851 LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
852 return 0;
853 }
854
855
updateLockToolbars()856 void GuiView::updateLockToolbars()
857 {
858 toolbarsMovable_ = false;
859 for (ToolbarInfo const & info : guiApp->toolbars()) {
860 GuiToolbar * tb = toolbar(info.name);
861 if (tb && tb->isMovable())
862 toolbarsMovable_ = true;
863 }
864 }
865
866
constructToolbars()867 void GuiView::constructToolbars()
868 {
869 ToolbarMap::iterator it = d.toolbars_.begin();
870 for (; it != d.toolbars_.end(); ++it)
871 delete it->second;
872 d.toolbars_.clear();
873
874 // I don't like doing this here, but the standard toolbar
875 // destroys this object when it's destroyed itself (vfr)
876 d.layout_ = new LayoutBox(*this);
877 d.stack_widget_->addWidget(d.layout_);
878 d.layout_->move(0,0);
879
880 // extracts the toolbars from the backend
881 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
882 Toolbars::Infos::iterator end = guiApp->toolbars().end();
883 for (; cit != end; ++cit)
884 d.toolbars_[cit->name] = new GuiToolbar(*cit, *this);
885 }
886
887
initToolbars()888 void GuiView::initToolbars()
889 {
890 // extracts the toolbars from the backend
891 Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
892 Toolbars::Infos::iterator end = guiApp->toolbars().end();
893 for (; cit != end; ++cit)
894 initToolbar(cit->name);
895 }
896
897
initToolbar(string const & name)898 void GuiView::initToolbar(string const & name)
899 {
900 GuiToolbar * tb = toolbar(name);
901 if (!tb)
902 return;
903 int const visibility = guiApp->toolbars().defaultVisibility(name);
904 bool newline = !(visibility & Toolbars::SAMEROW);
905 tb->setVisible(false);
906 tb->setVisibility(visibility);
907
908 if (visibility & Toolbars::TOP) {
909 if (newline)
910 addToolBarBreak(Qt::TopToolBarArea);
911 addToolBar(Qt::TopToolBarArea, tb);
912 }
913
914 if (visibility & Toolbars::BOTTOM) {
915 if (newline)
916 addToolBarBreak(Qt::BottomToolBarArea);
917 addToolBar(Qt::BottomToolBarArea, tb);
918 }
919
920 if (visibility & Toolbars::LEFT) {
921 if (newline)
922 addToolBarBreak(Qt::LeftToolBarArea);
923 addToolBar(Qt::LeftToolBarArea, tb);
924 }
925
926 if (visibility & Toolbars::RIGHT) {
927 if (newline)
928 addToolBarBreak(Qt::RightToolBarArea);
929 addToolBar(Qt::RightToolBarArea, tb);
930 }
931
932 if (visibility & Toolbars::ON)
933 tb->setVisible(true);
934
935 tb->setMovable(true);
936 }
937
938
tocModels()939 TocModels & GuiView::tocModels()
940 {
941 return d.toc_models_;
942 }
943
944
setFocus()945 void GuiView::setFocus()
946 {
947 LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
948 QMainWindow::setFocus();
949 }
950
951
hasFocus() const952 bool GuiView::hasFocus() const
953 {
954 if (currentWorkArea())
955 return currentWorkArea()->hasFocus();
956 if (currentMainWorkArea())
957 return currentMainWorkArea()->hasFocus();
958 return d.bg_widget_->hasFocus();
959 }
960
961
focusInEvent(QFocusEvent * e)962 void GuiView::focusInEvent(QFocusEvent * e)
963 {
964 LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
965 QMainWindow::focusInEvent(e);
966 // Make sure guiApp points to the correct view.
967 guiApp->setCurrentView(this);
968 if (currentWorkArea())
969 currentWorkArea()->setFocus();
970 else if (currentMainWorkArea())
971 currentMainWorkArea()->setFocus();
972 else
973 d.bg_widget_->setFocus();
974 }
975
976
showEvent(QShowEvent * e)977 void GuiView::showEvent(QShowEvent * e)
978 {
979 LYXERR(Debug::GUI, "Passed Geometry "
980 << size().height() << "x" << size().width()
981 << "+" << pos().x() << "+" << pos().y());
982
983 if (d.splitter_->count() == 0)
984 // No work area, switch to the background widget.
985 d.setBackground();
986
987 updateToolbars();
988 QMainWindow::showEvent(e);
989 }
990
991
closeScheduled()992 bool GuiView::closeScheduled()
993 {
994 closing_ = true;
995 return close();
996 }
997
998
prepareAllBuffersForLogout()999 bool GuiView::prepareAllBuffersForLogout()
1000 {
1001 Buffer * first = theBufferList().first();
1002 if (!first)
1003 return true;
1004
1005 // First, iterate over all buffers and ask the users if unsaved
1006 // changes should be saved.
1007 // We cannot use a for loop as the buffer list cycles.
1008 Buffer * b = first;
1009 do {
1010 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
1011 return false;
1012 b = theBufferList().next(b);
1013 } while (b != first);
1014
1015 // Next, save session state
1016 // When a view/window was closed before without quitting LyX, there
1017 // are already entries in the lastOpened list.
1018 theSession().lastOpened().clear();
1019 writeSession();
1020
1021 return true;
1022 }
1023
1024
1025 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
1026 ** is responsibility of the container (e.g., dialog)
1027 **/
closeEvent(QCloseEvent * close_event)1028 void GuiView::closeEvent(QCloseEvent * close_event)
1029 {
1030 LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
1031
1032 if (!GuiViewPrivate::busyBuffers.isEmpty()) {
1033 Alert::warning(_("Exit LyX"),
1034 _("LyX could not be closed because documents are being processed by LyX."));
1035 close_event->setAccepted(false);
1036 return;
1037 }
1038
1039 // If the user pressed the x (so we didn't call closeView
1040 // programmatically), we want to clear all existing entries.
1041 if (!closing_)
1042 theSession().lastOpened().clear();
1043 closing_ = true;
1044
1045 writeSession();
1046
1047 // it can happen that this event arrives without selecting the view,
1048 // e.g. when clicking the close button on a background window.
1049 setFocus();
1050 if (!closeWorkAreaAll()) {
1051 closing_ = false;
1052 close_event->ignore();
1053 return;
1054 }
1055
1056 // Make sure that nothing will use this to be closed View.
1057 guiApp->unregisterView(this);
1058
1059 if (isFullScreen()) {
1060 // Switch off fullscreen before closing.
1061 toggleFullScreen();
1062 updateDialogs();
1063 }
1064
1065 // Make sure the timer time out will not trigger a statusbar update.
1066 d.statusbar_timer_.stop();
1067
1068 // Saving fullscreen requires additional tweaks in the toolbar code.
1069 // It wouldn't also work under linux natively.
1070 if (lyxrc.allow_geometry_session) {
1071 saveLayout();
1072 saveUISettings();
1073 }
1074
1075 close_event->accept();
1076 }
1077
1078
dragEnterEvent(QDragEnterEvent * event)1079 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1080 {
1081 if (event->mimeData()->hasUrls())
1082 event->accept();
1083 /// \todo Ask lyx-devel is this is enough:
1084 /// if (event->mimeData()->hasFormat("text/plain"))
1085 /// event->acceptProposedAction();
1086 }
1087
1088
dropEvent(QDropEvent * event)1089 void GuiView::dropEvent(QDropEvent * event)
1090 {
1091 QList<QUrl> files = event->mimeData()->urls();
1092 if (files.isEmpty())
1093 return;
1094
1095 LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1096 for (int i = 0; i != files.size(); ++i) {
1097 string const file = os::internal_path(fromqstr(
1098 files.at(i).toLocalFile()));
1099 if (file.empty())
1100 continue;
1101
1102 string const ext = support::getExtension(file);
1103 vector<const Format *> found_formats;
1104
1105 // Find all formats that have the correct extension.
1106 vector<const Format *> const & import_formats
1107 = theConverters().importableFormats();
1108 vector<const Format *>::const_iterator it = import_formats.begin();
1109 for (; it != import_formats.end(); ++it)
1110 if ((*it)->hasExtension(ext))
1111 found_formats.push_back(*it);
1112
1113 FuncRequest cmd;
1114 if (found_formats.size() >= 1) {
1115 if (found_formats.size() > 1) {
1116 //FIXME: show a dialog to choose the correct importable format
1117 LYXERR(Debug::FILES,
1118 "Multiple importable formats found, selecting first");
1119 }
1120 string const arg = found_formats[0]->name() + " " + file;
1121 cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1122 }
1123 else {
1124 //FIXME: do we have to explicitly check whether it's a lyx file?
1125 LYXERR(Debug::FILES,
1126 "No formats found, trying to open it as a lyx file");
1127 cmd = FuncRequest(LFUN_FILE_OPEN, file);
1128 }
1129 // add the functions to the queue
1130 guiApp->addToFuncRequestQueue(cmd);
1131 event->accept();
1132 }
1133 // now process the collected functions. We perform the events
1134 // asynchronously. This prevents potential problems in case the
1135 // BufferView is closed within an event.
1136 guiApp->processFuncRequestQueueAsync();
1137 }
1138
1139
message(docstring const & str)1140 void GuiView::message(docstring const & str)
1141 {
1142 if (ForkedProcess::iAmAChild())
1143 return;
1144
1145 // call is moved to GUI-thread by GuiProgress
1146 d.progress_->appendMessage(toqstr(str));
1147 }
1148
1149
clearMessageText()1150 void GuiView::clearMessageText()
1151 {
1152 message(docstring());
1153 }
1154
1155
updateStatusBarMessage(QString const & str)1156 void GuiView::updateStatusBarMessage(QString const & str)
1157 {
1158 statusBar()->showMessage(str);
1159 d.statusbar_timer_.stop();
1160 d.statusbar_timer_.start(3000);
1161 }
1162
1163
clearMessage()1164 void GuiView::clearMessage()
1165 {
1166 // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1167 // the hasFocus function mostly returns false, even if the focus is on
1168 // a workarea in this view.
1169 //if (!hasFocus())
1170 // return;
1171 showMessage();
1172 d.statusbar_timer_.stop();
1173 }
1174
1175
updateWindowTitle(GuiWorkArea * wa)1176 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1177 {
1178 if (wa != d.current_work_area_
1179 || wa->bufferView().buffer().isInternal())
1180 return;
1181 Buffer const & buf = wa->bufferView().buffer();
1182 // Set the windows title
1183 docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
1184 if (buf.notifiesExternalModification()) {
1185 title = bformat(_("%1$s (modified externally)"), title);
1186 // If the external modification status has changed, then maybe the status of
1187 // buffer-save has changed too.
1188 updateToolbars();
1189 }
1190 #ifndef Q_WS_MAC
1191 title += from_ascii(" - LyX");
1192 #endif
1193 setWindowTitle(toqstr(title));
1194 // Sets the path for the window: this is used by OSX to
1195 // allow a context click on the title bar showing a menu
1196 // with the path up to the file
1197 setWindowFilePath(toqstr(buf.absFileName()));
1198 // Tell Qt whether the current document is changed
1199 setWindowModified(!buf.isClean());
1200
1201 if (buf.params().shell_escape)
1202 shell_escape_->show();
1203 else
1204 shell_escape_->hide();
1205
1206 if (buf.hasReadonlyFlag())
1207 read_only_->show();
1208 else
1209 read_only_->hide();
1210
1211 if (buf.lyxvc().inUse()) {
1212 version_control_->show();
1213 version_control_->setText(toqstr(buf.lyxvc().vcstatus()));
1214 } else
1215 version_control_->hide();
1216 }
1217
1218
on_currentWorkAreaChanged(GuiWorkArea * wa)1219 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1220 {
1221 if (d.current_work_area_)
1222 // disconnect the current work area from all slots
1223 QObject::disconnect(d.current_work_area_, 0, this, 0);
1224 disconnectBuffer();
1225 disconnectBufferView();
1226 connectBufferView(wa->bufferView());
1227 connectBuffer(wa->bufferView().buffer());
1228 d.current_work_area_ = wa;
1229 QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1230 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1231 QObject::connect(wa, SIGNAL(busy(bool)),
1232 this, SLOT(setBusy(bool)));
1233 // connection of a signal to a signal
1234 QObject::connect(wa, SIGNAL(bufferViewChanged()),
1235 this, SIGNAL(bufferViewChanged()));
1236 Q_EMIT updateWindowTitle(wa);
1237 Q_EMIT bufferViewChanged();
1238 }
1239
1240
onBufferViewChanged()1241 void GuiView::onBufferViewChanged()
1242 {
1243 structureChanged();
1244 // Buffer-dependent dialogs must be updated. This is done here because
1245 // some dialogs require buffer()->text.
1246 updateDialogs();
1247 }
1248
1249
on_lastWorkAreaRemoved()1250 void GuiView::on_lastWorkAreaRemoved()
1251 {
1252 if (closing_)
1253 // We already are in a close event. Nothing more to do.
1254 return;
1255
1256 if (d.splitter_->count() > 1)
1257 // We have a splitter so don't close anything.
1258 return;
1259
1260 // Reset and updates the dialogs.
1261 Q_EMIT bufferViewChanged();
1262
1263 resetWindowTitle();
1264 updateStatusBar();
1265
1266 if (lyxrc.open_buffers_in_tabs)
1267 // Nothing more to do, the window should stay open.
1268 return;
1269
1270 if (guiApp->viewIds().size() > 1) {
1271 close();
1272 return;
1273 }
1274
1275 #ifdef Q_OS_MAC
1276 // On Mac we also close the last window because the application stay
1277 // resident in memory. On other platforms we don't close the last
1278 // window because this would quit the application.
1279 close();
1280 #endif
1281 }
1282
1283
updateStatusBar()1284 void GuiView::updateStatusBar()
1285 {
1286 // let the user see the explicit message
1287 if (d.statusbar_timer_.isActive())
1288 return;
1289
1290 showMessage();
1291 }
1292
1293
showMessage()1294 void GuiView::showMessage()
1295 {
1296 if (busy_)
1297 return;
1298 QString msg = toqstr(theGuiApp()->viewStatusMessage());
1299 if (msg.isEmpty()) {
1300 BufferView const * bv = currentBufferView();
1301 if (bv)
1302 msg = toqstr(bv->cursor().currentState(devel_mode_));
1303 else
1304 msg = qt_("Welcome to LyX!");
1305 }
1306 statusBar()->showMessage(msg);
1307 }
1308
1309
event(QEvent * e)1310 bool GuiView::event(QEvent * e)
1311 {
1312 switch (e->type())
1313 {
1314 // Useful debug code:
1315 //case QEvent::ActivationChange:
1316 //case QEvent::WindowDeactivate:
1317 //case QEvent::Paint:
1318 //case QEvent::Enter:
1319 //case QEvent::Leave:
1320 //case QEvent::HoverEnter:
1321 //case QEvent::HoverLeave:
1322 //case QEvent::HoverMove:
1323 //case QEvent::StatusTip:
1324 //case QEvent::DragEnter:
1325 //case QEvent::DragLeave:
1326 //case QEvent::Drop:
1327 // break;
1328
1329 case QEvent::WindowActivate: {
1330 GuiView * old_view = guiApp->currentView();
1331 if (this == old_view) {
1332 setFocus();
1333 return QMainWindow::event(e);
1334 }
1335 if (old_view && old_view->currentBufferView()) {
1336 // save current selection to the selection buffer to allow
1337 // middle-button paste in this window.
1338 cap::saveSelection(old_view->currentBufferView()->cursor());
1339 }
1340 guiApp->setCurrentView(this);
1341 if (d.current_work_area_)
1342 on_currentWorkAreaChanged(d.current_work_area_);
1343 else
1344 resetWindowTitle();
1345 setFocus();
1346 return QMainWindow::event(e);
1347 }
1348
1349 case QEvent::ShortcutOverride: {
1350 // See bug 4888
1351 if (isFullScreen() && menuBar()->isHidden()) {
1352 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1353 // FIXME: we should also try to detect special LyX shortcut such as
1354 // Alt-P and Alt-M. Right now there is a hack in
1355 // GuiWorkArea::processKeySym() that hides again the menubar for
1356 // those cases.
1357 if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1358 menuBar()->show();
1359 return QMainWindow::event(e);
1360 }
1361 }
1362 return QMainWindow::event(e);
1363 }
1364
1365 default:
1366 return QMainWindow::event(e);
1367 }
1368 }
1369
resetWindowTitle()1370 void GuiView::resetWindowTitle()
1371 {
1372 setWindowTitle(qt_("LyX"));
1373 }
1374
focusNextPrevChild(bool)1375 bool GuiView::focusNextPrevChild(bool /*next*/)
1376 {
1377 setFocus();
1378 return true;
1379 }
1380
1381
busy() const1382 bool GuiView::busy() const
1383 {
1384 return busy_ > 0;
1385 }
1386
1387
setBusy(bool busy)1388 void GuiView::setBusy(bool busy)
1389 {
1390 bool const busy_before = busy_ > 0;
1391 busy ? ++busy_ : --busy_;
1392 if ((busy_ > 0) == busy_before)
1393 // busy state didn't change
1394 return;
1395
1396 if (busy) {
1397 QApplication::setOverrideCursor(Qt::WaitCursor);
1398 return;
1399 }
1400 QApplication::restoreOverrideCursor();
1401 updateLayoutList();
1402 }
1403
1404
resetCommandExecute()1405 void GuiView::resetCommandExecute()
1406 {
1407 command_execute_ = false;
1408 updateToolbars();
1409 }
1410
1411
pixelRatio() const1412 double GuiView::pixelRatio() const
1413 {
1414 #if QT_VERSION >= 0x050000
1415 return qt_scale_factor * devicePixelRatio();
1416 #else
1417 return 1.0;
1418 #endif
1419 }
1420
1421
workArea(int index)1422 GuiWorkArea * GuiView::workArea(int index)
1423 {
1424 if (TabWorkArea * twa = d.currentTabWorkArea())
1425 if (index < twa->count())
1426 return twa->workArea(index);
1427 return 0;
1428 }
1429
1430
workArea(Buffer & buffer)1431 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1432 {
1433 if (currentWorkArea()
1434 && ¤tWorkArea()->bufferView().buffer() == &buffer)
1435 return currentWorkArea();
1436 if (TabWorkArea * twa = d.currentTabWorkArea())
1437 return twa->workArea(buffer);
1438 return 0;
1439 }
1440
1441
addWorkArea(Buffer & buffer)1442 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1443 {
1444 // Automatically create a TabWorkArea if there are none yet.
1445 TabWorkArea * tab_widget = d.splitter_->count()
1446 ? d.currentTabWorkArea() : addTabWorkArea();
1447 return tab_widget->addWorkArea(buffer, *this);
1448 }
1449
1450
addTabWorkArea()1451 TabWorkArea * GuiView::addTabWorkArea()
1452 {
1453 TabWorkArea * twa = new TabWorkArea;
1454 QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1455 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1456 QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1457 this, SLOT(on_lastWorkAreaRemoved()));
1458
1459 d.splitter_->addWidget(twa);
1460 d.stack_widget_->setCurrentWidget(d.splitter_);
1461 return twa;
1462 }
1463
1464
currentWorkArea() const1465 GuiWorkArea const * GuiView::currentWorkArea() const
1466 {
1467 return d.current_work_area_;
1468 }
1469
1470
currentWorkArea()1471 GuiWorkArea * GuiView::currentWorkArea()
1472 {
1473 return d.current_work_area_;
1474 }
1475
1476
currentMainWorkArea() const1477 GuiWorkArea const * GuiView::currentMainWorkArea() const
1478 {
1479 if (!d.currentTabWorkArea())
1480 return 0;
1481 return d.currentTabWorkArea()->currentWorkArea();
1482 }
1483
1484
currentMainWorkArea()1485 GuiWorkArea * GuiView::currentMainWorkArea()
1486 {
1487 if (!d.currentTabWorkArea())
1488 return 0;
1489 return d.currentTabWorkArea()->currentWorkArea();
1490 }
1491
1492
setCurrentWorkArea(GuiWorkArea * wa)1493 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1494 {
1495 LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1496 if (!wa) {
1497 d.current_work_area_ = 0;
1498 d.setBackground();
1499 Q_EMIT bufferViewChanged();
1500 return;
1501 }
1502
1503 // FIXME: I've no clue why this is here and why it accesses
1504 // theGuiApp()->currentView, which might be 0 (bug 6464).
1505 // See also 27525 (vfr).
1506 if (theGuiApp()->currentView() == this
1507 && theGuiApp()->currentView()->currentWorkArea() == wa)
1508 return;
1509
1510 if (currentBufferView())
1511 cap::saveSelection(currentBufferView()->cursor());
1512
1513 theGuiApp()->setCurrentView(this);
1514 d.current_work_area_ = wa;
1515
1516 // We need to reset this now, because it will need to be
1517 // right if the tabWorkArea gets reset in the for loop. We
1518 // will change it back if we aren't in that case.
1519 GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1520 d.current_main_work_area_ = wa;
1521
1522 for (int i = 0; i != d.splitter_->count(); ++i) {
1523 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1524 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
1525 << ", Current main wa: " << currentMainWorkArea());
1526 return;
1527 }
1528 }
1529
1530 d.current_main_work_area_ = old_cmwa;
1531
1532 LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1533 on_currentWorkAreaChanged(wa);
1534 BufferView & bv = wa->bufferView();
1535 bv.cursor().fixIfBroken();
1536 bv.updateMetrics();
1537 wa->setUpdatesEnabled(true);
1538 LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1539 }
1540
1541
removeWorkArea(GuiWorkArea * wa)1542 void GuiView::removeWorkArea(GuiWorkArea * wa)
1543 {
1544 LASSERT(wa, return);
1545 if (wa == d.current_work_area_) {
1546 disconnectBuffer();
1547 disconnectBufferView();
1548 d.current_work_area_ = 0;
1549 d.current_main_work_area_ = 0;
1550 }
1551
1552 bool found_twa = false;
1553 for (int i = 0; i != d.splitter_->count(); ++i) {
1554 TabWorkArea * twa = d.tabWorkArea(i);
1555 if (twa->removeWorkArea(wa)) {
1556 // Found in this tab group, and deleted the GuiWorkArea.
1557 found_twa = true;
1558 if (twa->count() != 0) {
1559 if (d.current_work_area_ == 0)
1560 // This means that we are closing the current GuiWorkArea, so
1561 // switch to the next GuiWorkArea in the found TabWorkArea.
1562 setCurrentWorkArea(twa->currentWorkArea());
1563 } else {
1564 // No more WorkAreas in this tab group, so delete it.
1565 delete twa;
1566 }
1567 break;
1568 }
1569 }
1570
1571 // It is not a tabbed work area (i.e., the search work area), so it
1572 // should be deleted by other means.
1573 LASSERT(found_twa, return);
1574
1575 if (d.current_work_area_ == 0) {
1576 if (d.splitter_->count() != 0) {
1577 TabWorkArea * twa = d.currentTabWorkArea();
1578 setCurrentWorkArea(twa->currentWorkArea());
1579 } else {
1580 // No more work areas, switch to the background widget.
1581 setCurrentWorkArea(0);
1582 }
1583 }
1584 }
1585
1586
getLayoutDialog() const1587 LayoutBox * GuiView::getLayoutDialog() const
1588 {
1589 return d.layout_;
1590 }
1591
1592
updateLayoutList()1593 void GuiView::updateLayoutList()
1594 {
1595 if (d.layout_)
1596 d.layout_->updateContents(false);
1597 }
1598
1599
updateToolbars()1600 void GuiView::updateToolbars()
1601 {
1602 ToolbarMap::iterator end = d.toolbars_.end();
1603 if (d.current_work_area_) {
1604 int context = 0;
1605 if (d.current_work_area_->bufferView().cursor().inMathed()
1606 && !d.current_work_area_->bufferView().cursor().inRegexped())
1607 context |= Toolbars::MATH;
1608 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1609 context |= Toolbars::TABLE;
1610 if (currentBufferView()->buffer().areChangesPresent()
1611 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1612 && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1613 || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1614 && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1615 context |= Toolbars::REVIEW;
1616 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1617 context |= Toolbars::MATHMACROTEMPLATE;
1618 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1619 context |= Toolbars::IPA;
1620 if (command_execute_)
1621 context |= Toolbars::MINIBUFFER;
1622 if (minibuffer_focus_) {
1623 context |= Toolbars::MINIBUFFER_FOCUS;
1624 minibuffer_focus_ = false;
1625 }
1626
1627 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1628 it->second->update(context);
1629 } else
1630 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1631 it->second->update();
1632 }
1633
1634
setBuffer(Buffer * newBuffer,bool switch_to)1635 void GuiView::setBuffer(Buffer * newBuffer, bool switch_to)
1636 {
1637 LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1638 LASSERT(newBuffer, return);
1639
1640 GuiWorkArea * wa = workArea(*newBuffer);
1641 if (wa == 0) {
1642 setBusy(true);
1643 newBuffer->masterBuffer()->updateBuffer();
1644 setBusy(false);
1645 wa = addWorkArea(*newBuffer);
1646 // scroll to the position when the BufferView was last closed
1647 if (lyxrc.use_lastfilepos) {
1648 LastFilePosSection::FilePos filepos =
1649 theSession().lastFilePos().load(newBuffer->fileName());
1650 wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1651 }
1652 } else {
1653 //Disconnect the old buffer...there's no new one.
1654 disconnectBuffer();
1655 }
1656 connectBuffer(*newBuffer);
1657 connectBufferView(wa->bufferView());
1658 if (switch_to)
1659 setCurrentWorkArea(wa);
1660 }
1661
1662
connectBuffer(Buffer & buf)1663 void GuiView::connectBuffer(Buffer & buf)
1664 {
1665 buf.setGuiDelegate(this);
1666 }
1667
1668
disconnectBuffer()1669 void GuiView::disconnectBuffer()
1670 {
1671 if (d.current_work_area_)
1672 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1673 }
1674
1675
connectBufferView(BufferView & bv)1676 void GuiView::connectBufferView(BufferView & bv)
1677 {
1678 bv.setGuiDelegate(this);
1679 }
1680
1681
disconnectBufferView()1682 void GuiView::disconnectBufferView()
1683 {
1684 if (d.current_work_area_)
1685 d.current_work_area_->bufferView().setGuiDelegate(0);
1686 }
1687
1688
errors(string const & error_type,bool from_master)1689 void GuiView::errors(string const & error_type, bool from_master)
1690 {
1691 BufferView const * const bv = currentBufferView();
1692 if (!bv)
1693 return;
1694
1695 #if EXPORT_in_THREAD
1696 // We are called with from_master == false by default, so we
1697 // have to figure out whether that is the case or not.
1698 ErrorList & el = bv->buffer().errorList(error_type);
1699 if (el.empty()) {
1700 el = bv->buffer().masterBuffer()->errorList(error_type);
1701 from_master = true;
1702 }
1703 #else
1704 ErrorList const & el = from_master ?
1705 bv->buffer().masterBuffer()->errorList(error_type) :
1706 bv->buffer().errorList(error_type);
1707 #endif
1708
1709 if (el.empty())
1710 return;
1711
1712 string data = error_type;
1713 if (from_master)
1714 data = "from_master|" + error_type;
1715 showDialog("errorlist", data);
1716 }
1717
1718
updateTocItem(string const & type,DocIterator const & dit)1719 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1720 {
1721 d.toc_models_.updateItem(toqstr(type), dit);
1722 }
1723
1724
structureChanged()1725 void GuiView::structureChanged()
1726 {
1727 // This is called from the Buffer, which has no way to ensure that cursors
1728 // in BufferView remain valid.
1729 if (documentBufferView())
1730 documentBufferView()->cursor().sanitize();
1731 // FIXME: This is slightly expensive, though less than the tocBackend update
1732 // (#9880). This also resets the view in the Toc Widget (#6675).
1733 d.toc_models_.reset(documentBufferView());
1734 // Navigator needs more than a simple update in this case. It needs to be
1735 // rebuilt.
1736 updateDialog("toc", "");
1737 }
1738
1739
updateDialog(string const & name,string const & data)1740 void GuiView::updateDialog(string const & name, string const & data)
1741 {
1742 if (!isDialogVisible(name))
1743 return;
1744
1745 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1746 if (it == d.dialogs_.end())
1747 return;
1748
1749 Dialog * const dialog = it->second.get();
1750 if (dialog->isVisibleView())
1751 dialog->initialiseParams(data);
1752 }
1753
1754
documentBufferView()1755 BufferView * GuiView::documentBufferView()
1756 {
1757 return currentMainWorkArea()
1758 ? ¤tMainWorkArea()->bufferView()
1759 : 0;
1760 }
1761
1762
documentBufferView() const1763 BufferView const * GuiView::documentBufferView() const
1764 {
1765 return currentMainWorkArea()
1766 ? ¤tMainWorkArea()->bufferView()
1767 : 0;
1768 }
1769
1770
currentBufferView()1771 BufferView * GuiView::currentBufferView()
1772 {
1773 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1774 }
1775
1776
currentBufferView() const1777 BufferView const * GuiView::currentBufferView() const
1778 {
1779 return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1780 }
1781
1782
autosaveAndDestroy(Buffer const * orig,Buffer * clone)1783 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1784 Buffer const * orig, Buffer * clone)
1785 {
1786 bool const success = clone->autoSave();
1787 delete clone;
1788 busyBuffers.remove(orig);
1789 return success
1790 ? _("Automatic save done.")
1791 : _("Automatic save failed!");
1792 }
1793
1794
autoSave()1795 void GuiView::autoSave()
1796 {
1797 LYXERR(Debug::INFO, "Running autoSave()");
1798
1799 Buffer * buffer = documentBufferView()
1800 ? &documentBufferView()->buffer() : 0;
1801 if (!buffer) {
1802 resetAutosaveTimers();
1803 return;
1804 }
1805
1806 GuiViewPrivate::busyBuffers.insert(buffer);
1807 QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1808 buffer, buffer->cloneBufferOnly());
1809 d.autosave_watcher_.setFuture(f);
1810 resetAutosaveTimers();
1811 }
1812
1813
resetAutosaveTimers()1814 void GuiView::resetAutosaveTimers()
1815 {
1816 if (lyxrc.autosave)
1817 d.autosave_timeout_.restart();
1818 }
1819
1820
getStatus(FuncRequest const & cmd,FuncStatus & flag)1821 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1822 {
1823 bool enable = true;
1824 Buffer * buf = currentBufferView()
1825 ? ¤tBufferView()->buffer() : 0;
1826 Buffer * doc_buffer = documentBufferView()
1827 ? &(documentBufferView()->buffer()) : 0;
1828
1829 #ifdef Q_OS_MAC
1830 /* In LyX/Mac, when a dialog is open, the menus of the
1831 application can still be accessed without giving focus to
1832 the main window. In this case, we want to disable the menu
1833 entries that are buffer-related.
1834 This code must not be used on Linux and Windows, since it
1835 would disable buffer-related entries when hovering over the
1836 menu (see bug #9574).
1837 */
1838 if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1839 buf = 0;
1840 doc_buffer = 0;
1841 }
1842 #endif
1843
1844 // Check whether we need a buffer
1845 if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1846 // no, exit directly
1847 flag.message(from_utf8(N_("Command not allowed with"
1848 "out any document open")));
1849 flag.setEnabled(false);
1850 return true;
1851 }
1852
1853 if (cmd.origin() == FuncRequest::TOC) {
1854 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1855 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1856 flag.setEnabled(false);
1857 return true;
1858 }
1859
1860 switch(cmd.action()) {
1861 case LFUN_BUFFER_IMPORT:
1862 break;
1863
1864 case LFUN_MASTER_BUFFER_EXPORT:
1865 enable = doc_buffer
1866 && (doc_buffer->parent() != 0
1867 || doc_buffer->hasChildren())
1868 && !d.processing_thread_watcher_.isRunning()
1869 // this launches a dialog, which would be in the wrong Buffer
1870 && !(::lyx::operator==(cmd.argument(), "custom"));
1871 break;
1872
1873 case LFUN_MASTER_BUFFER_UPDATE:
1874 case LFUN_MASTER_BUFFER_VIEW:
1875 enable = doc_buffer
1876 && (doc_buffer->parent() != 0
1877 || doc_buffer->hasChildren())
1878 && !d.processing_thread_watcher_.isRunning();
1879 break;
1880
1881 case LFUN_BUFFER_UPDATE:
1882 case LFUN_BUFFER_VIEW: {
1883 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1884 enable = false;
1885 break;
1886 }
1887 string format = to_utf8(cmd.argument());
1888 if (cmd.argument().empty())
1889 format = doc_buffer->params().getDefaultOutputFormat();
1890 enable = doc_buffer->params().isExportable(format, true);
1891 break;
1892 }
1893
1894 case LFUN_BUFFER_RELOAD:
1895 enable = doc_buffer && !doc_buffer->isUnnamed()
1896 && doc_buffer->fileName().exists()
1897 && (!doc_buffer->isClean() || doc_buffer->notifiesExternalModification());
1898 break;
1899
1900 case LFUN_BUFFER_CHILD_OPEN:
1901 enable = doc_buffer != 0;
1902 break;
1903
1904 case LFUN_BUFFER_WRITE:
1905 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1906 break;
1907
1908 //FIXME: This LFUN should be moved to GuiApplication.
1909 case LFUN_BUFFER_WRITE_ALL: {
1910 // We enable the command only if there are some modified buffers
1911 Buffer * first = theBufferList().first();
1912 enable = false;
1913 if (!first)
1914 break;
1915 Buffer * b = first;
1916 // We cannot use a for loop as the buffer list is a cycle.
1917 do {
1918 if (!b->isClean()) {
1919 enable = true;
1920 break;
1921 }
1922 b = theBufferList().next(b);
1923 } while (b != first);
1924 break;
1925 }
1926
1927 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
1928 enable = doc_buffer && doc_buffer->notifiesExternalModification();
1929 break;
1930
1931 case LFUN_BUFFER_EXPORT: {
1932 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1933 enable = false;
1934 break;
1935 }
1936 return doc_buffer->getStatus(cmd, flag);
1937 break;
1938 }
1939
1940 case LFUN_BUFFER_EXPORT_AS:
1941 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1942 enable = false;
1943 break;
1944 }
1945 // fall through
1946 case LFUN_BUFFER_WRITE_AS:
1947 enable = doc_buffer != 0;
1948 break;
1949
1950 case LFUN_BUFFER_CLOSE:
1951 case LFUN_VIEW_CLOSE:
1952 enable = doc_buffer != 0;
1953 break;
1954
1955 case LFUN_BUFFER_CLOSE_ALL:
1956 enable = theBufferList().last() != theBufferList().first();
1957 break;
1958
1959 case LFUN_BUFFER_CHKTEX: {
1960 // hide if we have no checktex command
1961 if (lyxrc.chktex_command.empty()) {
1962 flag.setUnknown(true);
1963 enable = false;
1964 break;
1965 }
1966 if (!doc_buffer || !doc_buffer->params().isLatex()
1967 || d.processing_thread_watcher_.isRunning()) {
1968 // grey out, don't hide
1969 enable = false;
1970 break;
1971 }
1972 enable = true;
1973 break;
1974 }
1975
1976 case LFUN_VIEW_SPLIT:
1977 if (cmd.getArg(0) == "vertical")
1978 enable = doc_buffer && (d.splitter_->count() == 1 ||
1979 d.splitter_->orientation() == Qt::Vertical);
1980 else
1981 enable = doc_buffer && (d.splitter_->count() == 1 ||
1982 d.splitter_->orientation() == Qt::Horizontal);
1983 break;
1984
1985 case LFUN_TAB_GROUP_CLOSE:
1986 enable = d.tabWorkAreaCount() > 1;
1987 break;
1988
1989 case LFUN_DEVEL_MODE_TOGGLE:
1990 flag.setOnOff(devel_mode_);
1991 break;
1992
1993 case LFUN_TOOLBAR_TOGGLE: {
1994 string const name = cmd.getArg(0);
1995 if (GuiToolbar * t = toolbar(name))
1996 flag.setOnOff(t->isVisible());
1997 else {
1998 enable = false;
1999 docstring const msg =
2000 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
2001 flag.message(msg);
2002 }
2003 break;
2004 }
2005
2006 case LFUN_TOOLBAR_MOVABLE: {
2007 string const name = cmd.getArg(0);
2008 // use negation since locked == !movable
2009 if (name == "*")
2010 // toolbar name * locks all toolbars
2011 flag.setOnOff(!toolbarsMovable_);
2012 else if (GuiToolbar * t = toolbar(name))
2013 flag.setOnOff(!(t->isMovable()));
2014 else {
2015 enable = false;
2016 docstring const msg =
2017 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
2018 flag.message(msg);
2019 }
2020 break;
2021 }
2022
2023 case LFUN_ICON_SIZE:
2024 flag.setOnOff(d.iconSize(cmd.argument()) == iconSize());
2025 break;
2026
2027 case LFUN_DROP_LAYOUTS_CHOICE:
2028 enable = buf != 0;
2029 break;
2030
2031 case LFUN_UI_TOGGLE:
2032 flag.setOnOff(isFullScreen());
2033 break;
2034
2035 case LFUN_DIALOG_DISCONNECT_INSET:
2036 break;
2037
2038 case LFUN_DIALOG_HIDE:
2039 // FIXME: should we check if the dialog is shown?
2040 break;
2041
2042 case LFUN_DIALOG_TOGGLE:
2043 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
2044 // to set "enable"
2045 // fall through
2046 case LFUN_DIALOG_SHOW: {
2047 string const name = cmd.getArg(0);
2048 if (!doc_buffer)
2049 enable = name == "aboutlyx"
2050 || name == "file" //FIXME: should be removed.
2051 || name == "prefs"
2052 || name == "texinfo"
2053 || name == "progress"
2054 || name == "compare";
2055 else if (name == "character" || name == "symbols"
2056 || name == "mathdelimiter" || name == "mathmatrix") {
2057 if (!buf || buf->isReadonly())
2058 enable = false;
2059 else {
2060 Cursor const & cur = currentBufferView()->cursor();
2061 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
2062 }
2063 }
2064 else if (name == "latexlog")
2065 enable = FileName(doc_buffer->logName()).isReadableFile();
2066 else if (name == "spellchecker")
2067 enable = theSpellChecker()
2068 && !doc_buffer->isReadonly()
2069 && !doc_buffer->text().empty();
2070 else if (name == "vclog")
2071 enable = doc_buffer->lyxvc().inUse();
2072 break;
2073 }
2074
2075 case LFUN_DIALOG_UPDATE: {
2076 string const name = cmd.getArg(0);
2077 if (!buf)
2078 enable = name == "prefs";
2079 break;
2080 }
2081
2082 case LFUN_COMMAND_EXECUTE:
2083 case LFUN_MESSAGE:
2084 case LFUN_MENU_OPEN:
2085 // Nothing to check.
2086 break;
2087
2088 case LFUN_COMPLETION_INLINE:
2089 if (!d.current_work_area_
2090 || !d.current_work_area_->completer().inlinePossible(
2091 currentBufferView()->cursor()))
2092 enable = false;
2093 break;
2094
2095 case LFUN_COMPLETION_POPUP:
2096 if (!d.current_work_area_
2097 || !d.current_work_area_->completer().popupPossible(
2098 currentBufferView()->cursor()))
2099 enable = false;
2100 break;
2101
2102 case LFUN_COMPLETE:
2103 if (!d.current_work_area_
2104 || !d.current_work_area_->completer().inlinePossible(
2105 currentBufferView()->cursor()))
2106 enable = false;
2107 break;
2108
2109 case LFUN_COMPLETION_ACCEPT:
2110 if (!d.current_work_area_
2111 || (!d.current_work_area_->completer().popupVisible()
2112 && !d.current_work_area_->completer().inlineVisible()
2113 && !d.current_work_area_->completer().completionAvailable()))
2114 enable = false;
2115 break;
2116
2117 case LFUN_COMPLETION_CANCEL:
2118 if (!d.current_work_area_
2119 || (!d.current_work_area_->completer().popupVisible()
2120 && !d.current_work_area_->completer().inlineVisible()))
2121 enable = false;
2122 break;
2123
2124 case LFUN_BUFFER_ZOOM_OUT:
2125 case LFUN_BUFFER_ZOOM_IN: {
2126 // only diff between these two is that the default for ZOOM_OUT
2127 // is a neg. number
2128 bool const neg_zoom =
2129 convert<int>(cmd.argument()) < 0 ||
2130 (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
2131 if (lyxrc.currentZoom <= zoom_min_ && neg_zoom) {
2132 docstring const msg =
2133 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2134 flag.message(msg);
2135 enable = false;
2136 } else
2137 enable = doc_buffer;
2138 break;
2139 }
2140
2141 case LFUN_BUFFER_ZOOM: {
2142 bool const less_than_min_zoom =
2143 !cmd.argument().empty() && convert<int>(cmd.argument()) < zoom_min_;
2144 if (lyxrc.currentZoom <= zoom_min_ && less_than_min_zoom) {
2145 docstring const msg =
2146 bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
2147 flag.message(msg);
2148 enable = false;
2149 }
2150 else
2151 enable = doc_buffer;
2152 break;
2153 }
2154
2155 case LFUN_BUFFER_MOVE_NEXT:
2156 case LFUN_BUFFER_MOVE_PREVIOUS:
2157 // we do not cycle when moving
2158 case LFUN_BUFFER_NEXT:
2159 case LFUN_BUFFER_PREVIOUS:
2160 // because we cycle, it doesn't matter whether on first or last
2161 enable = (d.currentTabWorkArea()->count() > 1);
2162 break;
2163 case LFUN_BUFFER_SWITCH:
2164 // toggle on the current buffer, but do not toggle off
2165 // the other ones (is that a good idea?)
2166 if (doc_buffer
2167 && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2168 flag.setOnOff(true);
2169 break;
2170
2171 case LFUN_VC_REGISTER:
2172 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2173 break;
2174 case LFUN_VC_RENAME:
2175 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2176 break;
2177 case LFUN_VC_COPY:
2178 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2179 break;
2180 case LFUN_VC_CHECK_IN:
2181 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2182 break;
2183 case LFUN_VC_CHECK_OUT:
2184 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2185 break;
2186 case LFUN_VC_LOCKING_TOGGLE:
2187 enable = doc_buffer && !doc_buffer->hasReadonlyFlag()
2188 && doc_buffer->lyxvc().lockingToggleEnabled();
2189 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2190 break;
2191 case LFUN_VC_REVERT:
2192 enable = doc_buffer && doc_buffer->lyxvc().inUse()
2193 && !doc_buffer->hasReadonlyFlag();
2194 break;
2195 case LFUN_VC_UNDO_LAST:
2196 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2197 break;
2198 case LFUN_VC_REPO_UPDATE:
2199 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2200 break;
2201 case LFUN_VC_COMMAND: {
2202 if (cmd.argument().empty())
2203 enable = false;
2204 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2205 enable = false;
2206 break;
2207 }
2208 case LFUN_VC_COMPARE:
2209 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2210 break;
2211
2212 case LFUN_SERVER_GOTO_FILE_ROW:
2213 case LFUN_LYX_ACTIVATE:
2214 break;
2215 case LFUN_FORWARD_SEARCH:
2216 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2217 break;
2218
2219 case LFUN_FILE_INSERT_PLAINTEXT:
2220 case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2221 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2222 break;
2223
2224 case LFUN_SPELLING_CONTINUOUSLY:
2225 flag.setOnOff(lyxrc.spellcheck_continuously);
2226 break;
2227
2228 default:
2229 return false;
2230 }
2231
2232 if (!enable)
2233 flag.setEnabled(false);
2234
2235 return true;
2236 }
2237
2238
selectTemplateFile()2239 static FileName selectTemplateFile()
2240 {
2241 FileDialog dlg(qt_("Select template file"));
2242 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2243 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2244
2245 FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2246 QStringList(qt_("LyX Documents (*.lyx)")));
2247
2248 if (result.first == FileDialog::Later)
2249 return FileName();
2250 if (result.second.isEmpty())
2251 return FileName();
2252 return FileName(fromqstr(result.second));
2253 }
2254
2255
loadDocument(FileName const & filename,bool tolastfiles)2256 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2257 {
2258 setBusy(true);
2259
2260 Buffer * newBuffer = 0;
2261 try {
2262 newBuffer = checkAndLoadLyXFile(filename);
2263 } catch (ExceptionMessage const & e) {
2264 setBusy(false);
2265 throw(e);
2266 }
2267 setBusy(false);
2268
2269 if (!newBuffer) {
2270 message(_("Document not loaded."));
2271 return 0;
2272 }
2273
2274 setBuffer(newBuffer);
2275 newBuffer->errors("Parse");
2276
2277 if (tolastfiles) {
2278 theSession().lastFiles().add(filename);
2279 theSession().writeFile();
2280 }
2281
2282 return newBuffer;
2283 }
2284
2285
openDocument(string const & fname)2286 void GuiView::openDocument(string const & fname)
2287 {
2288 string initpath = lyxrc.document_path;
2289
2290 if (documentBufferView()) {
2291 string const trypath = documentBufferView()->buffer().filePath();
2292 // If directory is writeable, use this as default.
2293 if (FileName(trypath).isDirWritable())
2294 initpath = trypath;
2295 }
2296
2297 string filename;
2298
2299 if (fname.empty()) {
2300 FileDialog dlg(qt_("Select document to open"));
2301 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2302 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2303
2304 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2305 FileDialog::Result result =
2306 dlg.open(toqstr(initpath), filter);
2307
2308 if (result.first == FileDialog::Later)
2309 return;
2310
2311 filename = fromqstr(result.second);
2312
2313 // check selected filename
2314 if (filename.empty()) {
2315 message(_("Canceled."));
2316 return;
2317 }
2318 } else
2319 filename = fname;
2320
2321 // get absolute path of file and add ".lyx" to the filename if
2322 // necessary.
2323 FileName const fullname =
2324 fileSearch(string(), filename, "lyx", support::may_not_exist);
2325 if (!fullname.empty())
2326 filename = fullname.absFileName();
2327
2328 if (!fullname.onlyPath().isDirectory()) {
2329 Alert::warning(_("Invalid filename"),
2330 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2331 from_utf8(fullname.absFileName())));
2332 return;
2333 }
2334
2335 // if the file doesn't exist and isn't already open (bug 6645),
2336 // let the user create one
2337 if (!fullname.exists() && !theBufferList().exists(fullname) &&
2338 !LyXVC::file_not_found_hook(fullname)) {
2339 // the user specifically chose this name. Believe him.
2340 Buffer * const b = newFile(filename, string(), true);
2341 if (b)
2342 setBuffer(b);
2343 return;
2344 }
2345
2346 docstring const disp_fn = makeDisplayPath(filename);
2347 message(bformat(_("Opening document %1$s..."), disp_fn));
2348
2349 docstring str2;
2350 Buffer * buf = loadDocument(fullname);
2351 if (buf) {
2352 str2 = bformat(_("Document %1$s opened."), disp_fn);
2353 if (buf->lyxvc().inUse())
2354 str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2355 " " + _("Version control detected.");
2356 } else {
2357 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2358 }
2359 message(str2);
2360 }
2361
2362 // FIXME: clean that
import(GuiView * lv,FileName const & filename,string const & format,ErrorList & errorList)2363 static bool import(GuiView * lv, FileName const & filename,
2364 string const & format, ErrorList & errorList)
2365 {
2366 FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2367
2368 string loader_format;
2369 vector<string> loaders = theConverters().loaders();
2370 if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2371 vector<string>::const_iterator it = loaders.begin();
2372 vector<string>::const_iterator en = loaders.end();
2373 for (; it != en; ++it) {
2374 if (!theConverters().isReachable(format, *it))
2375 continue;
2376
2377 string const tofile =
2378 support::changeExtension(filename.absFileName(),
2379 theFormats().extension(*it));
2380 if (!theConverters().convert(0, filename, FileName(tofile),
2381 filename, format, *it, errorList))
2382 return false;
2383 loader_format = *it;
2384 break;
2385 }
2386 if (loader_format.empty()) {
2387 frontend::Alert::error(_("Couldn't import file"),
2388 bformat(_("No information for importing the format %1$s."),
2389 theFormats().prettyName(format)));
2390 return false;
2391 }
2392 } else
2393 loader_format = format;
2394
2395 if (loader_format == "lyx") {
2396 Buffer * buf = lv->loadDocument(lyxfile);
2397 if (!buf)
2398 return false;
2399 } else {
2400 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2401 if (!b)
2402 return false;
2403 lv->setBuffer(b);
2404 bool as_paragraphs = loader_format == "textparagraph";
2405 string filename2 = (loader_format == format) ? filename.absFileName()
2406 : support::changeExtension(filename.absFileName(),
2407 theFormats().extension(loader_format));
2408 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2409 as_paragraphs);
2410 guiApp->setCurrentView(lv);
2411 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2412 }
2413
2414 return true;
2415 }
2416
2417
importDocument(string const & argument)2418 void GuiView::importDocument(string const & argument)
2419 {
2420 string format;
2421 string filename = split(argument, format, ' ');
2422
2423 LYXERR(Debug::INFO, format << " file: " << filename);
2424
2425 // need user interaction
2426 if (filename.empty()) {
2427 string initpath = lyxrc.document_path;
2428 if (documentBufferView()) {
2429 string const trypath = documentBufferView()->buffer().filePath();
2430 // If directory is writeable, use this as default.
2431 if (FileName(trypath).isDirWritable())
2432 initpath = trypath;
2433 }
2434
2435 docstring const text = bformat(_("Select %1$s file to import"),
2436 theFormats().prettyName(format));
2437
2438 FileDialog dlg(toqstr(text));
2439 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2440 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2441
2442 docstring filter = theFormats().prettyName(format);
2443 filter += " (*.{";
2444 // FIXME UNICODE
2445 filter += from_utf8(theFormats().extensions(format));
2446 filter += "})";
2447
2448 FileDialog::Result result =
2449 dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2450
2451 if (result.first == FileDialog::Later)
2452 return;
2453
2454 filename = fromqstr(result.second);
2455
2456 // check selected filename
2457 if (filename.empty())
2458 message(_("Canceled."));
2459 }
2460
2461 if (filename.empty())
2462 return;
2463
2464 // get absolute path of file
2465 FileName const fullname(support::makeAbsPath(filename));
2466
2467 // Can happen if the user entered a path into the dialog
2468 // (see bug #7437)
2469 if (fullname.onlyFileName().empty()) {
2470 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2471 "Aborting import."),
2472 from_utf8(fullname.absFileName()));
2473 frontend::Alert::error(_("File name error"), msg);
2474 message(_("Canceled."));
2475 return;
2476 }
2477
2478
2479 FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2480
2481 // Check if the document already is open
2482 Buffer * buf = theBufferList().getBuffer(lyxfile);
2483 if (buf) {
2484 setBuffer(buf);
2485 if (!closeBuffer()) {
2486 message(_("Canceled."));
2487 return;
2488 }
2489 }
2490
2491 docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2492
2493 // if the file exists already, and we didn't do
2494 // -i lyx thefile.lyx, warn
2495 if (lyxfile.exists() && fullname != lyxfile) {
2496
2497 docstring text = bformat(_("The document %1$s already exists.\n\n"
2498 "Do you want to overwrite that document?"), displaypath);
2499 int const ret = Alert::prompt(_("Overwrite document?"),
2500 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2501
2502 if (ret == 1) {
2503 message(_("Canceled."));
2504 return;
2505 }
2506 }
2507
2508 message(bformat(_("Importing %1$s..."), displaypath));
2509 ErrorList errorList;
2510 if (import(this, fullname, format, errorList))
2511 message(_("imported."));
2512 else
2513 message(_("file not imported!"));
2514
2515 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2516 }
2517
2518
newDocument(string const & filename,bool from_template)2519 void GuiView::newDocument(string const & filename, bool from_template)
2520 {
2521 FileName initpath(lyxrc.document_path);
2522 if (documentBufferView()) {
2523 FileName const trypath(documentBufferView()->buffer().filePath());
2524 // If directory is writeable, use this as default.
2525 if (trypath.isDirWritable())
2526 initpath = trypath;
2527 }
2528
2529 string templatefile;
2530 if (from_template) {
2531 templatefile = selectTemplateFile().absFileName();
2532 if (templatefile.empty())
2533 return;
2534 }
2535
2536 Buffer * b;
2537 if (filename.empty())
2538 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2539 else
2540 b = newFile(filename, templatefile, true);
2541
2542 if (b)
2543 setBuffer(b);
2544
2545 // If no new document could be created, it is unsure
2546 // whether there is a valid BufferView.
2547 if (currentBufferView())
2548 // Ensure the cursor is correctly positioned on screen.
2549 currentBufferView()->showCursor();
2550 }
2551
2552
insertLyXFile(docstring const & fname)2553 void GuiView::insertLyXFile(docstring const & fname)
2554 {
2555 BufferView * bv = documentBufferView();
2556 if (!bv)
2557 return;
2558
2559 // FIXME UNICODE
2560 FileName filename(to_utf8(fname));
2561 if (filename.empty()) {
2562 // Launch a file browser
2563 // FIXME UNICODE
2564 string initpath = lyxrc.document_path;
2565 string const trypath = bv->buffer().filePath();
2566 // If directory is writeable, use this as default.
2567 if (FileName(trypath).isDirWritable())
2568 initpath = trypath;
2569
2570 // FIXME UNICODE
2571 FileDialog dlg(qt_("Select LyX document to insert"));
2572 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2573 dlg.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
2574
2575 FileDialog::Result result = dlg.open(toqstr(initpath),
2576 QStringList(qt_("LyX Documents (*.lyx)")));
2577
2578 if (result.first == FileDialog::Later)
2579 return;
2580
2581 // FIXME UNICODE
2582 filename.set(fromqstr(result.second));
2583
2584 // check selected filename
2585 if (filename.empty()) {
2586 // emit message signal.
2587 message(_("Canceled."));
2588 return;
2589 }
2590 }
2591
2592 bv->insertLyXFile(filename);
2593 bv->buffer().errors("Parse");
2594 }
2595
2596
renameBuffer(Buffer & b,docstring const & newname,RenameKind kind)2597 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2598 {
2599 FileName fname = b.fileName();
2600 FileName const oldname = fname;
2601
2602 if (!newname.empty()) {
2603 // FIXME UNICODE
2604 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2605 } else {
2606 // Switch to this Buffer.
2607 setBuffer(&b);
2608
2609 // No argument? Ask user through dialog.
2610 // FIXME UNICODE
2611 FileDialog dlg(qt_("Choose a filename to save document as"));
2612 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2613 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2614
2615 if (!isLyXFileName(fname.absFileName()))
2616 fname.changeExtension(".lyx");
2617
2618 FileDialog::Result result =
2619 dlg.save(toqstr(fname.onlyPath().absFileName()),
2620 QStringList(qt_("LyX Documents (*.lyx)")),
2621 toqstr(fname.onlyFileName()));
2622
2623 if (result.first == FileDialog::Later)
2624 return false;
2625
2626 fname.set(fromqstr(result.second));
2627
2628 if (fname.empty())
2629 return false;
2630
2631 if (!isLyXFileName(fname.absFileName()))
2632 fname.changeExtension(".lyx");
2633 }
2634
2635 // fname is now the new Buffer location.
2636
2637 // if there is already a Buffer open with this name, we do not want
2638 // to have another one. (the second test makes sure we're not just
2639 // trying to overwrite ourselves, which is fine.)
2640 if (theBufferList().exists(fname) && fname != oldname
2641 && theBufferList().getBuffer(fname) != &b) {
2642 docstring const text =
2643 bformat(_("The file\n%1$s\nis already open in your current session.\n"
2644 "Please close it before attempting to overwrite it.\n"
2645 "Do you want to choose a new filename?"),
2646 from_utf8(fname.absFileName()));
2647 int const ret = Alert::prompt(_("Chosen File Already Open"),
2648 text, 0, 1, _("&Rename"), _("&Cancel"));
2649 switch (ret) {
2650 case 0: return renameBuffer(b, docstring(), kind);
2651 case 1: return false;
2652 }
2653 //return false;
2654 }
2655
2656 bool const existsLocal = fname.exists();
2657 bool const existsInVC = LyXVC::fileInVC(fname);
2658 if (existsLocal || existsInVC) {
2659 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2660 if (kind != LV_WRITE_AS && existsInVC) {
2661 // renaming to a name that is already in VC
2662 // would not work
2663 docstring text = bformat(_("The document %1$s "
2664 "is already registered.\n\n"
2665 "Do you want to choose a new name?"),
2666 file);
2667 docstring const title = (kind == LV_VC_RENAME) ?
2668 _("Rename document?") : _("Copy document?");
2669 docstring const button = (kind == LV_VC_RENAME) ?
2670 _("&Rename") : _("&Copy");
2671 int const ret = Alert::prompt(title, text, 0, 1,
2672 button, _("&Cancel"));
2673 switch (ret) {
2674 case 0: return renameBuffer(b, docstring(), kind);
2675 case 1: return false;
2676 }
2677 }
2678
2679 if (existsLocal) {
2680 docstring text = bformat(_("The document %1$s "
2681 "already exists.\n\n"
2682 "Do you want to overwrite that document?"),
2683 file);
2684 int const ret = Alert::prompt(_("Overwrite document?"),
2685 text, 0, 2, _("&Overwrite"),
2686 _("&Rename"), _("&Cancel"));
2687 switch (ret) {
2688 case 0: break;
2689 case 1: return renameBuffer(b, docstring(), kind);
2690 case 2: return false;
2691 }
2692 }
2693 }
2694
2695 switch (kind) {
2696 case LV_VC_RENAME: {
2697 string msg = b.lyxvc().rename(fname);
2698 if (msg.empty())
2699 return false;
2700 message(from_utf8(msg));
2701 break;
2702 }
2703 case LV_VC_COPY: {
2704 string msg = b.lyxvc().copy(fname);
2705 if (msg.empty())
2706 return false;
2707 message(from_utf8(msg));
2708 break;
2709 }
2710 case LV_WRITE_AS:
2711 break;
2712 }
2713 // LyXVC created the file already in case of LV_VC_RENAME or
2714 // LV_VC_COPY, but call saveBuffer() nevertheless to get
2715 // relative paths of included stuff right if we moved e.g. from
2716 // /a/b.lyx to /a/c/b.lyx.
2717
2718 bool const saved = saveBuffer(b, fname);
2719 if (saved)
2720 b.reload();
2721 return saved;
2722 }
2723
2724
exportBufferAs(Buffer & b,docstring const & iformat)2725 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2726 {
2727 FileName fname = b.fileName();
2728
2729 FileDialog dlg(qt_("Choose a filename to export the document as"));
2730 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2731
2732 QStringList types;
2733 QString const anyformat = qt_("Guess from extension (*.*)");
2734 types << anyformat;
2735
2736 vector<Format const *> export_formats;
2737 for (Format const & f : theFormats())
2738 if (f.documentFormat())
2739 export_formats.push_back(&f);
2740 sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
2741 map<QString, string> fmap;
2742 QString filter;
2743 string ext;
2744 for (Format const * f : export_formats) {
2745 docstring const loc_prettyname = translateIfPossible(f->prettyname());
2746 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2747 loc_prettyname,
2748 from_ascii(f->extension())));
2749 types << loc_filter;
2750 fmap[loc_filter] = f->name();
2751 if (from_ascii(f->name()) == iformat) {
2752 filter = loc_filter;
2753 ext = f->extension();
2754 }
2755 }
2756 string ofname = fname.onlyFileName();
2757 if (!ext.empty())
2758 ofname = support::changeExtension(ofname, ext);
2759 FileDialog::Result result =
2760 dlg.save(toqstr(fname.onlyPath().absFileName()),
2761 types,
2762 toqstr(ofname),
2763 &filter);
2764 if (result.first != FileDialog::Chosen)
2765 return false;
2766
2767 string fmt_name;
2768 fname.set(fromqstr(result.second));
2769 if (filter == anyformat)
2770 fmt_name = theFormats().getFormatFromExtension(fname.extension());
2771 else
2772 fmt_name = fmap[filter];
2773 LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2774 << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2775
2776 if (fmt_name.empty() || fname.empty())
2777 return false;
2778
2779 // fname is now the new Buffer location.
2780 if (FileName(fname).exists()) {
2781 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2782 docstring text = bformat(_("The document %1$s already "
2783 "exists.\n\nDo you want to "
2784 "overwrite that document?"),
2785 file);
2786 int const ret = Alert::prompt(_("Overwrite document?"),
2787 text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2788 switch (ret) {
2789 case 0: break;
2790 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2791 case 2: return false;
2792 }
2793 }
2794
2795 FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2796 DispatchResult dr;
2797 dispatch(cmd, dr);
2798 return dr.dispatched();
2799 }
2800
2801
saveBuffer(Buffer & b)2802 bool GuiView::saveBuffer(Buffer & b)
2803 {
2804 return saveBuffer(b, FileName());
2805 }
2806
2807
saveBuffer(Buffer & b,FileName const & fn)2808 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2809 {
2810 if (workArea(b) && workArea(b)->inDialogMode())
2811 return true;
2812
2813 if (fn.empty() && b.isUnnamed())
2814 return renameBuffer(b, docstring());
2815
2816 bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2817 if (success) {
2818 theSession().lastFiles().add(b.fileName());
2819 theSession().writeFile();
2820 return true;
2821 }
2822
2823 // Switch to this Buffer.
2824 setBuffer(&b);
2825
2826 // FIXME: we don't tell the user *WHY* the save failed !!
2827 docstring const file = makeDisplayPath(b.absFileName(), 30);
2828 docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2829 "Do you want to rename the document and "
2830 "try again?"), file);
2831 int const ret = Alert::prompt(_("Rename and save?"),
2832 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2833 switch (ret) {
2834 case 0:
2835 if (!renameBuffer(b, docstring()))
2836 return false;
2837 break;
2838 case 1:
2839 break;
2840 case 2:
2841 return false;
2842 }
2843
2844 return saveBuffer(b, fn);
2845 }
2846
2847
hideWorkArea(GuiWorkArea * wa)2848 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2849 {
2850 return closeWorkArea(wa, false);
2851 }
2852
2853
2854 // We only want to close the buffer if it is not visible in other workareas
2855 // of the same view, nor in other views, and if this is not a child
closeWorkArea(GuiWorkArea * wa)2856 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2857 {
2858 Buffer & buf = wa->bufferView().buffer();
2859
2860 bool last_wa = d.countWorkAreasOf(buf) == 1
2861 && !inOtherView(buf) && !buf.parent();
2862
2863 bool close_buffer = last_wa;
2864
2865 if (last_wa) {
2866 if (lyxrc.close_buffer_with_last_view == "yes")
2867 ; // Nothing to do
2868 else if (lyxrc.close_buffer_with_last_view == "no")
2869 close_buffer = false;
2870 else {
2871 docstring file;
2872 if (buf.isUnnamed())
2873 file = from_utf8(buf.fileName().onlyFileName());
2874 else
2875 file = buf.fileName().displayName(30);
2876 docstring const text = bformat(
2877 _("Last view on document %1$s is being closed.\n"
2878 "Would you like to close or hide the document?\n"
2879 "\n"
2880 "Hidden documents can be displayed back through\n"
2881 "the menu: View->Hidden->...\n"
2882 "\n"
2883 "To remove this question, set your preference in:\n"
2884 " Tools->Preferences->Look&Feel->UserInterface\n"
2885 ), file);
2886 int ret = Alert::prompt(_("Close or hide document?"),
2887 text, 0, 1, _("&Close"), _("&Hide"));
2888 close_buffer = (ret == 0);
2889 }
2890 }
2891
2892 return closeWorkArea(wa, close_buffer);
2893 }
2894
2895
closeBuffer()2896 bool GuiView::closeBuffer()
2897 {
2898 GuiWorkArea * wa = currentMainWorkArea();
2899 // coverity complained about this
2900 // it seems unnecessary, but perhaps is worth the check
2901 LASSERT(wa, return false);
2902
2903 setCurrentWorkArea(wa);
2904 Buffer & buf = wa->bufferView().buffer();
2905 return closeWorkArea(wa, !buf.parent());
2906 }
2907
2908
writeSession() const2909 void GuiView::writeSession() const {
2910 GuiWorkArea const * active_wa = currentMainWorkArea();
2911 for (int i = 0; i < d.splitter_->count(); ++i) {
2912 TabWorkArea * twa = d.tabWorkArea(i);
2913 for (int j = 0; j < twa->count(); ++j) {
2914 GuiWorkArea * wa = twa->workArea(j);
2915 Buffer & buf = wa->bufferView().buffer();
2916 theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2917 }
2918 }
2919 }
2920
2921
closeBufferAll()2922 bool GuiView::closeBufferAll()
2923 {
2924 // Close the workareas in all other views
2925 QList<int> const ids = guiApp->viewIds();
2926 for (int i = 0; i != ids.size(); ++i) {
2927 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2928 return false;
2929 }
2930
2931 // Close our own workareas
2932 if (!closeWorkAreaAll())
2933 return false;
2934
2935 // Now close the hidden buffers. We prevent hidden buffers from being
2936 // dirty, so we can just close them.
2937 theBufferList().closeAll();
2938 return true;
2939 }
2940
2941
closeWorkAreaAll()2942 bool GuiView::closeWorkAreaAll()
2943 {
2944 setCurrentWorkArea(currentMainWorkArea());
2945
2946 // We might be in a situation that there is still a tabWorkArea, but
2947 // there are no tabs anymore. This can happen when we get here after a
2948 // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2949 // many TabWorkArea's have no documents anymore.
2950 int empty_twa = 0;
2951
2952 // We have to call count() each time, because it can happen that
2953 // more than one splitter will disappear in one iteration (bug 5998).
2954 while (d.splitter_->count() > empty_twa) {
2955 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2956
2957 if (twa->count() == 0)
2958 ++empty_twa;
2959 else {
2960 setCurrentWorkArea(twa->currentWorkArea());
2961 if (!closeTabWorkArea(twa))
2962 return false;
2963 }
2964 }
2965 return true;
2966 }
2967
2968
closeWorkArea(GuiWorkArea * wa,bool close_buffer)2969 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2970 {
2971 if (!wa)
2972 return false;
2973
2974 Buffer & buf = wa->bufferView().buffer();
2975
2976 if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2977 Alert::warning(_("Close document"),
2978 _("Document could not be closed because it is being processed by LyX."));
2979 return false;
2980 }
2981
2982 if (close_buffer)
2983 return closeBuffer(buf);
2984 else {
2985 if (!inMultiTabs(wa))
2986 if (!saveBufferIfNeeded(buf, true))
2987 return false;
2988 removeWorkArea(wa);
2989 return true;
2990 }
2991 }
2992
2993
closeBuffer(Buffer & buf)2994 bool GuiView::closeBuffer(Buffer & buf)
2995 {
2996 bool success = true;
2997 ListOfBuffers clist = buf.getChildren();
2998 ListOfBuffers::const_iterator it = clist.begin();
2999 ListOfBuffers::const_iterator const bend = clist.end();
3000 for (; it != bend; ++it) {
3001 Buffer * child_buf = *it;
3002 if (theBufferList().isOthersChild(&buf, child_buf)) {
3003 child_buf->setParent(0);
3004 continue;
3005 }
3006
3007 // FIXME: should we look in other tabworkareas?
3008 // ANSWER: I don't think so. I've tested, and if the child is
3009 // open in some other window, it closes without a problem.
3010 GuiWorkArea * child_wa = workArea(*child_buf);
3011 if (child_wa) {
3012 if (closing_)
3013 // If we are in a close_event all children will be closed in some time,
3014 // so no need to do it here. This will ensure that the children end up
3015 // in the session file in the correct order. If we close the master
3016 // buffer, we can close or release the child buffers here too.
3017 continue;
3018 success = closeWorkArea(child_wa, true);
3019 if (!success)
3020 break;
3021 } else {
3022 // In this case the child buffer is open but hidden.
3023 // Even in this case, children can be dirty (e.g.,
3024 // after a label change in the master, see #11405).
3025 // Therefore, check this
3026 if (closing_ && (child_buf->isClean() || child_buf->paragraphs().empty()))
3027 // If we are in a close_event all children will be closed in some time,
3028 // so no need to do it here. This will ensure that the children end up
3029 // in the session file in the correct order. If we close the master
3030 // buffer, we can close or release the child buffers here too.
3031 continue;
3032 // Save dirty buffers also if closing_!
3033 if (saveBufferIfNeeded(*child_buf, false)) {
3034 child_buf->removeAutosaveFile();
3035 theBufferList().release(child_buf);
3036 } else {
3037 // Saving of dirty children has been cancelled.
3038 // Cancel the whole process.
3039 success = false;
3040 break;
3041 }
3042 }
3043 }
3044 if (success) {
3045 // goto bookmark to update bookmark pit.
3046 // FIXME: we should update only the bookmarks related to this buffer!
3047 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
3048 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
3049 guiApp->gotoBookmark(i+1, false, false);
3050
3051 if (saveBufferIfNeeded(buf, false)) {
3052 buf.removeAutosaveFile();
3053 theBufferList().release(&buf);
3054 return true;
3055 }
3056 }
3057 // open all children again to avoid a crash because of dangling
3058 // pointers (bug 6603)
3059 buf.updateBuffer();
3060 return false;
3061 }
3062
3063
closeTabWorkArea(TabWorkArea * twa)3064 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
3065 {
3066 while (twa == d.currentTabWorkArea()) {
3067 twa->setCurrentIndex(twa->count() - 1);
3068
3069 GuiWorkArea * wa = twa->currentWorkArea();
3070 Buffer & b = wa->bufferView().buffer();
3071
3072 // We only want to close the buffer if the same buffer is not visible
3073 // in another view, and if this is not a child and if we are closing
3074 // a view (not a tabgroup).
3075 bool const close_buffer =
3076 !inOtherView(b) && !b.parent() && closing_;
3077
3078 if (!closeWorkArea(wa, close_buffer))
3079 return false;
3080 }
3081 return true;
3082 }
3083
3084
saveBufferIfNeeded(Buffer & buf,bool hiding)3085 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
3086 {
3087 if (buf.isClean() || buf.paragraphs().empty())
3088 return true;
3089
3090 // Switch to this Buffer.
3091 setBuffer(&buf);
3092
3093 docstring file;
3094 bool exists;
3095 // FIXME: Unicode?
3096 if (buf.isUnnamed()) {
3097 file = from_utf8(buf.fileName().onlyFileName());
3098 exists = false;
3099 } else {
3100 FileName filename = buf.fileName();
3101 filename.refresh();
3102 file = filename.displayName(30);
3103 exists = filename.exists();
3104 }
3105
3106 // Bring this window to top before asking questions.
3107 raise();
3108 activateWindow();
3109
3110 int ret;
3111 if (hiding && buf.isUnnamed()) {
3112 docstring const text = bformat(_("The document %1$s has not been "
3113 "saved yet.\n\nDo you want to save "
3114 "the document?"), file);
3115 ret = Alert::prompt(_("Save new document?"),
3116 text, 0, 1, _("&Save"), _("&Cancel"));
3117 if (ret == 1)
3118 ++ret;
3119 } else {
3120 docstring const text = exists ?
3121 bformat(_("The document %1$s has unsaved changes."
3122 "\n\nDo you want to save the document or "
3123 "discard the changes?"), file) :
3124 bformat(_("The document %1$s has not been saved yet."
3125 "\n\nDo you want to save the document or "
3126 "discard it entirely?"), file);
3127 docstring const title = exists ?
3128 _("Save changed document?") : _("Save document?");
3129 ret = Alert::prompt(title, text, 0, 2,
3130 _("&Save"), _("&Discard"), _("&Cancel"));
3131 }
3132
3133 switch (ret) {
3134 case 0:
3135 if (!saveBuffer(buf))
3136 return false;
3137 break;
3138 case 1:
3139 // If we crash after this we could have no autosave file
3140 // but I guess this is really improbable (Jug).
3141 // Sometimes improbable things happen:
3142 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
3143 // buf.removeAutosaveFile();
3144 if (hiding)
3145 // revert all changes
3146 reloadBuffer(buf);
3147 buf.markClean();
3148 break;
3149 case 2:
3150 return false;
3151 }
3152 return true;
3153 }
3154
3155
inMultiTabs(GuiWorkArea * wa)3156 bool GuiView::inMultiTabs(GuiWorkArea * wa)
3157 {
3158 Buffer & buf = wa->bufferView().buffer();
3159
3160 for (int i = 0; i != d.splitter_->count(); ++i) {
3161 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
3162 if (wa_ && wa_ != wa)
3163 return true;
3164 }
3165 return inOtherView(buf);
3166 }
3167
3168
inOtherView(Buffer & buf)3169 bool GuiView::inOtherView(Buffer & buf)
3170 {
3171 QList<int> const ids = guiApp->viewIds();
3172
3173 for (int i = 0; i != ids.size(); ++i) {
3174 if (id_ == ids[i])
3175 continue;
3176
3177 if (guiApp->view(ids[i]).workArea(buf))
3178 return true;
3179 }
3180 return false;
3181 }
3182
3183
gotoNextOrPreviousBuffer(NextOrPrevious np,bool const move)3184 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3185 {
3186 if (!documentBufferView())
3187 return;
3188
3189 if (TabWorkArea * twa = d.currentTabWorkArea()) {
3190 Buffer * const curbuf = &documentBufferView()->buffer();
3191 int nwa = twa->count();
3192 for (int i = 0; i < nwa; ++i) {
3193 if (&workArea(i)->bufferView().buffer() == curbuf) {
3194 int next_index;
3195 if (np == NEXTBUFFER)
3196 next_index = (i == nwa - 1 ? 0 : i + 1);
3197 else
3198 next_index = (i == 0 ? nwa - 1 : i - 1);
3199 if (move)
3200 twa->moveTab(i, next_index);
3201 else
3202 setBuffer(&workArea(next_index)->bufferView().buffer());
3203 break;
3204 }
3205 }
3206 }
3207 }
3208
3209
3210 /// make sure the document is saved
ensureBufferClean(Buffer * buffer)3211 static bool ensureBufferClean(Buffer * buffer)
3212 {
3213 LASSERT(buffer, return false);
3214 if (buffer->isClean() && !buffer->isUnnamed())
3215 return true;
3216
3217 docstring const file = buffer->fileName().displayName(30);
3218 docstring title;
3219 docstring text;
3220 if (!buffer->isUnnamed()) {
3221 text = bformat(_("The document %1$s has unsaved "
3222 "changes.\n\nDo you want to save "
3223 "the document?"), file);
3224 title = _("Save changed document?");
3225
3226 } else {
3227 text = bformat(_("The document %1$s has not been "
3228 "saved yet.\n\nDo you want to save "
3229 "the document?"), file);
3230 title = _("Save new document?");
3231 }
3232 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3233
3234 if (ret == 0)
3235 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3236
3237 return buffer->isClean() && !buffer->isUnnamed();
3238 }
3239
3240
reloadBuffer(Buffer & buf)3241 bool GuiView::reloadBuffer(Buffer & buf)
3242 {
3243 currentBufferView()->cursor().reset();
3244 Buffer::ReadStatus status = buf.reload();
3245 return status == Buffer::ReadSuccess;
3246 }
3247
3248
checkExternallyModifiedBuffers()3249 void GuiView::checkExternallyModifiedBuffers()
3250 {
3251 BufferList::iterator bit = theBufferList().begin();
3252 BufferList::iterator const bend = theBufferList().end();
3253 for (; bit != bend; ++bit) {
3254 Buffer * buf = *bit;
3255 if (buf->fileName().exists() && buf->isChecksumModified()) {
3256 docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3257 " Reload now? Any local changes will be lost."),
3258 from_utf8(buf->absFileName()));
3259 int const ret = Alert::prompt(_("Reload externally changed document?"),
3260 text, 0, 1, _("&Reload"), _("&Cancel"));
3261 if (!ret)
3262 reloadBuffer(*buf);
3263 }
3264 }
3265 }
3266
3267
dispatchVC(FuncRequest const & cmd,DispatchResult & dr)3268 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3269 {
3270 Buffer * buffer = documentBufferView()
3271 ? &(documentBufferView()->buffer()) : 0;
3272
3273 switch (cmd.action()) {
3274 case LFUN_VC_REGISTER:
3275 if (!buffer || !ensureBufferClean(buffer))
3276 break;
3277 if (!buffer->lyxvc().inUse()) {
3278 if (buffer->lyxvc().registrer()) {
3279 reloadBuffer(*buffer);
3280 dr.clearMessageUpdate();
3281 }
3282 }
3283 break;
3284
3285 case LFUN_VC_RENAME:
3286 case LFUN_VC_COPY: {
3287 if (!buffer || !ensureBufferClean(buffer))
3288 break;
3289 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3290 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3291 // Some changes are not yet committed.
3292 // We test here and not in getStatus(), since
3293 // this test is expensive.
3294 string log;
3295 LyXVC::CommandResult ret =
3296 buffer->lyxvc().checkIn(log);
3297 dr.setMessage(log);
3298 if (ret == LyXVC::ErrorCommand ||
3299 ret == LyXVC::VCSuccess)
3300 reloadBuffer(*buffer);
3301 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3302 frontend::Alert::error(
3303 _("Revision control error."),
3304 _("Document could not be checked in."));
3305 break;
3306 }
3307 }
3308 RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3309 LV_VC_RENAME : LV_VC_COPY;
3310 renameBuffer(*buffer, cmd.argument(), kind);
3311 }
3312 break;
3313 }
3314
3315 case LFUN_VC_CHECK_IN:
3316 if (!buffer || !ensureBufferClean(buffer))
3317 break;
3318 if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
3319 string log;
3320 LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3321 dr.setMessage(log);
3322 // Only skip reloading if the checkin was cancelled or
3323 // an error occurred before the real checkin VCS command
3324 // was executed, since the VCS might have changed the
3325 // file even if it could not checkin successfully.
3326 if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3327 reloadBuffer(*buffer);
3328 }
3329 break;
3330
3331 case LFUN_VC_CHECK_OUT:
3332 if (!buffer || !ensureBufferClean(buffer))
3333 break;
3334 if (buffer->lyxvc().inUse()) {
3335 dr.setMessage(buffer->lyxvc().checkOut());
3336 reloadBuffer(*buffer);
3337 }
3338 break;
3339
3340 case LFUN_VC_LOCKING_TOGGLE:
3341 LASSERT(buffer, return);
3342 if (!ensureBufferClean(buffer) || buffer->hasReadonlyFlag())
3343 break;
3344 if (buffer->lyxvc().inUse()) {
3345 string res = buffer->lyxvc().lockingToggle();
3346 if (res.empty()) {
3347 frontend::Alert::error(_("Revision control error."),
3348 _("Error when setting the locking property."));
3349 } else {
3350 dr.setMessage(res);
3351 reloadBuffer(*buffer);
3352 }
3353 }
3354 break;
3355
3356 case LFUN_VC_REVERT:
3357 LASSERT(buffer, return);
3358 if (buffer->lyxvc().revert()) {
3359 reloadBuffer(*buffer);
3360 dr.clearMessageUpdate();
3361 }
3362 break;
3363
3364 case LFUN_VC_UNDO_LAST:
3365 LASSERT(buffer, return);
3366 buffer->lyxvc().undoLast();
3367 reloadBuffer(*buffer);
3368 dr.clearMessageUpdate();
3369 break;
3370
3371 case LFUN_VC_REPO_UPDATE:
3372 LASSERT(buffer, return);
3373 if (ensureBufferClean(buffer)) {
3374 dr.setMessage(buffer->lyxvc().repoUpdate());
3375 checkExternallyModifiedBuffers();
3376 }
3377 break;
3378
3379 case LFUN_VC_COMMAND: {
3380 string flag = cmd.getArg(0);
3381 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3382 break;
3383 docstring message;
3384 if (contains(flag, 'M')) {
3385 if (!Alert::askForText(message, _("LyX VC: Log Message")))
3386 break;
3387 }
3388 string path = cmd.getArg(1);
3389 if (contains(path, "$$p") && buffer)
3390 path = subst(path, "$$p", buffer->filePath());
3391 LYXERR(Debug::LYXVC, "Directory: " << path);
3392 FileName pp(path);
3393 if (!pp.isReadableDirectory()) {
3394 lyxerr << _("Directory is not accessible.") << endl;
3395 break;
3396 }
3397 support::PathChanger p(pp);
3398
3399 string command = cmd.getArg(2);
3400 if (command.empty())
3401 break;
3402 if (buffer) {
3403 command = subst(command, "$$i", buffer->absFileName());
3404 command = subst(command, "$$p", buffer->filePath());
3405 }
3406 command = subst(command, "$$m", to_utf8(message));
3407 LYXERR(Debug::LYXVC, "Command: " << command);
3408 Systemcall one;
3409 one.startscript(Systemcall::Wait, command);
3410
3411 if (!buffer)
3412 break;
3413 if (contains(flag, 'I'))
3414 buffer->markDirty();
3415 if (contains(flag, 'R'))
3416 reloadBuffer(*buffer);
3417
3418 break;
3419 }
3420
3421 case LFUN_VC_COMPARE: {
3422 if (cmd.argument().empty()) {
3423 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3424 break;
3425 }
3426
3427 string rev1 = cmd.getArg(0);
3428 string f1, f2;
3429 LATTEST(buffer)
3430
3431 // f1
3432 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3433 break;
3434
3435 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3436 f2 = buffer->absFileName();
3437 } else {
3438 string rev2 = cmd.getArg(1);
3439 if (rev2.empty())
3440 break;
3441 // f2
3442 if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3443 break;
3444 }
3445
3446 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3447 f1 << "\n" << f2 << "\n" );
3448 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3449 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3450 break;
3451 }
3452
3453 default:
3454 break;
3455 }
3456 }
3457
3458
openChildDocument(string const & fname)3459 void GuiView::openChildDocument(string const & fname)
3460 {
3461 LASSERT(documentBufferView(), return);
3462 Buffer & buffer = documentBufferView()->buffer();
3463 FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3464 documentBufferView()->saveBookmark(false);
3465 Buffer * child = 0;
3466 if (theBufferList().exists(filename)) {
3467 child = theBufferList().getBuffer(filename);
3468 setBuffer(child);
3469 } else {
3470 message(bformat(_("Opening child document %1$s..."),
3471 makeDisplayPath(filename.absFileName())));
3472 child = loadDocument(filename, false);
3473 }
3474 // Set the parent name of the child document.
3475 // This makes insertion of citations and references in the child work,
3476 // when the target is in the parent or another child document.
3477 if (child)
3478 child->setParent(&buffer);
3479 }
3480
3481
goToFileRow(string const & argument)3482 bool GuiView::goToFileRow(string const & argument)
3483 {
3484 string file_name;
3485 int row;
3486 size_t i = argument.find_last_of(' ');
3487 if (i != string::npos) {
3488 file_name = os::internal_path(trim(argument.substr(0, i)));
3489 istringstream is(argument.substr(i + 1));
3490 is >> row;
3491 if (is.fail())
3492 i = string::npos;
3493 }
3494 if (i == string::npos) {
3495 LYXERR0("Wrong argument: " << argument);
3496 return false;
3497 }
3498 Buffer * buf = 0;
3499 string const abstmp = package().temp_dir().absFileName();
3500 string const realtmp = package().temp_dir().realPath();
3501 // We have to use os::path_prefix_is() here, instead of
3502 // simply prefixIs(), because the file name comes from
3503 // an external application and may need case adjustment.
3504 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3505 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3506 // Needed by inverse dvi search. If it is a file
3507 // in tmpdir, call the apropriated function.
3508 // If tmpdir is a symlink, we may have the real
3509 // path passed back, so we correct for that.
3510 if (!prefixIs(file_name, abstmp))
3511 file_name = subst(file_name, realtmp, abstmp);
3512 buf = theBufferList().getBufferFromTmp(file_name);
3513 } else {
3514 // Must replace extension of the file to be .lyx
3515 // and get full path
3516 FileName const s = fileSearch(string(),
3517 support::changeExtension(file_name, ".lyx"), "lyx");
3518 // Either change buffer or load the file
3519 if (theBufferList().exists(s))
3520 buf = theBufferList().getBuffer(s);
3521 else if (s.exists()) {
3522 buf = loadDocument(s);
3523 if (!buf)
3524 return false;
3525 } else {
3526 message(bformat(
3527 _("File does not exist: %1$s"),
3528 makeDisplayPath(file_name)));
3529 return false;
3530 }
3531 }
3532 if (!buf) {
3533 message(bformat(
3534 _("No buffer for file: %1$s."),
3535 makeDisplayPath(file_name))
3536 );
3537 return false;
3538 }
3539 setBuffer(buf);
3540 bool success = documentBufferView()->setCursorFromRow(row);
3541 if (!success) {
3542 LYXERR(Debug::LATEX,
3543 "setCursorFromRow: invalid position for row " << row);
3544 frontend::Alert::error(_("Inverse Search Failed"),
3545 _("Invalid position requested by inverse search.\n"
3546 "You may need to update the viewed document."));
3547 }
3548 return success;
3549 }
3550
3551
toolBarPopup(const QPoint &)3552 void GuiView::toolBarPopup(const QPoint & /*pos*/)
3553 {
3554 QMenu * menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
3555 menu->exec(QCursor::pos());
3556 }
3557
3558
3559 template<class T>
runAndDestroy(const T & func,Buffer const * orig,Buffer * clone,string const & format)3560 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3561 {
3562 Buffer::ExportStatus const status = func(format);
3563
3564 // the cloning operation will have produced a clone of the entire set of
3565 // documents, starting from the master. so we must delete those.
3566 Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3567 delete mbuf;
3568 busyBuffers.remove(orig);
3569 return status;
3570 }
3571
3572
compileAndDestroy(Buffer const * orig,Buffer * clone,string const & format)3573 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3574 {
3575 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3576 return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3577 }
3578
3579
exportAndDestroy(Buffer const * orig,Buffer * clone,string const & format)3580 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3581 {
3582 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3583 return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3584 }
3585
3586
previewAndDestroy(Buffer const * orig,Buffer * clone,string const & format)3587 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3588 {
3589 Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3590 return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3591 }
3592
3593
asyncBufferProcessing(string const & argument,Buffer const * used_buffer,docstring const & msg,Buffer::ExportStatus (* asyncFunc)(Buffer const *,Buffer *,string const &),Buffer::ExportStatus (Buffer::* syncFunc)(string const &,bool)const,Buffer::ExportStatus (Buffer::* previewFunc)(string const &)const,bool allow_async)3594 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3595 string const & argument,
3596 Buffer const * used_buffer,
3597 docstring const & msg,
3598 Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3599 Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3600 Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const,
3601 bool allow_async)
3602 {
3603 if (!used_buffer)
3604 return false;
3605
3606 string format = argument;
3607 if (format.empty())
3608 format = used_buffer->params().getDefaultOutputFormat();
3609 processing_format = format;
3610 if (!msg.empty()) {
3611 progress_->clearMessages();
3612 gv_->message(msg);
3613 }
3614 #if EXPORT_in_THREAD
3615 if (allow_async) {
3616 GuiViewPrivate::busyBuffers.insert(used_buffer);
3617 Buffer * cloned_buffer = used_buffer->cloneWithChildren();
3618 if (!cloned_buffer) {
3619 Alert::error(_("Export Error"),
3620 _("Error cloning the Buffer."));
3621 return false;
3622 }
3623 QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3624 asyncFunc,
3625 used_buffer,
3626 cloned_buffer,
3627 format);
3628 setPreviewFuture(f);
3629 last_export_format = used_buffer->params().bufferFormat();
3630 (void) syncFunc;
3631 (void) previewFunc;
3632 // We are asynchronous, so we don't know here anything about the success
3633 return true;
3634 } else {
3635 Buffer::ExportStatus status;
3636 if (syncFunc) {
3637 status = (used_buffer->*syncFunc)(format, false);
3638 } else if (previewFunc) {
3639 status = (used_buffer->*previewFunc)(format);
3640 } else
3641 return false;
3642 handleExportStatus(gv_, status, format);
3643 (void) asyncFunc;
3644 return (status == Buffer::ExportSuccess
3645 || status == Buffer::PreviewSuccess);
3646 }
3647 #else
3648 Buffer::ExportStatus status;
3649 if (syncFunc) {
3650 status = (used_buffer->*syncFunc)(format, true);
3651 } else if (previewFunc) {
3652 status = (used_buffer->*previewFunc)(format);
3653 } else
3654 return false;
3655 handleExportStatus(gv_, status, format);
3656 (void) asyncFunc;
3657 return (status == Buffer::ExportSuccess
3658 || status == Buffer::PreviewSuccess);
3659 #endif
3660 }
3661
dispatchToBufferView(FuncRequest const & cmd,DispatchResult & dr)3662 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3663 {
3664 BufferView * bv = currentBufferView();
3665 LASSERT(bv, return);
3666
3667 // Let the current BufferView dispatch its own actions.
3668 bv->dispatch(cmd, dr);
3669 if (dr.dispatched()) {
3670 if (cmd.action() == LFUN_REDO || cmd.action() == LFUN_UNDO)
3671 updateDialog("document", "");
3672 return;
3673 }
3674
3675 // Try with the document BufferView dispatch if any.
3676 BufferView * doc_bv = documentBufferView();
3677 if (doc_bv && doc_bv != bv) {
3678 doc_bv->dispatch(cmd, dr);
3679 if (dr.dispatched()) {
3680 if (cmd.action() == LFUN_REDO || cmd.action() == LFUN_UNDO)
3681 updateDialog("document", "");
3682 return;
3683 }
3684 }
3685
3686 // Then let the current Cursor dispatch its own actions.
3687 bv->cursor().dispatch(cmd);
3688
3689 // update completion. We do it here and not in
3690 // processKeySym to avoid another redraw just for a
3691 // changed inline completion
3692 if (cmd.origin() == FuncRequest::KEYBOARD) {
3693 if (cmd.action() == LFUN_SELF_INSERT
3694 || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3695 updateCompletion(bv->cursor(), true, true);
3696 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3697 updateCompletion(bv->cursor(), false, true);
3698 else
3699 updateCompletion(bv->cursor(), false, false);
3700 }
3701
3702 dr = bv->cursor().result();
3703 }
3704
3705
dispatch(FuncRequest const & cmd,DispatchResult & dr)3706 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3707 {
3708 BufferView * bv = currentBufferView();
3709 // By default we won't need any update.
3710 dr.screenUpdate(Update::None);
3711 // assume cmd will be dispatched
3712 dr.dispatched(true);
3713
3714 Buffer * doc_buffer = documentBufferView()
3715 ? &(documentBufferView()->buffer()) : 0;
3716
3717 if (cmd.origin() == FuncRequest::TOC) {
3718 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3719 // FIXME: do we need to pass a DispatchResult object here?
3720 toc->doDispatch(bv->cursor(), cmd);
3721 return;
3722 }
3723
3724 string const argument = to_utf8(cmd.argument());
3725
3726 switch(cmd.action()) {
3727 case LFUN_BUFFER_CHILD_OPEN:
3728 openChildDocument(to_utf8(cmd.argument()));
3729 break;
3730
3731 case LFUN_BUFFER_IMPORT:
3732 importDocument(to_utf8(cmd.argument()));
3733 break;
3734
3735 case LFUN_MASTER_BUFFER_EXPORT:
3736 if (doc_buffer)
3737 doc_buffer = const_cast<Buffer *>(doc_buffer->masterBuffer());
3738 // fall through
3739 case LFUN_BUFFER_EXPORT: {
3740 if (!doc_buffer)
3741 break;
3742 // GCC only sees strfwd.h when building merged
3743 if (::lyx::operator==(cmd.argument(), "custom")) {
3744 // LFUN_MASTER_BUFFER_EXPORT is not enabled for this case,
3745 // so the following test should not be needed.
3746 // In principle, we could try to switch to such a view...
3747 // if (cmd.action() == LFUN_BUFFER_EXPORT)
3748 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3749 break;
3750 }
3751
3752 string const dest = cmd.getArg(1);
3753 FileName target_dir;
3754 if (!dest.empty() && FileName::isAbsolute(dest))
3755 target_dir = FileName(support::onlyPath(dest));
3756 else
3757 target_dir = doc_buffer->fileName().onlyPath();
3758
3759 string const format = (argument.empty() || argument == "default") ?
3760 doc_buffer->params().getDefaultOutputFormat() : argument;
3761
3762 if ((dest.empty() && doc_buffer->isUnnamed())
3763 || !target_dir.isDirWritable()) {
3764 exportBufferAs(*doc_buffer, from_utf8(format));
3765 break;
3766 }
3767 /* TODO/Review: Is it a problem to also export the children?
3768 See the update_unincluded flag */
3769 d.asyncBufferProcessing(format,
3770 doc_buffer,
3771 _("Exporting ..."),
3772 &GuiViewPrivate::exportAndDestroy,
3773 &Buffer::doExport,
3774 0, cmd.allowAsync());
3775 // TODO Inform user about success
3776 break;
3777 }
3778
3779 case LFUN_BUFFER_EXPORT_AS: {
3780 LASSERT(doc_buffer, break);
3781 docstring f = cmd.argument();
3782 if (f.empty())
3783 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3784 exportBufferAs(*doc_buffer, f);
3785 break;
3786 }
3787
3788 case LFUN_BUFFER_UPDATE: {
3789 d.asyncBufferProcessing(argument,
3790 doc_buffer,
3791 _("Exporting ..."),
3792 &GuiViewPrivate::compileAndDestroy,
3793 &Buffer::doExport,
3794 0, cmd.allowAsync());
3795 break;
3796 }
3797 case LFUN_BUFFER_VIEW: {
3798 d.asyncBufferProcessing(argument,
3799 doc_buffer,
3800 _("Previewing ..."),
3801 &GuiViewPrivate::previewAndDestroy,
3802 0,
3803 &Buffer::preview, cmd.allowAsync());
3804 break;
3805 }
3806 case LFUN_MASTER_BUFFER_UPDATE: {
3807 d.asyncBufferProcessing(argument,
3808 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3809 docstring(),
3810 &GuiViewPrivate::compileAndDestroy,
3811 &Buffer::doExport,
3812 0, cmd.allowAsync());
3813 break;
3814 }
3815 case LFUN_MASTER_BUFFER_VIEW: {
3816 d.asyncBufferProcessing(argument,
3817 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3818 docstring(),
3819 &GuiViewPrivate::previewAndDestroy,
3820 0, &Buffer::preview, cmd.allowAsync());
3821 break;
3822 }
3823 case LFUN_BUFFER_SWITCH: {
3824 string const file_name = to_utf8(cmd.argument());
3825 if (!FileName::isAbsolute(file_name)) {
3826 dr.setError(true);
3827 dr.setMessage(_("Absolute filename expected."));
3828 break;
3829 }
3830
3831 Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3832 if (!buffer) {
3833 dr.setError(true);
3834 dr.setMessage(_("Document not loaded"));
3835 break;
3836 }
3837
3838 // Do we open or switch to the buffer in this view ?
3839 if (workArea(*buffer)
3840 || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3841 setBuffer(buffer);
3842 break;
3843 }
3844
3845 // Look for the buffer in other views
3846 QList<int> const ids = guiApp->viewIds();
3847 int i = 0;
3848 for (; i != ids.size(); ++i) {
3849 GuiView & gv = guiApp->view(ids[i]);
3850 if (gv.workArea(*buffer)) {
3851 gv.raise();
3852 gv.activateWindow();
3853 gv.setFocus();
3854 gv.setBuffer(buffer);
3855 break;
3856 }
3857 }
3858
3859 // If necessary, open a new window as a last resort
3860 if (i == ids.size()) {
3861 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3862 lyx::dispatch(cmd);
3863 }
3864 break;
3865 }
3866
3867 case LFUN_BUFFER_NEXT:
3868 gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3869 break;
3870
3871 case LFUN_BUFFER_MOVE_NEXT:
3872 gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3873 break;
3874
3875 case LFUN_BUFFER_PREVIOUS:
3876 gotoNextOrPreviousBuffer(PREVBUFFER, false);
3877 break;
3878
3879 case LFUN_BUFFER_MOVE_PREVIOUS:
3880 gotoNextOrPreviousBuffer(PREVBUFFER, true);
3881 break;
3882
3883 case LFUN_BUFFER_CHKTEX:
3884 LASSERT(doc_buffer, break);
3885 doc_buffer->runChktex();
3886 break;
3887
3888 case LFUN_COMMAND_EXECUTE: {
3889 command_execute_ = true;
3890 minibuffer_focus_ = true;
3891 break;
3892 }
3893 case LFUN_DROP_LAYOUTS_CHOICE:
3894 d.layout_->showPopup();
3895 break;
3896
3897 case LFUN_MENU_OPEN:
3898 if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3899 menu->exec(QCursor::pos());
3900 break;
3901
3902 case LFUN_FILE_INSERT:
3903 insertLyXFile(cmd.argument());
3904 break;
3905
3906 case LFUN_FILE_INSERT_PLAINTEXT:
3907 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3908 string const fname = to_utf8(cmd.argument());
3909 if (!fname.empty() && !FileName::isAbsolute(fname)) {
3910 dr.setMessage(_("Absolute filename expected."));
3911 break;
3912 }
3913
3914 FileName filename(fname);
3915 if (fname.empty()) {
3916 FileDialog dlg(qt_("Select file to insert"));
3917
3918 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3919 QStringList(qt_("All Files (*)")));
3920
3921 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3922 dr.setMessage(_("Canceled."));
3923 break;
3924 }
3925
3926 filename.set(fromqstr(result.second));
3927 }
3928
3929 if (bv) {
3930 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3931 bv->dispatch(new_cmd, dr);
3932 }
3933 break;
3934 }
3935
3936 case LFUN_BUFFER_RELOAD: {
3937 LASSERT(doc_buffer, break);
3938
3939 int ret = 0;
3940 if (!doc_buffer->isClean()) {
3941 docstring const file =
3942 makeDisplayPath(doc_buffer->absFileName(), 20);
3943 if (doc_buffer->notifiesExternalModification()) {
3944 docstring text = _("The current version will be lost. "
3945 "Are you sure you want to load the version on disk "
3946 "of the document %1$s?");
3947 ret = Alert::prompt(_("Reload saved document?"),
3948 bformat(text, file), 1, 1,
3949 _("&Reload"), _("&Cancel"));
3950 } else {
3951 docstring text = _("Any changes will be lost. "
3952 "Are you sure you want to revert to the saved version "
3953 "of the document %1$s?");
3954 ret = Alert::prompt(_("Revert to saved document?"),
3955 bformat(text, file), 1, 1,
3956 _("&Revert"), _("&Cancel"));
3957 }
3958 }
3959
3960 if (ret == 0) {
3961 doc_buffer->markClean();
3962 reloadBuffer(*doc_buffer);
3963 dr.forceBufferUpdate();
3964 }
3965 break;
3966 }
3967
3968 case LFUN_BUFFER_WRITE:
3969 LASSERT(doc_buffer, break);
3970 saveBuffer(*doc_buffer);
3971 break;
3972
3973 case LFUN_BUFFER_WRITE_AS:
3974 LASSERT(doc_buffer, break);
3975 renameBuffer(*doc_buffer, cmd.argument());
3976 break;
3977
3978 case LFUN_BUFFER_WRITE_ALL: {
3979 Buffer * first = theBufferList().first();
3980 if (!first)
3981 break;
3982 message(_("Saving all documents..."));
3983 // We cannot use a for loop as the buffer list cycles.
3984 Buffer * b = first;
3985 do {
3986 if (!b->isClean()) {
3987 saveBuffer(*b);
3988 LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3989 }
3990 b = theBufferList().next(b);
3991 } while (b != first);
3992 dr.setMessage(_("All documents saved."));
3993 break;
3994 }
3995
3996 case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
3997 LASSERT(doc_buffer, break);
3998 doc_buffer->clearExternalModification();
3999 break;
4000
4001 case LFUN_BUFFER_CLOSE:
4002 closeBuffer();
4003 break;
4004
4005 case LFUN_BUFFER_CLOSE_ALL:
4006 closeBufferAll();
4007 break;
4008
4009 case LFUN_DEVEL_MODE_TOGGLE:
4010 devel_mode_ = !devel_mode_;
4011 if (devel_mode_)
4012 dr.setMessage(_("Developer mode is now enabled."));
4013 else
4014 dr.setMessage(_("Developer mode is now disabled."));
4015 break;
4016
4017 case LFUN_TOOLBAR_TOGGLE: {
4018 string const name = cmd.getArg(0);
4019 if (GuiToolbar * t = toolbar(name))
4020 t->toggle();
4021 break;
4022 }
4023
4024 case LFUN_TOOLBAR_MOVABLE: {
4025 string const name = cmd.getArg(0);
4026 if (name == "*") {
4027 // toggle (all) toolbars movablility
4028 toolbarsMovable_ = !toolbarsMovable_;
4029 for (ToolbarInfo const & ti : guiApp->toolbars()) {
4030 GuiToolbar * tb = toolbar(ti.name);
4031 if (tb && tb->isMovable() != toolbarsMovable_)
4032 // toggle toolbar movablity if it does not fit lock
4033 // (all) toolbars positions state silent = true, since
4034 // status bar notifications are slow
4035 tb->movable(true);
4036 }
4037 if (toolbarsMovable_)
4038 dr.setMessage(_("Toolbars unlocked."));
4039 else
4040 dr.setMessage(_("Toolbars locked."));
4041 } else if (GuiToolbar * t = toolbar(name)) {
4042 // toggle current toolbar movablity
4043 t->movable();
4044 // update lock (all) toolbars positions
4045 updateLockToolbars();
4046 }
4047 break;
4048 }
4049
4050 case LFUN_ICON_SIZE: {
4051 QSize size = d.iconSize(cmd.argument());
4052 setIconSize(size);
4053 dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
4054 size.width(), size.height()));
4055 break;
4056 }
4057
4058 case LFUN_DIALOG_UPDATE: {
4059 string const name = to_utf8(cmd.argument());
4060 if (name == "prefs" || name == "document")
4061 updateDialog(name, string());
4062 else if (name == "paragraph")
4063 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
4064 else if (currentBufferView()) {
4065 Inset * inset = currentBufferView()->editedInset(name);
4066 // Can only update a dialog connected to an existing inset
4067 if (inset) {
4068 // FIXME: get rid of this indirection; GuiView ask the inset
4069 // if he is kind enough to update itself...
4070 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
4071 //FIXME: pass DispatchResult here?
4072 inset->dispatch(currentBufferView()->cursor(), fr);
4073 }
4074 }
4075 break;
4076 }
4077
4078 case LFUN_DIALOG_TOGGLE: {
4079 FuncCode const func_code = isDialogVisible(cmd.getArg(0))
4080 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
4081 dispatch(FuncRequest(func_code, cmd.argument()), dr);
4082 break;
4083 }
4084
4085 case LFUN_DIALOG_DISCONNECT_INSET:
4086 disconnectDialog(to_utf8(cmd.argument()));
4087 break;
4088
4089 case LFUN_DIALOG_HIDE: {
4090 guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
4091 break;
4092 }
4093
4094 case LFUN_DIALOG_SHOW: {
4095 string const name = cmd.getArg(0);
4096 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
4097
4098 if (name == "character") {
4099 data = freefont2string();
4100 if (!data.empty())
4101 showDialog("character", data);
4102 } else if (name == "latexlog") {
4103 // getStatus checks that
4104 LATTEST(doc_buffer);
4105 Buffer::LogType type;
4106 string const logfile = doc_buffer->logName(&type);
4107 switch (type) {
4108 case Buffer::latexlog:
4109 data = "latex ";
4110 break;
4111 case Buffer::buildlog:
4112 data = "literate ";
4113 break;
4114 }
4115 data += Lexer::quoteString(logfile);
4116 showDialog("log", data);
4117 } else if (name == "vclog") {
4118 // getStatus checks that
4119 LATTEST(doc_buffer);
4120 string const data = "vc " +
4121 Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
4122 showDialog("log", data);
4123 } else if (name == "symbols") {
4124 data = bv->cursor().getEncoding()->name();
4125 if (!data.empty())
4126 showDialog("symbols", data);
4127 // bug 5274
4128 } else if (name == "prefs" && isFullScreen()) {
4129 lfunUiToggle("fullscreen");
4130 showDialog("prefs", data);
4131 } else
4132 showDialog(name, data);
4133 break;
4134 }
4135
4136 case LFUN_MESSAGE:
4137 dr.setMessage(cmd.argument());
4138 break;
4139
4140 case LFUN_UI_TOGGLE: {
4141 string arg = cmd.getArg(0);
4142 if (!lfunUiToggle(arg)) {
4143 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
4144 dr.setMessage(bformat(msg, from_utf8(arg)));
4145 }
4146 // Make sure the keyboard focus stays in the work area.
4147 setFocus();
4148 break;
4149 }
4150
4151 case LFUN_VIEW_SPLIT: {
4152 LASSERT(doc_buffer, break);
4153 string const orientation = cmd.getArg(0);
4154 d.splitter_->setOrientation(orientation == "vertical"
4155 ? Qt::Vertical : Qt::Horizontal);
4156 TabWorkArea * twa = addTabWorkArea();
4157 GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
4158 setCurrentWorkArea(wa);
4159 break;
4160 }
4161 case LFUN_TAB_GROUP_CLOSE:
4162 if (TabWorkArea * twa = d.currentTabWorkArea()) {
4163 closeTabWorkArea(twa);
4164 d.current_work_area_ = 0;
4165 twa = d.currentTabWorkArea();
4166 // Switch to the next GuiWorkArea in the found TabWorkArea.
4167 if (twa) {
4168 // Make sure the work area is up to date.
4169 setCurrentWorkArea(twa->currentWorkArea());
4170 } else {
4171 setCurrentWorkArea(0);
4172 }
4173 }
4174 break;
4175
4176 case LFUN_VIEW_CLOSE:
4177 if (TabWorkArea * twa = d.currentTabWorkArea()) {
4178 closeWorkArea(twa->currentWorkArea());
4179 d.current_work_area_ = 0;
4180 twa = d.currentTabWorkArea();
4181 // Switch to the next GuiWorkArea in the found TabWorkArea.
4182 if (twa) {
4183 // Make sure the work area is up to date.
4184 setCurrentWorkArea(twa->currentWorkArea());
4185 } else {
4186 setCurrentWorkArea(0);
4187 }
4188 }
4189 break;
4190
4191 case LFUN_COMPLETION_INLINE:
4192 if (d.current_work_area_)
4193 d.current_work_area_->completer().showInline();
4194 break;
4195
4196 case LFUN_COMPLETION_POPUP:
4197 if (d.current_work_area_)
4198 d.current_work_area_->completer().showPopup();
4199 break;
4200
4201
4202 case LFUN_COMPLETE:
4203 if (d.current_work_area_)
4204 d.current_work_area_->completer().tab();
4205 break;
4206
4207 case LFUN_COMPLETION_CANCEL:
4208 if (d.current_work_area_) {
4209 if (d.current_work_area_->completer().popupVisible())
4210 d.current_work_area_->completer().hidePopup();
4211 else
4212 d.current_work_area_->completer().hideInline();
4213 }
4214 break;
4215
4216 case LFUN_COMPLETION_ACCEPT:
4217 if (d.current_work_area_)
4218 d.current_work_area_->completer().activate();
4219 break;
4220
4221 case LFUN_BUFFER_ZOOM_IN:
4222 case LFUN_BUFFER_ZOOM_OUT:
4223 case LFUN_BUFFER_ZOOM: {
4224 if (cmd.argument().empty()) {
4225 if (cmd.action() == LFUN_BUFFER_ZOOM)
4226 zoom_ratio_ = 1.0;
4227 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4228 zoom_ratio_ += 0.1;
4229 else
4230 zoom_ratio_ -= 0.1;
4231 } else {
4232 if (cmd.action() == LFUN_BUFFER_ZOOM)
4233 zoom_ratio_ = convert<int>(cmd.argument()) / double(lyxrc.defaultZoom);
4234 else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
4235 zoom_ratio_ += convert<int>(cmd.argument()) / 100.0;
4236 else
4237 zoom_ratio_ -= convert<int>(cmd.argument()) / 100.0;
4238 }
4239
4240 // Actual zoom value: default zoom + fractional extra value
4241 int zoom = lyxrc.defaultZoom * zoom_ratio_;
4242 if (zoom < static_cast<int>(zoom_min_))
4243 zoom = zoom_min_;
4244
4245 lyxrc.currentZoom = zoom;
4246
4247 dr.setMessage(bformat(_("Zoom level is now %1$d% (default value: %2$d%)"),
4248 lyxrc.currentZoom, lyxrc.defaultZoom));
4249
4250 // The global QPixmapCache is used in GuiPainter to cache text
4251 // painting so we must reset it.
4252 QPixmapCache::clear();
4253 guiApp->fontLoader().update();
4254 dr.screenUpdate(Update::Force | Update::FitCursor);
4255 break;
4256 }
4257
4258 case LFUN_VC_REGISTER:
4259 case LFUN_VC_RENAME:
4260 case LFUN_VC_COPY:
4261 case LFUN_VC_CHECK_IN:
4262 case LFUN_VC_CHECK_OUT:
4263 case LFUN_VC_REPO_UPDATE:
4264 case LFUN_VC_LOCKING_TOGGLE:
4265 case LFUN_VC_REVERT:
4266 case LFUN_VC_UNDO_LAST:
4267 case LFUN_VC_COMMAND:
4268 case LFUN_VC_COMPARE:
4269 dispatchVC(cmd, dr);
4270 break;
4271
4272 case LFUN_SERVER_GOTO_FILE_ROW:
4273 if(goToFileRow(to_utf8(cmd.argument())))
4274 dr.screenUpdate(Update::Force | Update::FitCursor);
4275 break;
4276
4277 case LFUN_LYX_ACTIVATE:
4278 activateWindow();
4279 break;
4280
4281 case LFUN_FORWARD_SEARCH: {
4282 // it seems safe to assume we have a document buffer, since
4283 // getStatus wants one.
4284 LATTEST(doc_buffer);
4285 Buffer const * doc_master = doc_buffer->masterBuffer();
4286 FileName const path(doc_master->temppath());
4287 string const texname = doc_master->isChild(doc_buffer)
4288 ? DocFileName(changeExtension(
4289 doc_buffer->absFileName(),
4290 "tex")).mangledFileName()
4291 : doc_buffer->latexName();
4292 string const fulltexname =
4293 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
4294 string const mastername =
4295 removeExtension(doc_master->latexName());
4296 FileName const dviname(addName(path.absFileName(),
4297 addExtension(mastername, "dvi")));
4298 FileName const pdfname(addName(path.absFileName(),
4299 addExtension(mastername, "pdf")));
4300 bool const have_dvi = dviname.exists();
4301 bool const have_pdf = pdfname.exists();
4302 if (!have_dvi && !have_pdf) {
4303 dr.setMessage(_("Please, preview the document first."));
4304 break;
4305 }
4306 string outname = dviname.onlyFileName();
4307 string command = lyxrc.forward_search_dvi;
4308 if (!have_dvi || (have_pdf &&
4309 pdfname.lastModified() > dviname.lastModified())) {
4310 outname = pdfname.onlyFileName();
4311 command = lyxrc.forward_search_pdf;
4312 }
4313
4314 DocIterator cur = bv->cursor();
4315 int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4316 LYXERR(Debug::ACTION, "Forward search: row:" << row
4317 << " cur:" << cur);
4318 if (row == -1 || command.empty()) {
4319 dr.setMessage(_("Couldn't proceed."));
4320 break;
4321 }
4322 string texrow = convert<string>(row);
4323
4324 command = subst(command, "$$n", texrow);
4325 command = subst(command, "$$f", fulltexname);
4326 command = subst(command, "$$t", texname);
4327 command = subst(command, "$$o", outname);
4328
4329 PathChanger p(path);
4330 Systemcall one;
4331 one.startscript(Systemcall::DontWait, command);
4332 break;
4333 }
4334
4335 case LFUN_SPELLING_CONTINUOUSLY:
4336 lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4337 dr.screenUpdate(Update::Force);
4338 break;
4339
4340 default:
4341 // The LFUN must be for one of BufferView, Buffer or Cursor;
4342 // let's try that:
4343 dispatchToBufferView(cmd, dr);
4344 break;
4345 }
4346
4347 // Part of automatic menu appearance feature.
4348 if (isFullScreen()) {
4349 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4350 menuBar()->hide();
4351 }
4352
4353 // Need to update bv because many LFUNs here might have destroyed it
4354 bv = currentBufferView();
4355
4356 // Clear non-empty selections
4357 // (e.g. from a "char-forward-select" followed by "char-backward-select")
4358 if (bv) {
4359 Cursor & cur = bv->cursor();
4360 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4361 cur.clearSelection();
4362 }
4363 }
4364 }
4365
4366
lfunUiToggle(string const & ui_component)4367 bool GuiView::lfunUiToggle(string const & ui_component)
4368 {
4369 if (ui_component == "scrollbar") {
4370 // hide() is of no help
4371 if (d.current_work_area_->verticalScrollBarPolicy() ==
4372 Qt::ScrollBarAlwaysOff)
4373
4374 d.current_work_area_->setVerticalScrollBarPolicy(
4375 Qt::ScrollBarAsNeeded);
4376 else
4377 d.current_work_area_->setVerticalScrollBarPolicy(
4378 Qt::ScrollBarAlwaysOff);
4379 } else if (ui_component == "statusbar") {
4380 statusBar()->setVisible(!statusBar()->isVisible());
4381 } else if (ui_component == "menubar") {
4382 menuBar()->setVisible(!menuBar()->isVisible());
4383 } else
4384 if (ui_component == "frame") {
4385 int l, t, r, b;
4386 getContentsMargins(&l, &t, &r, &b);
4387 //are the frames in default state?
4388 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4389 if (l == 0) {
4390 setContentsMargins(-2, -2, -2, -2);
4391 } else {
4392 setContentsMargins(0, 0, 0, 0);
4393 }
4394 } else
4395 if (ui_component == "fullscreen") {
4396 toggleFullScreen();
4397 } else
4398 return false;
4399 return true;
4400 }
4401
4402
toggleFullScreen()4403 void GuiView::toggleFullScreen()
4404 {
4405 if (isFullScreen()) {
4406 for (int i = 0; i != d.splitter_->count(); ++i)
4407 d.tabWorkArea(i)->setFullScreen(false);
4408 setContentsMargins(0, 0, 0, 0);
4409 setWindowState(windowState() ^ Qt::WindowFullScreen);
4410 restoreLayout();
4411 menuBar()->show();
4412 statusBar()->show();
4413 } else {
4414 // bug 5274
4415 hideDialogs("prefs", 0);
4416 for (int i = 0; i != d.splitter_->count(); ++i)
4417 d.tabWorkArea(i)->setFullScreen(true);
4418 setContentsMargins(-2, -2, -2, -2);
4419 saveLayout();
4420 setWindowState(windowState() ^ Qt::WindowFullScreen);
4421 if (lyxrc.full_screen_statusbar)
4422 statusBar()->hide();
4423 if (lyxrc.full_screen_menubar)
4424 menuBar()->hide();
4425 if (lyxrc.full_screen_toolbars) {
4426 ToolbarMap::iterator end = d.toolbars_.end();
4427 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4428 it->second->hide();
4429 }
4430 }
4431
4432 // give dialogs like the TOC a chance to adapt
4433 updateDialogs();
4434 }
4435
4436
updateInset(Inset const * inset)4437 Buffer const * GuiView::updateInset(Inset const * inset)
4438 {
4439 if (!inset)
4440 return 0;
4441
4442 Buffer const * inset_buffer = &(inset->buffer());
4443
4444 for (int i = 0; i != d.splitter_->count(); ++i) {
4445 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4446 if (!wa)
4447 continue;
4448 Buffer const * buffer = &(wa->bufferView().buffer());
4449 if (inset_buffer == buffer)
4450 wa->scheduleRedraw(true);
4451 }
4452 return inset_buffer;
4453 }
4454
4455
restartCaret()4456 void GuiView::restartCaret()
4457 {
4458 /* When we move around, or type, it's nice to be able to see
4459 * the caret immediately after the keypress.
4460 */
4461 if (d.current_work_area_)
4462 d.current_work_area_->startBlinkingCaret();
4463
4464 // Take this occasion to update the other GUI elements.
4465 updateDialogs();
4466 updateStatusBar();
4467 }
4468
4469
updateCompletion(Cursor & cur,bool start,bool keep)4470 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4471 {
4472 if (d.current_work_area_)
4473 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4474 }
4475
4476 namespace {
4477
4478 // This list should be kept in sync with the list of insets in
4479 // src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
4480 // dialog should have the same name as the inset.
4481 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4482 // docs in LyXAction.cpp.
4483
4484 char const * const dialognames[] = {
4485
4486 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4487 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4488 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4489 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4490 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4491 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4492 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4493 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4494
4495 char const * const * const end_dialognames =
4496 dialognames + (sizeof(dialognames) / sizeof(char *));
4497
4498 class cmpCStr {
4499 public:
cmpCStr(char const * name)4500 cmpCStr(char const * name) : name_(name) {}
operator ()(char const * other)4501 bool operator()(char const * other) {
4502 return strcmp(other, name_) == 0;
4503 }
4504 private:
4505 char const * name_;
4506 };
4507
4508
isValidName(string const & name)4509 bool isValidName(string const & name)
4510 {
4511 return find_if(dialognames, end_dialognames,
4512 cmpCStr(name.c_str())) != end_dialognames;
4513 }
4514
4515 } // namespace
4516
4517
resetDialogs()4518 void GuiView::resetDialogs()
4519 {
4520 // Make sure that no LFUN uses any GuiView.
4521 guiApp->setCurrentView(0);
4522 saveLayout();
4523 saveUISettings();
4524 menuBar()->clear();
4525 constructToolbars();
4526 guiApp->menus().fillMenuBar(menuBar(), this, false);
4527 d.layout_->updateContents(true);
4528 // Now update controls with current buffer.
4529 guiApp->setCurrentView(this);
4530 restoreLayout();
4531 restartCaret();
4532 }
4533
4534
findOrBuild(string const & name,bool hide_it)4535 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4536 {
4537 if (!isValidName(name))
4538 return 0;
4539
4540 map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4541
4542 if (it != d.dialogs_.end()) {
4543 if (hide_it)
4544 it->second->hideView();
4545 return it->second.get();
4546 }
4547
4548 Dialog * dialog = build(name);
4549 d.dialogs_[name].reset(dialog);
4550 if (lyxrc.allow_geometry_session)
4551 dialog->restoreSession();
4552 if (hide_it)
4553 dialog->hideView();
4554 return dialog;
4555 }
4556
4557
showDialog(string const & name,string const & data,Inset * inset)4558 void GuiView::showDialog(string const & name, string const & data,
4559 Inset * inset)
4560 {
4561 triggerShowDialog(toqstr(name), toqstr(data), inset);
4562 }
4563
4564
doShowDialog(QString const & qname,QString const & qdata,Inset * inset)4565 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4566 Inset * inset)
4567 {
4568 if (d.in_show_)
4569 return;
4570
4571 const string name = fromqstr(qname);
4572 const string data = fromqstr(qdata);
4573
4574 d.in_show_ = true;
4575 try {
4576 Dialog * dialog = findOrBuild(name, false);
4577 if (dialog) {
4578 bool const visible = dialog->isVisibleView();
4579 dialog->showData(data);
4580 if (currentBufferView())
4581 currentBufferView()->editInset(name, inset);
4582 // We only set the focus to the new dialog if it was not yet
4583 // visible in order not to change the existing previous behaviour
4584 if (visible) {
4585 // activateWindow is needed for floating dockviews
4586 dialog->asQWidget()->raise();
4587 dialog->asQWidget()->activateWindow();
4588 dialog->asQWidget()->setFocus();
4589 }
4590 }
4591 }
4592 catch (ExceptionMessage const & ex) {
4593 d.in_show_ = false;
4594 throw ex;
4595 }
4596 d.in_show_ = false;
4597 }
4598
4599
isDialogVisible(string const & name) const4600 bool GuiView::isDialogVisible(string const & name) const
4601 {
4602 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4603 if (it == d.dialogs_.end())
4604 return false;
4605 return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4606 }
4607
4608
hideDialog(string const & name,Inset * inset)4609 void GuiView::hideDialog(string const & name, Inset * inset)
4610 {
4611 map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4612 if (it == d.dialogs_.end())
4613 return;
4614
4615 if (inset) {
4616 if (!currentBufferView())
4617 return;
4618 if (inset != currentBufferView()->editedInset(name))
4619 return;
4620 }
4621
4622 Dialog * const dialog = it->second.get();
4623 if (dialog->isVisibleView())
4624 dialog->hideView();
4625 if (currentBufferView())
4626 currentBufferView()->editInset(name, 0);
4627 }
4628
4629
disconnectDialog(string const & name)4630 void GuiView::disconnectDialog(string const & name)
4631 {
4632 if (!isValidName(name))
4633 return;
4634 if (currentBufferView())
4635 currentBufferView()->editInset(name, 0);
4636 }
4637
4638
hideAll() const4639 void GuiView::hideAll() const
4640 {
4641 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4642 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4643
4644 for(; it != end; ++it)
4645 it->second->hideView();
4646 }
4647
4648
updateDialogs()4649 void GuiView::updateDialogs()
4650 {
4651 map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
4652 map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4653
4654 for(; it != end; ++it) {
4655 Dialog * dialog = it->second.get();
4656 if (dialog) {
4657 if (dialog->needBufferOpen() && !documentBufferView())
4658 hideDialog(fromqstr(dialog->name()), 0);
4659 else if (dialog->isVisibleView())
4660 dialog->checkStatus();
4661 }
4662 }
4663 updateToolbars();
4664 updateLayoutList();
4665 }
4666
4667 Dialog * createDialog(GuiView & lv, string const & name);
4668
4669 // will be replaced by a proper factory...
4670 Dialog * createGuiAbout(GuiView & lv);
4671 Dialog * createGuiBibtex(GuiView & lv);
4672 Dialog * createGuiChanges(GuiView & lv);
4673 Dialog * createGuiCharacter(GuiView & lv);
4674 Dialog * createGuiCitation(GuiView & lv);
4675 Dialog * createGuiCompare(GuiView & lv);
4676 Dialog * createGuiCompareHistory(GuiView & lv);
4677 Dialog * createGuiDelimiter(GuiView & lv);
4678 Dialog * createGuiDocument(GuiView & lv);
4679 Dialog * createGuiErrorList(GuiView & lv);
4680 Dialog * createGuiExternal(GuiView & lv);
4681 Dialog * createGuiGraphics(GuiView & lv);
4682 Dialog * createGuiInclude(GuiView & lv);
4683 Dialog * createGuiIndex(GuiView & lv);
4684 Dialog * createGuiListings(GuiView & lv);
4685 Dialog * createGuiLog(GuiView & lv);
4686 Dialog * createGuiMathMatrix(GuiView & lv);
4687 Dialog * createGuiNote(GuiView & lv);
4688 Dialog * createGuiParagraph(GuiView & lv);
4689 Dialog * createGuiPhantom(GuiView & lv);
4690 Dialog * createGuiPreferences(GuiView & lv);
4691 Dialog * createGuiPrint(GuiView & lv);
4692 Dialog * createGuiPrintindex(GuiView & lv);
4693 Dialog * createGuiRef(GuiView & lv);
4694 Dialog * createGuiSearch(GuiView & lv);
4695 Dialog * createGuiSearchAdv(GuiView & lv);
4696 Dialog * createGuiSendTo(GuiView & lv);
4697 Dialog * createGuiShowFile(GuiView & lv);
4698 Dialog * createGuiSpellchecker(GuiView & lv);
4699 Dialog * createGuiSymbols(GuiView & lv);
4700 Dialog * createGuiTabularCreate(GuiView & lv);
4701 Dialog * createGuiTexInfo(GuiView & lv);
4702 Dialog * createGuiToc(GuiView & lv);
4703 Dialog * createGuiThesaurus(GuiView & lv);
4704 Dialog * createGuiViewSource(GuiView & lv);
4705 Dialog * createGuiWrap(GuiView & lv);
4706 Dialog * createGuiProgressView(GuiView & lv);
4707
4708
4709
build(string const & name)4710 Dialog * GuiView::build(string const & name)
4711 {
4712 LASSERT(isValidName(name), return 0);
4713
4714 Dialog * dialog = createDialog(*this, name);
4715 if (dialog)
4716 return dialog;
4717
4718 if (name == "aboutlyx")
4719 return createGuiAbout(*this);
4720 if (name == "bibtex")
4721 return createGuiBibtex(*this);
4722 if (name == "changes")
4723 return createGuiChanges(*this);
4724 if (name == "character")
4725 return createGuiCharacter(*this);
4726 if (name == "citation")
4727 return createGuiCitation(*this);
4728 if (name == "compare")
4729 return createGuiCompare(*this);
4730 if (name == "comparehistory")
4731 return createGuiCompareHistory(*this);
4732 if (name == "document")
4733 return createGuiDocument(*this);
4734 if (name == "errorlist")
4735 return createGuiErrorList(*this);
4736 if (name == "external")
4737 return createGuiExternal(*this);
4738 if (name == "file")
4739 return createGuiShowFile(*this);
4740 if (name == "findreplace")
4741 return createGuiSearch(*this);
4742 if (name == "findreplaceadv")
4743 return createGuiSearchAdv(*this);
4744 if (name == "graphics")
4745 return createGuiGraphics(*this);
4746 if (name == "include")
4747 return createGuiInclude(*this);
4748 if (name == "index")
4749 return createGuiIndex(*this);
4750 if (name == "index_print")
4751 return createGuiPrintindex(*this);
4752 if (name == "listings")
4753 return createGuiListings(*this);
4754 if (name == "log")
4755 return createGuiLog(*this);
4756 if (name == "mathdelimiter")
4757 return createGuiDelimiter(*this);
4758 if (name == "mathmatrix")
4759 return createGuiMathMatrix(*this);
4760 if (name == "note")
4761 return createGuiNote(*this);
4762 if (name == "paragraph")
4763 return createGuiParagraph(*this);
4764 if (name == "phantom")
4765 return createGuiPhantom(*this);
4766 if (name == "prefs")
4767 return createGuiPreferences(*this);
4768 if (name == "ref")
4769 return createGuiRef(*this);
4770 if (name == "sendto")
4771 return createGuiSendTo(*this);
4772 if (name == "spellchecker")
4773 return createGuiSpellchecker(*this);
4774 if (name == "symbols")
4775 return createGuiSymbols(*this);
4776 if (name == "tabularcreate")
4777 return createGuiTabularCreate(*this);
4778 if (name == "texinfo")
4779 return createGuiTexInfo(*this);
4780 if (name == "thesaurus")
4781 return createGuiThesaurus(*this);
4782 if (name == "toc")
4783 return createGuiToc(*this);
4784 if (name == "view-source")
4785 return createGuiViewSource(*this);
4786 if (name == "wrap")
4787 return createGuiWrap(*this);
4788 if (name == "progress")
4789 return createGuiProgressView(*this);
4790
4791 return 0;
4792 }
4793
4794
SEMenu(QWidget * parent)4795 SEMenu::SEMenu(QWidget * parent)
4796 {
4797 QAction * action = addAction(qt_("Disable Shell Escape"));
4798 connect(action, SIGNAL(triggered()),
4799 parent, SLOT(disableShellEscape()));
4800 }
4801
4802
4803 } // namespace frontend
4804 } // namespace lyx
4805
4806 #include "moc_GuiView.cpp"
4807