]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/GuiView.cpp
Use <cstdint> instead of <boost/cstdint.hpp>
[lyx.git] / src / frontends / qt4 / GuiView.cpp
index a714960da90c2e3635cc97ef4d164abaa5e21625..6e87f007f93151b27ab99bf45d5fce837529f9a4 100644 (file)
@@ -19,6 +19,7 @@
 #include "FileDialog.h"
 #include "FontLoader.h"
 #include "GuiApplication.h"
+#include "GuiClickableLabel.h"
 #include "GuiCommandBuffer.h"
 #include "GuiCompleter.h"
 #include "GuiKeySymbol.h"
@@ -51,7 +52,9 @@
 #include "FuncStatus.h"
 #include "FuncRequest.h"
 #include "Intl.h"
+#include "Language.h"
 #include "Layout.h"
+#include "LayoutFile.h"
 #include "Lexer.h"
 #include "LyXAction.h"
 #include "LyX.h"
 #include <QMovie>
 #include <QPainter>
 #include <QPixmap>
-#include <QPixmapCache>
 #include <QPoint>
 #include <QPushButton>
 #include <QScrollBar>
@@ -157,6 +159,9 @@ public:
                if (!lyxrc.show_banner)
                        return;
                /// The text to be written on top of the pixmap
+               QString const htext = qt_("The Document\nProcessor[[welcome banner]]");
+               QString const htextsize = qt_("1.0[[possibly scale the welcome banner text size]]");
+               /// The text to be written on top of the pixmap
                QString const text = lyx_version ?
                        qt_("version ") + lyx_version : qt_("unknown version");
 #if QT_VERSION >= 0x050000
@@ -178,7 +183,14 @@ public:
                QPainter pain(&splash_);
                pain.setPen(QColor(0, 0, 0));
                qreal const fsize = fontSize();
-               QPointF const position = textPosition();
+               bool ok;
+               int hfsize = 20;
+               qreal locscale = htextsize.toFloat(&ok);
+               if (!ok)
+                       locscale = 1.0;
+               QPointF const position = textPosition(false);
+               QPointF const hposition = textPosition(true);
+               QRectF const hrect(hposition, splashSize());
                LYXERR(Debug::GUI,
                        "widget pixel ratio: " << pixelRatio() <<
                        " splash pixel ratio: " << splashPixelRatio() <<
@@ -190,6 +202,36 @@ public:
                font.setPointSizeF(fsize);
                pain.setFont(font);
                pain.drawText(position, text);
+               // The font used to display the version info
+               font.setStyleHint(QFont::SansSerif);
+               font.setWeight(QFont::Normal);
+               font.setPointSizeF(hfsize);
+               // Check how long the logo gets with the current font
+               // and adapt if the font is running wider than what
+               // we assume
+               QFontMetrics fm(font);
+               // Split the title into lines to measure the longest line
+               // in the current l7n.
+               QStringList titlesegs = htext.split('\n');
+               int wline = 0;
+               int hline = fm.height();
+               QStringList::const_iterator sit;
+               for (sit = titlesegs.constBegin(); sit != titlesegs.constEnd(); ++sit) {
+                       if (fm.width(*sit) > wline)
+                               wline = fm.width(*sit);
+               }
+               // The longest line in the reference font (for English)
+               // is 180. Calculate scale factor from that.
+               double const wscale = wline > 0 ? (180.0 / wline) : 1;
+               // Now do the same for the height (necessary for condensed fonts)
+               double const hscale = (34.0 / hline);
+               // take the lower of the two scale factors.
+               double const scale = min(wscale, hscale);
+               // Now rescale. Also consider l7n's offset factor.
+               font.setPointSizeF(hfsize * scale * locscale);
+
+               pain.setFont(font);
+               pain.drawText(hrect, Qt::AlignLeft, htext);
                setFocusPolicy(Qt::StrongFocus);
        }
 
@@ -227,18 +269,19 @@ private:
        /// Current ratio between physical pixels and device-independent pixels
        double pixelRatio() const {
 #if QT_VERSION >= 0x050000
-               return devicePixelRatio();
+               return qt_scale_factor * devicePixelRatio();
 #else
                return 1.0;
 #endif
        }
 
        qreal fontSize() const {
-               return toqstr(lyxrc.font_sizes[FONT_SIZE_NORMAL]).toDouble();
+               return toqstr(lyxrc.font_sizes[NORMAL_SIZE]).toDouble();
        }
 
-       QPointF textPosition() const {
-               return QPointF(width_/2 - 18, height_/2 + 45);
+       QPointF textPosition(bool const heading) const {
+               return heading ? QPointF(width_/2 - 18, height_/2 - 45)
+                              : QPointF(width_/2 - 18, height_/2 + 45);
        }
 
        QSize splashSize() const {
@@ -263,7 +306,7 @@ typedef map<string, GuiToolbar *> ToolbarMap;
 
 typedef shared_ptr<Dialog> DialogPtr;
 
-} // namespace anon
+} // namespace
 
 
 class GuiView::GuiViewPrivate
@@ -327,62 +370,6 @@ public:
                delete stack_widget_;
        }
 
-       QMenu * toolBarPopup(GuiView * parent)
-       {
-               // FIXME: translation
-               QMenu * menu = new QMenu(parent);
-               QActionGroup * iconSizeGroup = new QActionGroup(parent);
-
-               QAction * smallIcons = new QAction(iconSizeGroup);
-               smallIcons->setText(qt_("Small-sized icons"));
-               smallIcons->setCheckable(true);
-               QObject::connect(smallIcons, SIGNAL(triggered()),
-                       parent, SLOT(smallSizedIcons()));
-               menu->addAction(smallIcons);
-
-               QAction * normalIcons = new QAction(iconSizeGroup);
-               normalIcons->setText(qt_("Normal-sized icons"));
-               normalIcons->setCheckable(true);
-               QObject::connect(normalIcons, SIGNAL(triggered()),
-                       parent, SLOT(normalSizedIcons()));
-               menu->addAction(normalIcons);
-
-               QAction * bigIcons = new QAction(iconSizeGroup);
-               bigIcons->setText(qt_("Big-sized icons"));
-               bigIcons->setCheckable(true);
-               QObject::connect(bigIcons, SIGNAL(triggered()),
-                       parent, SLOT(bigSizedIcons()));
-               menu->addAction(bigIcons);
-
-               QAction * hugeIcons = new QAction(iconSizeGroup);
-               hugeIcons->setText(qt_("Huge-sized icons"));
-               hugeIcons->setCheckable(true);
-               QObject::connect(hugeIcons, SIGNAL(triggered()),
-                       parent, SLOT(hugeSizedIcons()));
-               menu->addAction(hugeIcons);
-
-               QAction * giantIcons = new QAction(iconSizeGroup);
-               giantIcons->setText(qt_("Giant-sized icons"));
-               giantIcons->setCheckable(true);
-               QObject::connect(giantIcons, SIGNAL(triggered()),
-                       parent, SLOT(giantSizedIcons()));
-               menu->addAction(giantIcons);
-
-               unsigned int cur = parent->iconSize().width();
-               if ( cur == parent->d.smallIconSize)
-                       smallIcons->setChecked(true);
-               else if (cur == parent->d.normalIconSize)
-                       normalIcons->setChecked(true);
-               else if (cur == parent->d.bigIconSize)
-                       bigIcons->setChecked(true);
-               else if (cur == parent->d.hugeIconSize)
-                       hugeIcons->setChecked(true);
-               else if (cur == parent->d.giantIconSize)
-                       giantIcons->setChecked(true);
-
-               return menu;
-       }
-
        void setBackground()
        {
                stack_widget_->setCurrentWidget(bg_widget_);
@@ -439,6 +426,60 @@ public:
                processing_thread_watcher_.setFuture(f);
        }
 
+       QSize iconSize(docstring const & icon_size)
+       {
+               unsigned int size;
+               if (icon_size == "small")
+                       size = smallIconSize;
+               else if (icon_size == "normal")
+                       size = normalIconSize;
+               else if (icon_size == "big")
+                       size = bigIconSize;
+               else if (icon_size == "huge")
+                       size = hugeIconSize;
+               else if (icon_size == "giant")
+                       size = giantIconSize;
+               else
+                       size = icon_size.empty() ? normalIconSize : convert<int>(icon_size);
+
+               if (size < smallIconSize)
+                       size = smallIconSize;
+
+               return QSize(size, size);
+       }
+
+       QSize iconSize(QString const & icon_size)
+       {
+               return iconSize(qstring_to_ucs4(icon_size));
+       }
+
+       string & iconSize(QSize const & qsize)
+       {
+               LATTEST(qsize.width() == qsize.height());
+
+               static string icon_size;
+
+               unsigned int size = qsize.width();
+
+               if (size < smallIconSize)
+                       size = smallIconSize;
+
+               if (size == smallIconSize)
+                       icon_size = "small";
+               else if (size == normalIconSize)
+                       icon_size = "normal";
+               else if (size == bigIconSize)
+                       icon_size = "big";
+               else if (size == hugeIconSize)
+                       icon_size = "huge";
+               else if (size == giantIconSize)
+                       icon_size = "giant";
+               else
+                       icon_size = convert<string>(size);
+
+               return icon_size;
+       }
+
 public:
        GuiView * gv_;
        GuiWorkArea * current_work_area_;
@@ -486,13 +527,17 @@ public:
        string processing_format;
 
        static QSet<Buffer const *> busyBuffers;
-       static Buffer::ExportStatus previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
-       static Buffer::ExportStatus exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
-       static Buffer::ExportStatus compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
+       static Buffer::ExportStatus previewAndDestroy(Buffer const * orig,
+                       Buffer * buffer, string const & format);
+       static Buffer::ExportStatus exportAndDestroy(Buffer const * orig,
+                       Buffer * buffer, string const & format);
+       static Buffer::ExportStatus compileAndDestroy(Buffer const * orig,
+                       Buffer * buffer, string const & format);
        static docstring autosaveAndDestroy(Buffer const * orig, Buffer * buffer);
 
        template<class T>
-       static Buffer::ExportStatus runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format);
+       static Buffer::ExportStatus runAndDestroy(const T& func,
+                       Buffer const * orig, Buffer * buffer, string const & format);
 
        // TODO syncFunc/previewFunc: use bind
        bool asyncBufferProcessing(string const & argument,
@@ -500,7 +545,8 @@ public:
                                   docstring const & msg,
                                   Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
                                   Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
-                                  Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const);
+                                  Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const,
+                                  bool allow_async);
 
        QVector<GuiWorkArea*> guiWorkAreas();
 };
@@ -510,13 +556,14 @@ QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
 
 GuiView::GuiView(int id)
        : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0),
-         command_execute_(false), minibuffer_focus_(false)
+         command_execute_(false), minibuffer_focus_(false), toolbarsMovable_(true),
+         devel_mode_(false)
 {
        connect(this, SIGNAL(bufferViewChanged()),
-               this, SLOT(on_bufferViewChanged()));
+               this, SLOT(onBufferViewChanged()));
 
        // GuiToolbars *must* be initialised before the menu bar.
-       normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
+       setIconSize(QSize(d.normalIconSize, d.normalIconSize)); // at least on Mac the default is 32 otherwise, which is huge
        constructToolbars();
 
        // set ourself as the current view. This is needed for the menu bar
@@ -531,7 +578,8 @@ GuiView::GuiView(int id)
 
        // Start autosave timer
        if (lyxrc.autosave) {
-               d.autosave_timeout_.timeout.connect(bind(&GuiView::autoSave, this));
+               // The connection is closed when this is destroyed.
+               d.autosave_timeout_.timeout.connect([this](){ autoSave();});
                d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
                d.autosave_timeout_.start();
        }
@@ -563,7 +611,7 @@ GuiView::GuiView(int id)
        setAcceptDrops(true);
 
        // add busy indicator to statusbar
-       QLabel * busylabel = new QLabel(statusBar());
+       GuiClickableLabel * busylabel = new GuiClickableLabel(statusBar());
        statusBar()->addPermanentWidget(busylabel);
        search_mode mode = theGuiApp()->imageSearchMode();
        QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
@@ -572,15 +620,32 @@ GuiView::GuiView(int id)
        busyanim->start();
        busylabel->hide();
 
-       connect(&d.processing_thread_watcher_, SIGNAL(started()), 
+       connect(&d.processing_thread_watcher_, SIGNAL(started()),
                busylabel, SLOT(show()));
-       connect(&d.processing_thread_watcher_, SIGNAL(finished()), 
+       connect(&d.processing_thread_watcher_, SIGNAL(finished()),
                busylabel, SLOT(hide()));
+       connect(busylabel, SIGNAL(clicked()), this, SLOT(checkCancelBackground()));
 
        QFontMetrics const fm(statusBar()->fontMetrics());
-       int const roheight = max(int(d.normalIconSize), fm.height());
-       QSize const rosize(roheight, roheight);
-       QPixmap readonly = QIcon(getPixmap("images/", "emblem-readonly", "svgz,png")).pixmap(rosize);
+       int const iconheight = max(int(d.normalIconSize), fm.height());
+       QSize const iconsize(iconheight, iconheight);
+
+       QPixmap shellescape = QIcon(getPixmap("images/", "emblem-shellescape", "svgz,png")).pixmap(iconsize);
+       shell_escape_ = new QLabel(statusBar());
+       shell_escape_->setPixmap(shellescape);
+       shell_escape_->setScaledContents(true);
+       shell_escape_->setAlignment(Qt::AlignCenter);
+       shell_escape_->setContextMenuPolicy(Qt::CustomContextMenu);
+       shell_escape_->setToolTip(qt_("WARNING: LaTeX is allowed to execute "
+                                     "external commands for this document. "
+                                     "Right click to change."));
+       SEMenu * menu = new SEMenu(this);
+       connect(shell_escape_, SIGNAL(customContextMenuRequested(QPoint)),
+               menu, SLOT(showMenu(QPoint)));
+       shell_escape_->hide();
+       statusBar()->addPermanentWidget(shell_escape_);
+
+       QPixmap readonly = QIcon(getPixmap("images/", "emblem-readonly", "svgz,png")).pixmap(iconsize);
        read_only_ = new QLabel(statusBar());
        read_only_->setPixmap(readonly);
        read_only_->setScaledContents(true);
@@ -608,6 +673,11 @@ GuiView::GuiView(int id)
        connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
                SLOT(doShowDialog(QString const &, QString const &, Inset *)));
 
+       // set custom application bars context menu, e.g. tool bar and menu bar
+       setContextMenuPolicy(Qt::CustomContextMenu);
+       connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
+               SLOT(toolBarPopup(const QPoint &)));
+
        // Forbid too small unresizable window because it can happen
        // with some window manager under X11.
        setMinimumSize(300, 200);
@@ -634,6 +704,29 @@ GuiView::~GuiView()
 }
 
 
+void GuiView::disableShellEscape()
+{
+       BufferView * bv = documentBufferView();
+       if (!bv)
+               return;
+       theSession().shellescapeFiles().remove(bv->buffer().absFileName());
+       bv->buffer().params().shell_escape = false;
+       bv->processUpdateFlags(Update::Force);
+}
+
+
+void GuiView::checkCancelBackground()
+{
+       docstring const ttl = _("Cancel Export?");
+       docstring const msg = _("Do you want to cancel the background export process?");
+       int const ret =
+               Alert::prompt(ttl, msg, 1, 1,
+                       _("&Cancel export"), _("Co&ntinue"));
+       if (ret == 0)
+               Systemcall::killscript();
+}
+
+
 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
 {
        QVector<GuiWorkArea*> areas;
@@ -649,7 +742,7 @@ QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
        string const & format)
 {
-       docstring const fmt = formats.prettyName(format);
+       docstring const fmt = theFormats().prettyName(format);
        docstring msg;
        switch (status) {
        case Buffer::ExportSuccess:
@@ -670,6 +763,9 @@ static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
        case Buffer::PreviewError:
                msg = bformat(_("Error while previewing format: %1$s"), fmt);
                break;
+       case Buffer::ExportKilled:
+               msg = bformat(_("Conversion cancelled while previewing format: %1$s"), fmt);
+               break;
        }
        view->message(msg);
 }
@@ -687,14 +783,26 @@ void GuiView::processingThreadFinished()
 
        Buffer::ExportStatus const status = watcher->result();
        handleExportStatus(this, status, d.processing_format);
-       
+
        updateToolbars();
        BufferView const * const bv = currentBufferView();
        if (bv && !bv->buffer().errorList("Export").empty()) {
                errors("Export");
                return;
        }
-       errors(d.last_export_format);
+
+       bool const error = (status != Buffer::ExportSuccess &&
+                           status != Buffer::PreviewSuccess &&
+                           status != Buffer::ExportCancel);
+       if (error && bv) {
+               ErrorList & el = bv->buffer().errorList(d.last_export_format);
+               // at this point, we do not know if buffer-view or
+               // master-buffer-view was called. If there was an export error,
+               // and the current buffer's error log is empty, we guess that
+               // it must be master-buffer-view that was called so we set
+               // from_master=true.
+               errors(d.last_export_format, el.empty());
+       }
 }
 
 
@@ -710,6 +818,8 @@ void GuiView::autoSaveThreadFinished()
 void GuiView::saveLayout() const
 {
        QSettings settings;
+       settings.setValue("zoom_ratio", zoom_ratio_);
+       settings.setValue("devel_mode", devel_mode_);
        settings.beginGroup("views");
        settings.beginGroup(QString::number(id_));
 #if defined(Q_WS_X11) || defined(QPA_XCB)
@@ -719,26 +829,35 @@ void GuiView::saveLayout() const
        settings.setValue("geometry", saveGeometry());
 #endif
        settings.setValue("layout", saveState(0));
-       settings.setValue("icon_size", iconSize());
+       settings.setValue("icon_size", toqstr(d.iconSize(iconSize())));
 }
 
 
 void GuiView::saveUISettings() const
 {
+       QSettings settings;
+
        // Save the toolbar private states
        ToolbarMap::iterator end = d.toolbars_.end();
        for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
-               it->second->saveSession();
+               it->second->saveSession(settings);
        // Now take care of all other dialogs
        map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
        for (; it!= d.dialogs_.end(); ++it)
-               it->second->saveSession();
+               it->second->saveSession(settings);
 }
 
 
 bool GuiView::restoreLayout()
 {
        QSettings settings;
+       zoom_ratio_ = settings.value("zoom_ratio", 1.0).toDouble();
+       // Actual zoom value: default zoom + fractional offset
+       int zoom = lyxrc.defaultZoom * zoom_ratio_;
+       if (zoom < static_cast<int>(zoom_min_))
+               zoom = zoom_min_;
+       lyxrc.currentZoom = zoom;
+       devel_mode_ = settings.value("devel_mode", devel_mode_).toBool();
        settings.beginGroup("views");
        settings.beginGroup(QString::number(id_));
        QString const icon_key = "icon_size";
@@ -746,17 +865,7 @@ bool GuiView::restoreLayout()
                return false;
 
        //code below is skipped when when ~/.config/LyX is (re)created
-       QSize icon_size = settings.value(icon_key).toSize();
-       // Check whether session size changed.
-       if (icon_size.width() != int(d.smallIconSize) &&
-           icon_size.width() != int(d.normalIconSize) &&
-           icon_size.width() != int(d.bigIconSize) &&
-           icon_size.width() != int(d.hugeIconSize) &&
-           icon_size.width() != int(d.giantIconSize)) {
-               icon_size.setWidth(d.normalIconSize);
-               icon_size.setHeight(d.normalIconSize);
-       }
-       setIconSize(icon_size);
+       setIconSize(d.iconSize(settings.value(icon_key).toString()));
 
 #if defined(Q_WS_X11) || defined(QPA_XCB)
        QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
@@ -787,7 +896,7 @@ bool GuiView::restoreLayout()
 
        if (!restoreState(settings.value("layout").toByteArray(), 0))
                initToolbars();
-       
+
        // init the toolbars that have not been restored
        Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
        Toolbars::Infos::iterator end = guiApp->toolbars().end();
@@ -797,6 +906,9 @@ bool GuiView::restoreLayout()
                        initToolbar(cit->name);
        }
 
+       // update lock (all) toolbars positions
+       updateLockToolbars();
+
        updateDialogs();
        return true;
 }
@@ -813,6 +925,17 @@ GuiToolbar * GuiView::toolbar(string const & name)
 }
 
 
+void GuiView::updateLockToolbars()
+{
+       toolbarsMovable_ = false;
+       for (ToolbarInfo const & info : guiApp->toolbars()) {
+               GuiToolbar * tb = toolbar(info.name);
+               if (tb && tb->isMovable())
+                       toolbarsMovable_ = true;
+       }
+}
+
+
 void GuiView::constructToolbars()
 {
        ToolbarMap::iterator it = d.toolbars_.begin();
@@ -880,6 +1003,8 @@ void GuiView::initToolbar(string const & name)
 
        if (visibility & Toolbars::ON)
                tb->setVisible(true);
+
+       tb->setMovable(true);
 }
 
 
@@ -921,12 +1046,6 @@ void GuiView::focusInEvent(QFocusEvent * e)
 }
 
 
-QMenu * GuiView::createPopupMenu()
-{
-       return d.toolBarPopup(this);
-}
-
-
 void GuiView::showEvent(QShowEvent * e)
 {
        LYXERR(Debug::GUI, "Passed Geometry "
@@ -983,7 +1102,7 @@ void GuiView::closeEvent(QCloseEvent * close_event)
        LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
 
        if (!GuiViewPrivate::busyBuffers.isEmpty()) {
-               Alert::warning(_("Exit LyX"), 
+               Alert::warning(_("Exit LyX"),
                        _("LyX could not be closed because documents are being processed by LyX."));
                close_event->setAccepted(false);
                return;
@@ -1114,36 +1233,6 @@ void GuiView::updateStatusBarMessage(QString const & str)
 }
 
 
-void GuiView::smallSizedIcons()
-{
-       setIconSize(QSize(d.smallIconSize, d.smallIconSize));
-}
-
-
-void GuiView::normalSizedIcons()
-{
-       setIconSize(QSize(d.normalIconSize, d.normalIconSize));
-}
-
-
-void GuiView::bigSizedIcons()
-{
-       setIconSize(QSize(d.bigIconSize, d.bigIconSize));
-}
-
-
-void GuiView::hugeSizedIcons()
-{
-       setIconSize(QSize(d.hugeIconSize, d.hugeIconSize));
-}
-
-
-void GuiView::giantSizedIcons()
-{
-       setIconSize(QSize(d.giantIconSize, d.giantIconSize));
-}
-
-
 void GuiView::clearMessage()
 {
        // FIXME: This code was introduced in r19643 to fix bug #4123. However,
@@ -1164,6 +1253,12 @@ void GuiView::updateWindowTitle(GuiWorkArea * wa)
        Buffer const & buf = wa->bufferView().buffer();
        // Set the windows title
        docstring title = buf.fileName().displayName(130) + from_ascii("[*]");
+       if (buf.notifiesExternalModification()) {
+               title = bformat(_("%1$s (modified externally)"), title);
+               // If the external modification status has changed, then maybe the status of
+               // buffer-save has changed too.
+               updateToolbars();
+       }
 #ifndef Q_WS_MAC
        title += from_ascii(" - LyX");
 #endif
@@ -1175,7 +1270,12 @@ void GuiView::updateWindowTitle(GuiWorkArea * wa)
        // Tell Qt whether the current document is changed
        setWindowModified(!buf.isClean());
 
-       if (buf.isReadonly())
+       if (buf.params().shell_escape)
+               shell_escape_->show();
+       else
+               shell_escape_->hide();
+
+       if (buf.hasReadonlyFlag())
                read_only_->show();
        else
                read_only_->hide();
@@ -1210,7 +1310,7 @@ void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
 }
 
 
-void GuiView::on_bufferViewChanged()
+void GuiView::onBufferViewChanged()
 {
        structureChanged();
        // Buffer-dependent dialogs must be updated. This is done here because
@@ -1271,7 +1371,7 @@ void GuiView::showMessage()
        if (msg.isEmpty()) {
                BufferView const * bv = currentBufferView();
                if (bv)
-                       msg = toqstr(bv->cursor().currentState());
+                       msg = toqstr(bv->cursor().currentState(devel_mode_));
                else
                        msg = qt_("Welcome to LyX!");
        }
@@ -1370,7 +1470,7 @@ void GuiView::setBusy(bool busy)
                return;
        }
        QApplication::restoreOverrideCursor();
-       updateLayoutList();     
+       updateLayoutList();
 }
 
 
@@ -1384,18 +1484,18 @@ void GuiView::resetCommandExecute()
 double GuiView::pixelRatio() const
 {
 #if QT_VERSION >= 0x050000
-       return devicePixelRatio();
+       return qt_scale_factor * devicePixelRatio();
 #else
        return 1.0;
 #endif
 }
-       
-       
+
+
 GuiWorkArea * GuiView::workArea(int index)
 {
        if (TabWorkArea * twa = d.currentTabWorkArea())
                if (index < twa->count())
-                       return dynamic_cast<GuiWorkArea *>(twa->widget(index));
+                       return twa->workArea(index);
        return 0;
 }
 
@@ -1484,7 +1584,7 @@ void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
 
        theGuiApp()->setCurrentView(this);
        d.current_work_area_ = wa;
-       
+
        // We need to reset this now, because it will need to be
        // right if the tabWorkArea gets reset in the for loop. We
        // will change it back if we aren't in that case.
@@ -1493,14 +1593,14 @@ void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
 
        for (int i = 0; i != d.splitter_->count(); ++i) {
                if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
-                       LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() 
+                       LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea()
                                << ", Current main wa: " << currentMainWorkArea());
                        return;
                }
        }
-       
+
        d.current_main_work_area_ = old_cmwa;
-       
+
        LYXERR(Debug::DEBUG, "This is not a tabbed wa");
        on_currentWorkAreaChanged(wa);
        BufferView & bv = wa->bufferView();
@@ -1604,11 +1704,11 @@ void GuiView::updateToolbars()
 }
 
 
-void GuiView::setBuffer(Buffer * newBuffer)
+void GuiView::setBuffer(Buffer * newBuffer, bool switch_to)
 {
        LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
        LASSERT(newBuffer, return);
-       
+
        GuiWorkArea * wa = workArea(*newBuffer);
        if (wa == 0) {
                setBusy(true);
@@ -1627,7 +1727,8 @@ void GuiView::setBuffer(Buffer * newBuffer)
        }
        connectBuffer(*newBuffer);
        connectBufferView(wa->bufferView());
-       setCurrentWorkArea(wa);
+       if (switch_to)
+               setCurrentWorkArea(wa);
 }
 
 
@@ -1663,27 +1764,17 @@ void GuiView::errors(string const & error_type, bool from_master)
        if (!bv)
                return;
 
-#if EXPORT_in_THREAD
-       // We are called with from_master == false by default, so we
-       // have to figure out whether that is the case or not.
-       ErrorList & el = bv->buffer().errorList(error_type);
-       if (el.empty()) {
-           el = bv->buffer().masterBuffer()->errorList(error_type);
-           from_master = true;
-       }
-#else
        ErrorList const & el = from_master ?
                bv->buffer().masterBuffer()->errorList(error_type) :
                bv->buffer().errorList(error_type);
-#endif
 
        if (el.empty())
                return;
 
-       string data = error_type;
+       string err = error_type;
        if (from_master)
-               data = "from_master|" + error_type;
-       showDialog("errorlist", data);
+               err = "from_master|" + error_type;
+       showDialog("errorlist", err);
 }
 
 
@@ -1695,6 +1786,10 @@ void GuiView::updateTocItem(string const & type, DocIterator const & dit)
 
 void GuiView::structureChanged()
 {
+       // This is called from the Buffer, which has no way to ensure that cursors
+       // in BufferView remain valid.
+       if (documentBufferView())
+               documentBufferView()->cursor().sanitize();
        // FIXME: This is slightly expensive, though less than the tocBackend update
        // (#9880). This also resets the view in the Toc Widget (#6675).
        d.toc_models_.reset(documentBufferView());
@@ -1704,7 +1799,7 @@ void GuiView::structureChanged()
 }
 
 
-void GuiView::updateDialog(string const & name, string const & data)
+void GuiView::updateDialog(string const & name, string const & sdata)
 {
        if (!isDialogVisible(name))
                return;
@@ -1715,7 +1810,7 @@ void GuiView::updateDialog(string const & name, string const & data)
 
        Dialog * const dialog = it->second.get();
        if (dialog->isVisibleView())
-               dialog->initialiseParams(data);
+               dialog->initialiseParams(sdata);
 }
 
 
@@ -1828,6 +1923,15 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
        case LFUN_BUFFER_IMPORT:
                break;
 
+       case LFUN_MASTER_BUFFER_EXPORT:
+               enable = doc_buffer
+                       && (doc_buffer->parent() != 0
+                           || doc_buffer->hasChildren())
+                       && !d.processing_thread_watcher_.isRunning()
+                       // this launches a dialog, which would be in the wrong Buffer
+                       && !(::lyx::operator==(cmd.argument(), "custom"));
+               break;
+
        case LFUN_MASTER_BUFFER_UPDATE:
        case LFUN_MASTER_BUFFER_VIEW:
                enable = doc_buffer
@@ -1852,8 +1956,7 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
        case LFUN_BUFFER_RELOAD:
                enable = doc_buffer && !doc_buffer->isUnnamed()
                        && doc_buffer->fileName().exists()
-                       && (!doc_buffer->isClean()
-                          || doc_buffer->isExternallyModified(Buffer::timestamp_method));
+                       && (!doc_buffer->isClean() || doc_buffer->notifiesExternalModification());
                break;
 
        case LFUN_BUFFER_CHILD_OPEN:
@@ -1883,11 +1986,34 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                break;
        }
 
-       case LFUN_BUFFER_WRITE_AS:
+       case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
+               enable = doc_buffer && doc_buffer->notifiesExternalModification();
+               break;
+
+       case LFUN_BUFFER_EXPORT: {
+               if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
+                       enable = false;
+                       break;
+               }
+               return doc_buffer->getStatus(cmd, flag);
+               break;
+       }
+
        case LFUN_BUFFER_EXPORT_AS:
+               if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
+                       enable = false;
+                       break;
+               }
+               // fall through
+       case LFUN_BUFFER_WRITE_AS:
+       case LFUN_BUFFER_WRITE_AS_TEMPLATE:
                enable = doc_buffer != 0;
                break;
 
+       case LFUN_EXPORT_CANCEL:
+               enable = d.processing_thread_watcher_.isRunning();
+               break;
+
        case LFUN_BUFFER_CLOSE:
        case LFUN_VIEW_CLOSE:
                enable = doc_buffer != 0;
@@ -1897,6 +2023,23 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                enable = theBufferList().last() != theBufferList().first();
                break;
 
+       case LFUN_BUFFER_CHKTEX: {
+               // hide if we have no checktex command
+               if (lyxrc.chktex_command.empty()) {
+                       flag.setUnknown(true);
+                       enable = false;
+                       break;
+               }
+               if (!doc_buffer || !doc_buffer->params().isLatex()
+                   || d.processing_thread_watcher_.isRunning()) {
+                       // grey out, don't hide
+                       enable = false;
+                       break;
+               }
+               enable = true;
+               break;
+       }
+
        case LFUN_VIEW_SPLIT:
                if (cmd.getArg(0) == "vertical")
                        enable = doc_buffer && (d.splitter_->count() == 1 ||
@@ -1910,6 +2053,10 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                enable = d.tabWorkAreaCount() > 1;
                break;
 
+       case LFUN_DEVEL_MODE_TOGGLE:
+               flag.setOnOff(devel_mode_);
+               break;
+
        case LFUN_TOOLBAR_TOGGLE: {
                string const name = cmd.getArg(0);
                if (GuiToolbar * t = toolbar(name))
@@ -1923,6 +2070,27 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                break;
        }
 
+       case LFUN_TOOLBAR_MOVABLE: {
+               string const name = cmd.getArg(0);
+               // use negation since locked == !movable
+               if (name == "*")
+                       // toolbar name * locks all toolbars
+                       flag.setOnOff(!toolbarsMovable_);
+               else if (GuiToolbar * t = toolbar(name))
+                       flag.setOnOff(!(t->isMovable()));
+               else {
+                       enable = false;
+                       docstring const msg =
+                               bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
+                       flag.message(msg);
+               }
+               break;
+       }
+
+       case LFUN_ICON_SIZE:
+               flag.setOnOff(d.iconSize(cmd.argument()) == iconSize());
+               break;
+
        case LFUN_DROP_LAYOUTS_CHOICE:
                enable = buf != 0;
                break;
@@ -1940,12 +2108,14 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
 
        case LFUN_DIALOG_TOGGLE:
                flag.setOnOff(isDialogVisible(cmd.getArg(0)));
-               // fall through to set "enable"
+               // to set "enable"
+               // fall through
        case LFUN_DIALOG_SHOW: {
                string const name = cmd.getArg(0);
                if (!doc_buffer)
                        enable = name == "aboutlyx"
                                || name == "file" //FIXME: should be removed.
+                               || name == "lyxfiles"
                                || name == "prefs"
                                || name == "texinfo"
                                || name == "progress"
@@ -1962,7 +2132,7 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                else if (name == "latexlog")
                        enable = FileName(doc_buffer->logName()).isReadableFile();
                else if (name == "spellchecker")
-                       enable = theSpellChecker() 
+                       enable = theSpellChecker()
                                && !doc_buffer->isReadonly()
                                && !doc_buffer->text().empty();
                else if (name == "vclog")
@@ -2026,7 +2196,7 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                bool const neg_zoom =
                        convert<int>(cmd.argument()) < 0 ||
                        (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
-               if (lyxrc.zoom <= zoom_min_ && neg_zoom) {
+               if (lyxrc.currentZoom <= zoom_min_ && neg_zoom) {
                        docstring const msg =
                                bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
                        flag.message(msg);
@@ -2035,6 +2205,21 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                        enable = doc_buffer;
                break;
        }
+
+       case LFUN_BUFFER_ZOOM: {
+               bool const less_than_min_zoom =
+                       !cmd.argument().empty() && convert<int>(cmd.argument()) < zoom_min_;
+               if (lyxrc.currentZoom <= zoom_min_ && less_than_min_zoom) {
+                       docstring const msg =
+                               bformat(_("Zoom level cannot be less than %1$d%."), zoom_min_);
+                       flag.message(msg);
+                       enable = false;
+               }
+               else
+                       enable = doc_buffer;
+               break;
+       }
+
        case LFUN_BUFFER_MOVE_NEXT:
        case LFUN_BUFFER_MOVE_PREVIOUS:
                // we do not cycle when moving
@@ -2067,12 +2252,13 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
                break;
        case LFUN_VC_LOCKING_TOGGLE:
-               enable = doc_buffer && !doc_buffer->isReadonly()
+               enable = doc_buffer && !doc_buffer->hasReadonlyFlag()
                        && doc_buffer->lyxvc().lockingToggleEnabled();
                flag.setOnOff(enable && doc_buffer->lyxvc().locking());
                break;
        case LFUN_VC_REVERT:
-               enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
+               enable = doc_buffer && doc_buffer->lyxvc().inUse()
+                       && !doc_buffer->hasReadonlyFlag();
                break;
        case LFUN_VC_UNDO_LAST:
                enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
@@ -2121,8 +2307,8 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
 static FileName selectTemplateFile()
 {
        FileDialog dlg(qt_("Select template file"));
-       dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
-       dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
+       dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
+       dlg.setButton2(qt_("&Templates"), toqstr(lyxrc.template_path));
 
        FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
                                 QStringList(qt_("LyX Documents (*.lyx)")));
@@ -2156,8 +2342,10 @@ Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
        setBuffer(newBuffer);
        newBuffer->errors("Parse");
 
-       if (tolastfiles)
+       if (tolastfiles) {
                theSession().lastFiles().add(filename);
+               theSession().writeFile();
+  }
 
        return newBuffer;
 }
@@ -2178,9 +2366,8 @@ void GuiView::openDocument(string const & fname)
 
        if (fname.empty()) {
                FileDialog dlg(qt_("Select document to open"));
-               dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
-               dlg.setButton2(qt_("Examples|#E#e"),
-                               toqstr(addPath(package().system_support().absFileName(), "examples")));
+               dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
+               dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path));
 
                QStringList const filter(qt_("LyX Documents (*.lyx)"));
                FileDialog::Result result =
@@ -2257,9 +2444,9 @@ static bool import(GuiView * lv, FileName const & filename,
 
                        string const tofile =
                                support::changeExtension(filename.absFileName(),
-                               formats.extension(*it));
-                       if (!theConverters().convert(0, filename, FileName(tofile),
-                               filename, format, *it, errorList))
+                               theFormats().extension(*it));
+                       if (theConverters().convert(0, filename, FileName(tofile),
+                               filename, format, *it, errorList) != Converters::SUCCESS)
                                return false;
                        loader_format = *it;
                        break;
@@ -2267,7 +2454,7 @@ static bool import(GuiView * lv, FileName const & filename,
                if (loader_format.empty()) {
                        frontend::Alert::error(_("Couldn't import file"),
                                         bformat(_("No information for importing the format %1$s."),
-                                        formats.prettyName(format)));
+                                        theFormats().prettyName(format)));
                        return false;
                }
        } else
@@ -2285,7 +2472,7 @@ static bool import(GuiView * lv, FileName const & filename,
                bool as_paragraphs = loader_format == "textparagraph";
                string filename2 = (loader_format == format) ? filename.absFileName()
                        : support::changeExtension(filename.absFileName(),
-                                         formats.extension(loader_format));
+                                         theFormats().extension(loader_format));
                lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
                        as_paragraphs);
                guiApp->setCurrentView(lv);
@@ -2314,17 +2501,16 @@ void GuiView::importDocument(string const & argument)
                }
 
                docstring const text = bformat(_("Select %1$s file to import"),
-                       formats.prettyName(format));
+                       theFormats().prettyName(format));
 
                FileDialog dlg(toqstr(text));
-               dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
-               dlg.setButton2(qt_("Examples|#E#e"),
-                       toqstr(addPath(package().system_support().absFileName(), "examples")));
+               dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
+               dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path));
 
-               docstring filter = formats.prettyName(format);
+               docstring filter = theFormats().prettyName(format);
                filter += " (*.{";
                // FIXME UNICODE
-               filter += from_utf8(formats.extensions(format));
+               filter += from_utf8(theFormats().extensions(format));
                filter += "})";
 
                FileDialog::Result result =
@@ -2398,7 +2584,8 @@ void GuiView::importDocument(string const & argument)
 }
 
 
-void GuiView::newDocument(string const & filename, bool from_template)
+void GuiView::newDocument(string const & filename, string templatefile,
+                         bool from_template)
 {
        FileName initpath(lyxrc.document_path);
        if (documentBufferView()) {
@@ -2408,9 +2595,9 @@ void GuiView::newDocument(string const & filename, bool from_template)
                        initpath = trypath;
        }
 
-       string templatefile;
        if (from_template) {
-               templatefile = selectTemplateFile().absFileName();
+               if (templatefile.empty())
+                       templatefile =  selectTemplateFile().absFileName();
                if (templatefile.empty())
                        return;
        }
@@ -2432,7 +2619,7 @@ void GuiView::newDocument(string const & filename, bool from_template)
 }
 
 
-void GuiView::insertLyXFile(docstring const & fname)
+void GuiView::insertLyXFile(docstring const & fname, bool ignorelang)
 {
        BufferView * bv = documentBufferView();
        if (!bv)
@@ -2451,10 +2638,8 @@ void GuiView::insertLyXFile(docstring const & fname)
 
                // FIXME UNICODE
                FileDialog dlg(qt_("Select LyX document to insert"));
-               dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
-               dlg.setButton2(qt_("Examples|#E#e"),
-                       toqstr(addPath(package().system_support().absFileName(),
-                       "examples")));
+               dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
+               dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path));
 
                FileDialog::Result result = dlg.open(toqstr(initpath),
                                         QStringList(qt_("LyX Documents (*.lyx)")));
@@ -2473,34 +2658,116 @@ void GuiView::insertLyXFile(docstring const & fname)
                }
        }
 
-       bv->insertLyXFile(filename);
+       bv->insertLyXFile(filename, ignorelang);
        bv->buffer().errors("Parse");
 }
 
 
+string const GuiView::getTemplatesPath(Buffer & b)
+{
+       // We start off with the user's templates path
+       string result = addPath(package().user_support().absFileName(), "templates");
+       // Check for the document language
+       string const langcode = b.params().language->code();
+       string const shortcode = langcode.substr(0, 2);
+       if (!langcode.empty() && shortcode != "en") {
+               string subpath = addPath(result, shortcode);
+               string subpath_long = addPath(result, langcode);
+               // If we have a subdirectory for the language already,
+               // navigate there
+               FileName sp = FileName(subpath);
+               if (sp.isDirectory())
+                       result = subpath;
+               else if (FileName(subpath_long).isDirectory())
+                       result = subpath_long;
+               else {
+                       // Ask whether we should create such a subdirectory
+                       docstring const text =
+                               bformat(_("It is suggested to save the template in a subdirectory\n"
+                                         "appropriate to the document language (%1$s).\n"
+                                         "This subdirectory does not exists yet.\n"
+                                         "Do you want to create it?"),
+                                       _(b.params().language->display()));
+                       if (Alert::prompt(_("Create Language Directory?"),
+                                         text, 0, 1, _("&Yes, Create"), _("&No, Save Template in Parent Directory")) == 0) {
+                               // If the user agreed, we try to create it and report if this failed.
+                               if (!sp.createDirectory(0777))
+                                       Alert::error(_("Subdirectory creation failed!"),
+                                                    _("Could not create subdirectory.\n"
+                                                      "The template will be saved in the parent directory."));
+                               else
+                                       result = subpath;
+                       }
+               }
+       }
+       // Do we have a layout category?
+       string const cat = b.params().baseClass() ?
+                               b.params().baseClass()->category()
+                             : string();
+       if (!cat.empty()) {
+               string subpath = addPath(result, cat);
+               // If we have a subdirectory for the category already,
+               // navigate there
+               FileName sp = FileName(subpath);
+               if (sp.isDirectory())
+                       result = subpath;
+               else {
+                       // Ask whether we should create such a subdirectory
+                       docstring const text =
+                               bformat(_("It is suggested to save the template in a subdirectory\n"
+                                         "appropriate to the layout category (%1$s).\n"
+                                         "This subdirectory does not exists yet.\n"
+                                         "Do you want to create it?"),
+                                       _(cat));
+                       if (Alert::prompt(_("Create Category Directory?"),
+                                         text, 0, 1, _("&Yes, Create"), _("&No, Save Template in Parent Directory")) == 0) {
+                               // If the user agreed, we try to create it and report if this failed.
+                               if (!sp.createDirectory(0777))
+                                       Alert::error(_("Subdirectory creation failed!"),
+                                                    _("Could not create subdirectory.\n"
+                                                      "The template will be saved in the parent directory."));
+                               else
+                                       result = subpath;
+                       }
+               }
+       }
+       return result;
+}
+
+
 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
 {
        FileName fname = b.fileName();
        FileName const oldname = fname;
+       bool const as_template = (kind == LV_WRITE_AS_TEMPLATE);
 
        if (!newname.empty()) {
                // FIXME UNICODE
-               fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
+               if (as_template)
+                       fname = support::makeAbsPath(to_utf8(newname), getTemplatesPath(b));
+               else
+                       fname = support::makeAbsPath(to_utf8(newname),
+                                                    oldname.onlyPath().absFileName());
        } else {
                // Switch to this Buffer.
                setBuffer(&b);
 
                // No argument? Ask user through dialog.
                // FIXME UNICODE
-               FileDialog dlg(qt_("Choose a filename to save document as"));
-               dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
-               dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
+               QString const title = as_template ? qt_("Choose a filename to save template as")
+                                                 : qt_("Choose a filename to save document as");
+               FileDialog dlg(title);
+               dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
+               dlg.setButton2(qt_("&Templates"), toqstr(lyxrc.template_path));
 
                if (!isLyXFileName(fname.absFileName()))
                        fname.changeExtension(".lyx");
 
+               string const path = as_template ?
+                                       getTemplatesPath(b)
+                                     : fname.onlyPath().absFileName();
                FileDialog::Result result =
-                       dlg.save(toqstr(fname.onlyPath().absFileName()),
+                       dlg.save(toqstr(path),
                                   QStringList(qt_("LyX Documents (*.lyx)")),
                                         toqstr(fname.onlyFileName()));
 
@@ -2592,6 +2859,7 @@ bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kin
                break;
        }
        case LV_WRITE_AS:
+       case LV_WRITE_AS_TEMPLATE:
                break;
        }
        // LyXVC created the file already in case of LV_VC_RENAME or
@@ -2611,14 +2879,14 @@ bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
        FileName fname = b.fileName();
 
        FileDialog dlg(qt_("Choose a filename to export the document as"));
-       dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
+       dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
 
        QStringList types;
        QString const anyformat = qt_("Guess from extension (*.*)");
        types << anyformat;
 
        vector<Format const *> export_formats;
-       for (Format const & f : formats)
+       for (Format const & f : theFormats())
                if (f.documentFormat())
                        export_formats.push_back(&f);
        sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
@@ -2651,7 +2919,7 @@ bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
        string fmt_name;
        fname.set(fromqstr(result.second));
        if (filter == anyformat)
-               fmt_name = formats.getFormatFromExtension(fname.extension());
+               fmt_name = theFormats().getFormatFromExtension(fname.extension());
        else
                fmt_name = fmap[filter];
        LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
@@ -2700,6 +2968,7 @@ bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
        bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
        if (success) {
                theSession().lastFiles().add(b.fileName());
+               theSession().writeFile();
                return true;
        }
 
@@ -2794,7 +3063,7 @@ void GuiView::writeSession() const {
        for (int i = 0; i < d.splitter_->count(); ++i) {
                TabWorkArea * twa = d.tabWorkArea(i);
                for (int j = 0; j < twa->count(); ++j) {
-                       GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
+                       GuiWorkArea * wa = twa->workArea(j);
                        Buffer & buf = wa->bufferView().buffer();
                        theSession().lastOpened().add(buf.fileName(), wa == active_wa);
                }
@@ -2857,7 +3126,7 @@ bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
        Buffer & buf = wa->bufferView().buffer();
 
        if (GuiViewPrivate::busyBuffers.contains(&buf)) {
-               Alert::warning(_("Close document"), 
+               Alert::warning(_("Close document"),
                        _("Document could not be closed because it is being processed by LyX."));
                return false;
        }
@@ -2876,35 +3145,51 @@ bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
 
 bool GuiView::closeBuffer(Buffer & buf)
 {
-       // If we are in a close_event all children will be closed in some time,
-       // so no need to do it here. This will ensure that the children end up
-       // in the session file in the correct order. If we close the master
-       // buffer, we can close or release the child buffers here too.
        bool success = true;
-       if (!closing_) {
-               ListOfBuffers clist = buf.getChildren();
-               ListOfBuffers::const_iterator it = clist.begin();
-               ListOfBuffers::const_iterator const bend = clist.end();
-               for (; it != bend; ++it) {
-                       Buffer * child_buf = *it;
-                       if (theBufferList().isOthersChild(&buf, child_buf)) {
-                               child_buf->setParent(0);
-                               continue;
-                       }
+       ListOfBuffers clist = buf.getChildren();
+       ListOfBuffers::const_iterator it = clist.begin();
+       ListOfBuffers::const_iterator const bend = clist.end();
+       for (; it != bend; ++it) {
+               Buffer * child_buf = *it;
+               if (theBufferList().isOthersChild(&buf, child_buf)) {
+                       child_buf->setParent(0);
+                       continue;
+               }
 
-                       // FIXME: should we look in other tabworkareas?
-                       // ANSWER: I don't think so. I've tested, and if the child is
-                       // open in some other window, it closes without a problem.
-                       GuiWorkArea * child_wa = workArea(*child_buf);
-                       if (child_wa) {
-                               success = closeWorkArea(child_wa, true);
-                               if (!success)
-                                       break;
-                       } else {
-                               // In this case the child buffer is open but hidden.
-                               // It therefore should not (MUST NOT) be dirty!
-                               LATTEST(child_buf->isClean());
+               // FIXME: should we look in other tabworkareas?
+               // ANSWER: I don't think so. I've tested, and if the child is
+               // open in some other window, it closes without a problem.
+               GuiWorkArea * child_wa = workArea(*child_buf);
+               if (child_wa) {
+                       if (closing_)
+                               // If we are in a close_event all children will be closed in some time,
+                               // so no need to do it here. This will ensure that the children end up
+                               // in the session file in the correct order. If we close the master
+                               // buffer, we can close or release the child buffers here too.
+                               continue;
+                       success = closeWorkArea(child_wa, true);
+                       if (!success)
+                               break;
+               } else {
+                       // In this case the child buffer is open but hidden.
+                       // Even in this case, children can be dirty (e.g.,
+                       // after a label change in the master, see #11405).
+                       // Therefore, check this
+                       if (closing_ && (child_buf->isClean() || child_buf->paragraphs().empty()))
+                               // If we are in a close_event all children will be closed in some time,
+                               // so no need to do it here. This will ensure that the children end up
+                               // in the session file in the correct order. If we close the master
+                               // buffer, we can close or release the child buffers here too.
+                               continue;
+                       // Save dirty buffers also if closing_!
+                       if (saveBufferIfNeeded(*child_buf, false)) {
+                               child_buf->removeAutosaveFile();
                                theBufferList().release(child_buf);
+                       } else {
+                               // Saving of dirty children has been cancelled.
+                               // Cancel the whole process.
+                               success = false;
+                               break;
                        }
                }
        }
@@ -2958,11 +3243,17 @@ bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
        setBuffer(&buf);
 
        docstring file;
+       bool exists;
        // FIXME: Unicode?
-       if (buf.isUnnamed())
+       if (buf.isUnnamed()) {
                file = from_utf8(buf.fileName().onlyFileName());
-       else
-               file = buf.fileName().displayName(30);
+               exists = false;
+       } else {
+               FileName filename = buf.fileName();
+               filename.refresh();
+               file = filename.displayName(30);
+               exists = filename.exists();
+       }
 
        // Bring this window to top before asking questions.
        raise();
@@ -2978,10 +3269,17 @@ bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
                if (ret == 1)
                        ++ret;
        } else {
-               docstring const text = bformat(_("The document %1$s has unsaved changes."
-                       "\n\nDo you want to save the document or discard the changes?"), file);
-               ret = Alert::prompt(_("Save changed document?"),
-                       text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
+               docstring const text = exists ?
+                       bformat(_("The document %1$s has unsaved changes."
+                                 "\n\nDo you want to save the document or "
+                                 "discard the changes?"), file) :
+                       bformat(_("The document %1$s has not been saved yet."
+                                 "\n\nDo you want to save the document or "
+                                 "discard it entirely?"), file);
+               docstring const title = exists ?
+                       _("Save changed document?") : _("Save document?");
+               ret = Alert::prompt(title, text, 0, 2,
+                                   _("&Save"), _("&Discard"), _("&Cancel"));
        }
 
        switch (ret) {
@@ -3039,7 +3337,7 @@ void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
 {
        if (!documentBufferView())
                return;
-       
+
        if (TabWorkArea * twa = d.currentTabWorkArea()) {
                Buffer * const curbuf = &documentBufferView()->buffer();
                int nwa = twa->count();
@@ -3094,6 +3392,7 @@ static bool ensureBufferClean(Buffer * buffer)
 
 bool GuiView::reloadBuffer(Buffer & buf)
 {
+       currentBufferView()->cursor().reset();
        Buffer::ReadStatus status = buf.reload();
        return status == Buffer::ReadSuccess;
 }
@@ -3105,8 +3404,7 @@ void GuiView::checkExternallyModifiedBuffers()
        BufferList::iterator const bend = theBufferList().end();
        for (; bit != bend; ++bit) {
                Buffer * buf = *bit;
-               if (buf->fileName().exists()
-                       && buf->isExternallyModified(Buffer::checksum_method)) {
+               if (buf->fileName().exists() && buf->isChecksumModified()) {
                        docstring text = bformat(_("Document \n%1$s\n has been externally modified."
                                        " Reload now? Any local changes will be lost."),
                                        from_utf8(buf->absFileName()));
@@ -3140,7 +3438,7 @@ void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
        case LFUN_VC_COPY: {
                if (!buffer || !ensureBufferClean(buffer))
                        break;
-               if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
+               if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
                        if (buffer->lyxvc().isCheckInWithConfirmation()) {
                                // Some changes are not yet committed.
                                // We test here and not in getStatus(), since
@@ -3169,7 +3467,7 @@ void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
        case LFUN_VC_CHECK_IN:
                if (!buffer || !ensureBufferClean(buffer))
                        break;
-               if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
+               if (buffer->lyxvc().inUse() && !buffer->hasReadonlyFlag()) {
                        string log;
                        LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
                        dr.setMessage(log);
@@ -3193,7 +3491,7 @@ void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
 
        case LFUN_VC_LOCKING_TOGGLE:
                LASSERT(buffer, return);
-               if (!ensureBufferClean(buffer) || buffer->isReadonly())
+               if (!ensureBufferClean(buffer) || buffer->hasReadonlyFlag())
                        break;
                if (buffer->lyxvc().inUse()) {
                        string res = buffer->lyxvc().lockingToggle();
@@ -3280,10 +3578,9 @@ void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
 
                string rev1 = cmd.getArg(0);
                string f1, f2;
+               LATTEST(buffer)
 
                // f1
-               // it seems safe to assume we have a buffer
-               // coverity[FORWARD_NULL]
                if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
                        break;
 
@@ -3337,7 +3634,7 @@ void GuiView::openChildDocument(string const & fname)
 bool GuiView::goToFileRow(string const & argument)
 {
        string file_name;
-       int row;
+       int row = -1;
        size_t i = argument.find_last_of(' ');
        if (i != string::npos) {
                file_name = os::internal_path(trim(argument.substr(0, i)));
@@ -3404,8 +3701,16 @@ bool GuiView::goToFileRow(string const & argument)
 }
 
 
+void GuiView::toolBarPopup(const QPoint & /*pos*/)
+{
+       QMenu * menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
+       menu->exec(QCursor::pos());
+}
+
+
 template<class T>
-Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
+Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func,
+               Buffer const * orig, Buffer * clone, string const & format)
 {
        Buffer::ExportStatus const status = func(format);
 
@@ -3418,23 +3723,29 @@ Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffe
 }
 
 
-Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
+Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(
+               Buffer const * orig, Buffer * clone, string const & format)
 {
-       Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
+       Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const =
+                       &Buffer::doExport;
        return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
 }
 
 
-Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
+Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(
+               Buffer const * orig, Buffer * clone, string const & format)
 {
-       Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
+       Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const =
+                       &Buffer::doExport;
        return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
 }
 
 
-Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
+Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(
+               Buffer const * orig, Buffer * clone, string const & format)
 {
-       Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
+       Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const =
+                       &Buffer::preview;
        return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
 }
 
@@ -3445,7 +3756,8 @@ bool GuiView::GuiViewPrivate::asyncBufferProcessing(
                           docstring const & msg,
                           Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
                           Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
-                          Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
+                          Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const,
+                          bool allow_async)
 {
        if (!used_buffer)
                return false;
@@ -3459,35 +3771,50 @@ bool GuiView::GuiViewPrivate::asyncBufferProcessing(
                gv_->message(msg);
        }
 #if EXPORT_in_THREAD
-       GuiViewPrivate::busyBuffers.insert(used_buffer);
-       Buffer * cloned_buffer = used_buffer->cloneFromMaster();
-       if (!cloned_buffer) {
-               Alert::error(_("Export Error"),
-                            _("Error cloning the Buffer."));
-               return false;
+       if (allow_async) {
+               GuiViewPrivate::busyBuffers.insert(used_buffer);
+               Buffer * cloned_buffer = used_buffer->cloneWithChildren();
+               if (!cloned_buffer) {
+                       Alert::error(_("Export Error"),
+                                    _("Error cloning the Buffer."));
+                       return false;
+               }
+               QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
+                                       asyncFunc,
+                                       used_buffer,
+                                       cloned_buffer,
+                                       format);
+               setPreviewFuture(f);
+               last_export_format = used_buffer->params().bufferFormat();
+               (void) syncFunc;
+               (void) previewFunc;
+               // We are asynchronous, so we don't know here anything about the success
+               return true;
+       } else {
+               Buffer::ExportStatus status;
+               if (syncFunc) {
+                       status = (used_buffer->*syncFunc)(format, false);
+               } else if (previewFunc) {
+                       status = (used_buffer->*previewFunc)(format);
+               } else
+                       return false;
+               handleExportStatus(gv_, status, format);
+               (void) asyncFunc;
+               return (status == Buffer::ExportSuccess
+                               || status == Buffer::PreviewSuccess);
        }
-       QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
-                               asyncFunc,
-                               used_buffer,
-                               cloned_buffer,
-                               format);
-       setPreviewFuture(f);
-       last_export_format = used_buffer->params().bufferFormat();
-       (void) syncFunc;
-       (void) previewFunc;
-       // We are asynchronous, so we don't know here anything about the success
-       return true;
 #else
+       (void) allow_async;
        Buffer::ExportStatus status;
        if (syncFunc) {
                status = (used_buffer->*syncFunc)(format, true);
        } else if (previewFunc) {
-               status = (used_buffer->*previewFunc)(format); 
+               status = (used_buffer->*previewFunc)(format);
        } else
                return false;
        handleExportStatus(gv_, status, format);
        (void) asyncFunc;
-       return (status == Buffer::ExportSuccess 
+       return (status == Buffer::ExportSuccess
                        || status == Buffer::PreviewSuccess);
 #endif
 }
@@ -3499,15 +3826,21 @@ void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
 
        // Let the current BufferView dispatch its own actions.
        bv->dispatch(cmd, dr);
-       if (dr.dispatched())
+       if (dr.dispatched()) {
+               if (cmd.action() == LFUN_REDO || cmd.action() == LFUN_UNDO)
+                       updateDialog("document", "");
                return;
+       }
 
        // Try with the document BufferView dispatch if any.
        BufferView * doc_bv = documentBufferView();
        if (doc_bv && doc_bv != bv) {
                doc_bv->dispatch(cmd, dr);
-               if (dr.dispatched())
+               if (dr.dispatched()) {
+                       if (cmd.action() == LFUN_REDO || cmd.action() == LFUN_UNDO)
+                               updateDialog("document", "");
                        return;
+               }
        }
 
        // Then let the current Cursor dispatch its own actions.
@@ -3559,11 +3892,19 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        importDocument(to_utf8(cmd.argument()));
                        break;
 
+               case LFUN_MASTER_BUFFER_EXPORT:
+                       if (doc_buffer)
+                               doc_buffer = const_cast<Buffer *>(doc_buffer->masterBuffer());
+                       // fall through
                case LFUN_BUFFER_EXPORT: {
                        if (!doc_buffer)
                                break;
                        // GCC only sees strfwd.h when building merged
                        if (::lyx::operator==(cmd.argument(), "custom")) {
+                               // LFUN_MASTER_BUFFER_EXPORT is not enabled for this case,
+                               // so the following test should not be needed.
+                               // In principle, we could try to switch to such a view...
+                               // if (cmd.action() == LFUN_BUFFER_EXPORT)
                                dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
                                break;
                        }
@@ -3575,19 +3916,22 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        else
                                target_dir = doc_buffer->fileName().onlyPath();
 
+                       string const format = (argument.empty() || argument == "default") ?
+                               doc_buffer->params().getDefaultOutputFormat() : argument;
+
                        if ((dest.empty() && doc_buffer->isUnnamed())
                            || !target_dir.isDirWritable()) {
-                               exportBufferAs(*doc_buffer, cmd.argument());
+                               exportBufferAs(*doc_buffer, from_utf8(format));
                                break;
                        }
                        /* TODO/Review: Is it a problem to also export the children?
                                        See the update_unincluded flag */
-                       d.asyncBufferProcessing(argument,
+                       d.asyncBufferProcessing(format,
                                                doc_buffer,
                                                _("Exporting ..."),
                                                &GuiViewPrivate::exportAndDestroy,
                                                &Buffer::doExport,
-                                               0);
+                                               0, cmd.allowAsync());
                        // TODO Inform user about success
                        break;
                }
@@ -3607,7 +3951,7 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                                                _("Exporting ..."),
                                                &GuiViewPrivate::compileAndDestroy,
                                                &Buffer::doExport,
-                                               0);
+                                               0, cmd.allowAsync());
                        break;
                }
                case LFUN_BUFFER_VIEW: {
@@ -3616,7 +3960,7 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                                                _("Previewing ..."),
                                                &GuiViewPrivate::previewAndDestroy,
                                                0,
-                                               &Buffer::preview);
+                                               &Buffer::preview, cmd.allowAsync());
                        break;
                }
                case LFUN_MASTER_BUFFER_UPDATE: {
@@ -3625,7 +3969,7 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                                                docstring(),
                                                &GuiViewPrivate::compileAndDestroy,
                                                &Buffer::doExport,
-                                               0);
+                                               0, cmd.allowAsync());
                        break;
                }
                case LFUN_MASTER_BUFFER_VIEW: {
@@ -3633,7 +3977,11 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                                                (doc_buffer ? doc_buffer->masterBuffer() : 0),
                                                docstring(),
                                                &GuiViewPrivate::previewAndDestroy,
-                                               0, &Buffer::preview);
+                                               0, &Buffer::preview, cmd.allowAsync());
+                       break;
+               }
+               case LFUN_EXPORT_CANCEL: {
+                       Systemcall::killscript();
                        break;
                }
                case LFUN_BUFFER_SWITCH: {
@@ -3696,6 +4044,11 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        gotoNextOrPreviousBuffer(PREVBUFFER, true);
                        break;
 
+               case LFUN_BUFFER_CHKTEX:
+                       LASSERT(doc_buffer, break);
+                       doc_buffer->runChktex();
+                       break;
+
                case LFUN_COMMAND_EXECUTE: {
                        command_execute_ = true;
                        minibuffer_focus_ = true;
@@ -3710,9 +4063,13 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                                menu->exec(QCursor::pos());
                        break;
 
-               case LFUN_FILE_INSERT:
-                       insertLyXFile(cmd.argument());
+               case LFUN_FILE_INSERT: {
+                       if (cmd.getArg(1) == "ignorelang")
+                               insertLyXFile(from_utf8(cmd.getArg(0)), true);
+                       else
+                               insertLyXFile(cmd.argument());
                        break;
+               }
 
                case LFUN_FILE_INSERT_PLAINTEXT:
                case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
@@ -3721,14 +4078,14 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                                dr.setMessage(_("Absolute filename expected."));
                                break;
                        }
-                       
+
                        FileName filename(fname);
                        if (fname.empty()) {
                                FileDialog dlg(qt_("Select file to insert"));
 
                                FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
                                        QStringList(qt_("All Files (*)")));
-                               
+
                                if (result.first == FileDialog::Later || result.second.isEmpty()) {
                                        dr.setMessage(_("Canceled."));
                                        break;
@@ -3747,15 +4104,28 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                case LFUN_BUFFER_RELOAD: {
                        LASSERT(doc_buffer, break);
 
+                       // drop changes?
+                       bool drop = (cmd.argument() == "dump");
+
                        int ret = 0;
-                       if (!doc_buffer->isClean()) {
+                       if (!drop && !doc_buffer->isClean()) {
                                docstring const file =
                                        makeDisplayPath(doc_buffer->absFileName(), 20);
-                               docstring text = bformat(_("Any changes will be lost. "
-                                       "Are you sure you want to revert to the saved version "
-                                       "of the document %1$s?"), file);
-                               ret = Alert::prompt(_("Revert to saved document?"),
-                                       text, 1, 1, _("&Revert"), _("&Cancel"));
+                               if (doc_buffer->notifiesExternalModification()) {
+                                       docstring text = _("The current version will be lost. "
+                                           "Are you sure you want to load the version on disk "
+                                           "of the document %1$s?");
+                                       ret = Alert::prompt(_("Reload saved document?"),
+                                                           bformat(text, file), 1, 1,
+                                                           _("&Reload"), _("&Cancel"));
+                               } else {
+                                       docstring text = _("Any changes will be lost. "
+                                           "Are you sure you want to revert to the saved version "
+                                           "of the document %1$s?");
+                                       ret = Alert::prompt(_("Revert to saved document?"),
+                                                           bformat(text, file), 1, 1,
+                                                           _("&Revert"), _("&Cancel"));
+                               }
                        }
 
                        if (ret == 0) {
@@ -3776,6 +4146,12 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        renameBuffer(*doc_buffer, cmd.argument());
                        break;
 
+               case LFUN_BUFFER_WRITE_AS_TEMPLATE:
+                       LASSERT(doc_buffer, break);
+                       renameBuffer(*doc_buffer, cmd.argument(),
+                                    LV_WRITE_AS_TEMPLATE);
+                       break;
+
                case LFUN_BUFFER_WRITE_ALL: {
                        Buffer * first = theBufferList().first();
                        if (!first)
@@ -3794,6 +4170,11 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        break;
                }
 
+               case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
+                       LASSERT(doc_buffer, break);
+                       doc_buffer->clearExternalModification();
+                       break;
+
                case LFUN_BUFFER_CLOSE:
                        closeBuffer();
                        break;
@@ -3802,6 +4183,14 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        closeBufferAll();
                        break;
 
+               case LFUN_DEVEL_MODE_TOGGLE:
+                       devel_mode_ = !devel_mode_;
+                       if (devel_mode_)
+                               dr.setMessage(_("Developer mode is now enabled."));
+                       else
+                               dr.setMessage(_("Developer mode is now disabled."));
+                       break;
+
                case LFUN_TOOLBAR_TOGGLE: {
                        string const name = cmd.getArg(0);
                        if (GuiToolbar * t = toolbar(name))
@@ -3809,6 +4198,40 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        break;
                }
 
+               case LFUN_TOOLBAR_MOVABLE: {
+                       string const name = cmd.getArg(0);
+                       if (name == "*") {
+                               // toggle (all) toolbars movablility
+                               toolbarsMovable_ = !toolbarsMovable_;
+                               for (ToolbarInfo const & ti : guiApp->toolbars()) {
+                                       GuiToolbar * tb = toolbar(ti.name);
+                                       if (tb && tb->isMovable() != toolbarsMovable_)
+                                               // toggle toolbar movablity if it does not fit lock
+                                               // (all) toolbars positions state silent = true, since
+                                               // status bar notifications are slow
+                                               tb->movable(true);
+                               }
+                               if (toolbarsMovable_)
+                                       dr.setMessage(_("Toolbars unlocked."));
+                               else
+                                       dr.setMessage(_("Toolbars locked."));
+                       } else if (GuiToolbar * t = toolbar(name)) {
+                               // toggle current toolbar movablity
+                               t->movable();
+                               // update lock (all) toolbars positions
+                               updateLockToolbars();
+                       }
+                       break;
+               }
+
+               case LFUN_ICON_SIZE: {
+                       QSize size = d.iconSize(cmd.argument());
+                       setIconSize(size);
+                       dr.setMessage(bformat(_("Icon size set to %1$dx%2$d."),
+                                               size.width(), size.height()));
+                       break;
+               }
+
                case LFUN_DIALOG_UPDATE: {
                        string const name = to_utf8(cmd.argument());
                        if (name == "prefs" || name == "document")
@@ -3847,39 +4270,39 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
 
                case LFUN_DIALOG_SHOW: {
                        string const name = cmd.getArg(0);
-                       string data = trim(to_utf8(cmd.argument()).substr(name.size()));
+                       string sdata = trim(to_utf8(cmd.argument()).substr(name.size()));
 
-                       if (name == "character") {
-                               data = freefont2string();
-                               if (!data.empty())
-                                       showDialog("character", data);
-                       } else if (name == "latexlog") {
+                       if (name == "latexlog") {
+                               // gettatus checks that
+                               LATTEST(doc_buffer);
                                Buffer::LogType type;
                                string const logfile = doc_buffer->logName(&type);
                                switch (type) {
                                case Buffer::latexlog:
-                                       data = "latex ";
+                                       sdata = "latex ";
                                        break;
                                case Buffer::buildlog:
-                                       data = "literate ";
+                                       sdata = "literate ";
                                        break;
                                }
-                               data += Lexer::quoteString(logfile);
-                               showDialog("log", data);
+                               sdata += Lexer::quoteString(logfile);
+                               showDialog("log", sdata);
                        } else if (name == "vclog") {
-                               string const data = "vc " +
+                               // getStatus checks that
+                               LATTEST(doc_buffer);
+                               string const sdata2 = "vc " +
                                        Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
-                               showDialog("log", data);
+                               showDialog("log", sdata2);
                        } else if (name == "symbols") {
-                               data = bv->cursor().getEncoding()->name();
-                               if (!data.empty())
-                                       showDialog("symbols", data);
+                               sdata = bv->cursor().getEncoding()->name();
+                               if (!sdata.empty())
+                                       showDialog("symbols", sdata);
                        // bug 5274
                        } else if (name == "prefs" && isFullScreen()) {
                                lfunUiToggle("fullscreen");
-                               showDialog("prefs", data);
+                               showDialog("prefs", sdata);
                        } else
-                               showDialog(name, data);
+                               showDialog(name, sdata);
                        break;
                }
 
@@ -3969,28 +4392,36 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        break;
 
                case LFUN_BUFFER_ZOOM_IN:
-               case LFUN_BUFFER_ZOOM_OUT: {
-                       // use a signed temp to avoid overflow
-                       int zoom = lyxrc.zoom;
+               case LFUN_BUFFER_ZOOM_OUT:
+               case LFUN_BUFFER_ZOOM: {
                        if (cmd.argument().empty()) {
-                               if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
-                                       zoom += 20;
+                               if (cmd.action() == LFUN_BUFFER_ZOOM)
+                                       zoom_ratio_ = 1.0;
+                               else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
+                                       zoom_ratio_ += 0.1;
                                else
-                                       zoom -= 20;
-                       } else
-                               zoom += convert<int>(cmd.argument());
+                                       zoom_ratio_ -= 0.1;
+                       } else {
+                               if (cmd.action() == LFUN_BUFFER_ZOOM)
+                                       zoom_ratio_ = convert<int>(cmd.argument()) / double(lyxrc.defaultZoom);
+                               else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
+                                       zoom_ratio_ += convert<int>(cmd.argument()) / 100.0;
+                               else
+                                       zoom_ratio_ -= convert<int>(cmd.argument()) / 100.0;
+                       }
 
+                       // Actual zoom value: default zoom + fractional extra value
+                       int zoom = lyxrc.defaultZoom * zoom_ratio_;
                        if (zoom < static_cast<int>(zoom_min_))
                                zoom = zoom_min_;
-                       lyxrc.zoom = zoom;
 
-                       dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
+                       lyxrc.currentZoom = zoom;
+
+                       dr.setMessage(bformat(_("Zoom level is now %1$d% (default value: %2$d%)"),
+                                             lyxrc.currentZoom, lyxrc.defaultZoom));
 
-                       // The global QPixmapCache is used in GuiPainter to cache text
-                       // painting so we must reset it.
-                       QPixmapCache::clear();
                        guiApp->fontLoader().update();
-                       lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
+                       dr.screenUpdate(Update::Force | Update::FitCursor);
                        break;
                }
 
@@ -4018,9 +4449,9 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        break;
 
                case LFUN_FORWARD_SEARCH: {
-               // it seems safe to assume we have a document buffer, since
-               // getStatus wants one.
-               // coverity[FORWARD_NULL]
+                       // it seems safe to assume we have a document buffer, since
+                       // getStatus wants one.
+                       LATTEST(doc_buffer);
                        Buffer const * doc_master = doc_buffer->masterBuffer();
                        FileName const path(doc_master->temppath());
                        string const texname = doc_master->isChild(doc_buffer)
@@ -4028,7 +4459,7 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                                        doc_buffer->absFileName(),
                                                "tex")).mangledFileName()
                                : doc_buffer->latexName();
-                       string const fulltexname = 
+                       string const fulltexname =
                                support::makeAbsPath(texname, doc_master->temppath()).absFileName();
                        string const mastername =
                                removeExtension(doc_master->latexName());
@@ -4186,19 +4617,19 @@ Buffer const * GuiView::updateInset(Inset const * inset)
                        continue;
                Buffer const * buffer = &(wa->bufferView().buffer());
                if (inset_buffer == buffer)
-                       wa->scheduleRedraw();
+                       wa->scheduleRedraw(true);
        }
        return inset_buffer;
 }
 
 
-void GuiView::restartCursor()
+void GuiView::restartCaret()
 {
        /* When we move around, or type, it's nice to be able to see
-        * the cursor immediately after the keypress.
+        * the caret immediately after the keypress.
         */
        if (d.current_work_area_)
-               d.current_work_area_->startBlinkingCursor();
+               d.current_work_area_->startBlinkingCaret();
 
        // Take this occasion to update the other GUI elements.
        updateDialogs();
@@ -4226,7 +4657,7 @@ char const * const dialognames[] = {
 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
 "href", "include", "index", "index_print", "info", "listings", "label", "line",
-"log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
+"log", "lyxfiles", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
@@ -4251,7 +4682,7 @@ bool isValidName(string const & name)
                                cmpCStr(name.c_str())) != end_dialognames;
 }
 
-} // namespace anon
+} // namespace
 
 
 void GuiView::resetDialogs()
@@ -4267,7 +4698,7 @@ void GuiView::resetDialogs()
        // Now update controls with current buffer.
        guiApp->setCurrentView(this);
        restoreLayout();
-       restartCursor();
+       restartCaret();
 }
 
 
@@ -4294,10 +4725,10 @@ Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
 }
 
 
-void GuiView::showDialog(string const & name, string const & data,
+void GuiView::showDialog(string const & name, string const & sdata,
        Inset * inset)
 {
-       triggerShowDialog(toqstr(name), toqstr(data), inset);
+       triggerShowDialog(toqstr(name), toqstr(sdata), inset);
 }
 
 
@@ -4308,15 +4739,15 @@ void GuiView::doShowDialog(QString const & qname, QString const & qdata,
                return;
 
        const string name = fromqstr(qname);
-       const string data = fromqstr(qdata);
+       const string sdata = fromqstr(qdata);
 
        d.in_show_ = true;
        try {
                Dialog * dialog = findOrBuild(name, false);
                if (dialog) {
                        bool const visible = dialog->isVisibleView();
-                       dialog->showData(data);
-                       if (inset && currentBufferView())
+                       dialog->showData(sdata);
+                       if (currentBufferView())
                                currentBufferView()->editInset(name, inset);
                        // We only set the focus to the new dialog if it was not yet
                        // visible in order not to change the existing previous behaviour
@@ -4422,6 +4853,7 @@ Dialog * createGuiInclude(GuiView & lv);
 Dialog * createGuiIndex(GuiView & lv);
 Dialog * createGuiListings(GuiView & lv);
 Dialog * createGuiLog(GuiView & lv);
+Dialog * createGuiLyXFiles(GuiView & lv);
 Dialog * createGuiMathMatrix(GuiView & lv);
 Dialog * createGuiNote(GuiView & lv);
 Dialog * createGuiParagraph(GuiView & lv);
@@ -4492,6 +4924,8 @@ Dialog * GuiView::build(string const & name)
                return createGuiListings(*this);
        if (name == "log")
                return createGuiLog(*this);
+       if (name == "lyxfiles")
+               return createGuiLyXFiles(*this);
        if (name == "mathdelimiter")
                return createGuiDelimiter(*this);
        if (name == "mathmatrix")
@@ -4531,6 +4965,13 @@ Dialog * GuiView::build(string const & name)
 }
 
 
+SEMenu::SEMenu(QWidget * parent)
+{
+       QAction * action = addAction(qt_("Disable Shell Escape"));
+       connect(action, SIGNAL(triggered()),
+               parent, SLOT(disableShellEscape()));
+}
+
 } // namespace frontend
 } // namespace lyx