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