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