]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/GuiView.cpp
Fix trailing whitespace in cpp files.
[lyx.git] / src / frontends / qt4 / GuiView.cpp
index 52fadcf3ef231d7619ae709f5dee7e8474008747..7b7189743ecd1de84b5588b48a03cd2c79781c31 100644 (file)
@@ -15,7 +15,6 @@
 
 #include "GuiView.h"
 
-#include "Dialog.h"
 #include "DispatchResult.h"
 #include "FileDialog.h"
 #include "FontLoader.h"
@@ -32,6 +31,7 @@
 #include "TocModel.h"
 
 #include "qt_helpers.h"
+#include "support/filetools.h"
 
 #include "frontends/alert.h"
 #include "frontends/KeySymbol.h"
 #include <QSplitter>
 #include <QStackedWidget>
 #include <QStatusBar>
+#include <QSvgRenderer>
 #include <QtConcurrentRun>
 #include <QTime>
 #include <QTimer>
@@ -149,7 +150,8 @@ namespace {
 class BackgroundWidget : public QWidget
 {
 public:
-       BackgroundWidget()
+       BackgroundWidget(int width, int height)
+               : width_(width), height_(height)
        {
                LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
                if (!lyxrc.show_banner)
@@ -157,32 +159,44 @@ public:
                /// 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
+               QString imagedir = "images/";
+               FileName fname = imageLibFileSearch(imagedir, "banner", "svgz");
+               QSvgRenderer svgRenderer(toqstr(fname.absFileName()));
+               if (svgRenderer.isValid()) {
+                       splash_ = QPixmap(splashSize());
+                       QPainter painter(&splash_);
+                       svgRenderer.render(&painter);
+                       splash_.setDevicePixelRatio(pixelRatio());
+               } else {
+                       splash_ = getPixmap("images/", "banner", "png");
+               }
+#else
                splash_ = getPixmap("images/", "banner", "svgz,png");
+#endif
 
                QPainter pain(&splash_);
                pain.setPen(QColor(0, 0, 0));
-               double const multiplier = splashPixelRatio() / pixelRatio();
-               int const size = static_cast<int>(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble() * multiplier);
-               int const x = static_cast<int>(190 * multiplier);
-               int const y = static_cast<int>(225 * multiplier);
+               qreal const fsize = fontSize();
+               QPointF const position = textPosition();
                LYXERR(Debug::GUI,
                        "widget pixel ratio: " << pixelRatio() <<
                        " splash pixel ratio: " << splashPixelRatio() <<
-                       " version text size,position: " << size << "@" << x << "+" << y);
+                       " version text size,position: " << fsize << "@" << position.x() << "+" << position.y());
                QFont font;
                // The font used to display the version info
                font.setStyleHint(QFont::SansSerif);
                font.setWeight(QFont::Bold);
-               font.setPointSize(size);
+               font.setPointSizeF(fsize);
                pain.setFont(font);
-               pain.drawText(x, y, text);
+               pain.drawText(position, text);
                setFocusPolicy(Qt::StrongFocus);
        }
 
        void paintEvent(QPaintEvent *)
        {
-               int const w = static_cast<int>(splash_.width() / splashPixelRatio());
-               int const h = static_cast<int>(splash_.height() / splashPixelRatio());
+               int const w = width_;
+               int const h = height_;
                int const x = (width() - w) / 2;
                int const y = (height() - h) / 2;
                LYXERR(Debug::GUI,
@@ -207,16 +221,32 @@ public:
 
 private:
        QPixmap splash_;
+       int const width_;
+       int const height_;
 
        /// 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();
+       }
+
+       QPointF textPosition() const {
+               return QPointF(width_/2 - 18, height_/2 + 45);
+       }
+
+       QSize splashSize() const {
+               return QSize(
+                       static_cast<unsigned int>(width_ * pixelRatio()),
+                       static_cast<unsigned int>(height_ * pixelRatio()));
+       }
+
        /// Ratio between physical pixels and device-independent pixels of splash image
        double splashPixelRatio() const {
 #if QT_VERSION >= 0x050000
@@ -236,8 +266,12 @@ typedef shared_ptr<Dialog> DialogPtr;
 } // namespace anon
 
 
-struct GuiView::GuiViewPrivate
+class GuiView::GuiViewPrivate
 {
+       /// noncopyable
+       GuiViewPrivate(GuiViewPrivate const &);
+       void operator=(GuiViewPrivate const &);
+public:
        GuiViewPrivate(GuiView * gv)
                : gv_(gv), current_work_area_(0), current_main_work_area_(0),
                layout_(0), autosave_timeout_(5000),
@@ -264,7 +298,7 @@ struct GuiView::GuiViewPrivate
                }
 
                splitter_ = new QSplitter;
-               bg_widget_ = new BackgroundWidget;
+               bg_widget_ = new BackgroundWidget(400, 250);
                stack_widget_ = new QStackedWidget;
                stack_widget_->addWidget(bg_widget_);
                stack_widget_->addWidget(splitter_);
@@ -293,62 +327,6 @@ struct GuiView::GuiViewPrivate
                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_);
@@ -405,6 +383,60 @@ struct GuiView::GuiViewPrivate
                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_;
@@ -475,10 +507,14 @@ QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
 
 
 GuiView::GuiView(int id)
-       : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0)
+       : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0),
+         command_execute_(false), minibuffer_focus_(false)
 {
+       connect(this, SIGNAL(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
@@ -493,7 +529,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();
        }
@@ -515,7 +552,7 @@ GuiView::GuiView(int id)
 #endif
 
 #endif
-       resetWindowTitleAndIconText();
+       resetWindowTitle();
 
        // use tabbed dock area for multiple docks
        // (such as "source" and "messages")
@@ -534,11 +571,28 @@ 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()));
 
+       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);
+       read_only_ = new QLabel(statusBar());
+       read_only_->setPixmap(readonly);
+       read_only_->setScaledContents(true);
+       read_only_->setAlignment(Qt::AlignCenter);
+       read_only_->hide();
+       statusBar()->addPermanentWidget(read_only_);
+
+       version_control_ = new QLabel(statusBar());
+       version_control_->setAlignment(Qt::AlignCenter);
+       version_control_->setFrameStyle(QFrame::StyledPanel);
+       version_control_->hide();
+       statusBar()->addPermanentWidget(version_control_);
+
        statusBar()->setSizeGripEnabled(true);
        updateStatusBar();
 
@@ -553,6 +607,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);
@@ -594,7 +653,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:
@@ -632,7 +691,7 @@ 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()) {
@@ -655,6 +714,7 @@ void GuiView::autoSaveThreadFinished()
 void GuiView::saveLayout() const
 {
        QSettings settings;
+       settings.setValue("zoom", lyxrc.currentZoom);
        settings.beginGroup("views");
        settings.beginGroup(QString::number(id_));
 #if defined(Q_WS_X11) || defined(QPA_XCB)
@@ -664,7 +724,7 @@ 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())));
 }
 
 
@@ -684,6 +744,8 @@ void GuiView::saveUISettings() const
 bool GuiView::restoreLayout()
 {
        QSettings settings;
+       lyxrc.currentZoom = settings.value("zoom", lyxrc.zoom).toInt();
+       lyx::dispatch(FuncRequest(LFUN_BUFFER_ZOOM, convert<docstring>(lyxrc.currentZoom)));
        settings.beginGroup("views");
        settings.beginGroup(QString::number(id_));
        QString const icon_key = "icon_size";
@@ -691,17 +753,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();
@@ -732,7 +784,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();
@@ -742,6 +794,9 @@ bool GuiView::restoreLayout()
                        initToolbar(cit->name);
        }
 
+       // update lock (all) toolbars positions
+       updateLockToolbars();
+
        updateDialogs();
        return true;
 }
@@ -758,6 +813,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();
@@ -825,6 +891,8 @@ void GuiView::initToolbar(string const & name)
 
        if (visibility & Toolbars::ON)
                tb->setVisible(true);
+
+       tb->setMovable(true);
 }
 
 
@@ -866,12 +934,6 @@ void GuiView::focusInEvent(QFocusEvent * e)
 }
 
 
-QMenu * GuiView::createPopupMenu()
-{
-       return d.toolBarPopup(this);
-}
-
-
 void GuiView::showEvent(QShowEvent * e)
 {
        LYXERR(Debug::GUI, "Passed Geometry "
@@ -928,7 +990,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;
@@ -1059,36 +1121,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,
@@ -1106,37 +1138,64 @@ void GuiView::updateWindowTitle(GuiWorkArea * wa)
        if (wa != d.current_work_area_
                || wa->bufferView().buffer().isInternal())
                return;
-       setWindowTitle(qt_("LyX: ") + wa->windowTitle());
-       setWindowIconText(wa->windowIconText());
-#if (QT_VERSION >= 0x040400)
-       // Sets the path for the window: this is used by OSX to 
+       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
+       setWindowTitle(toqstr(title));
+       // Sets the path for the window: this is used by OSX to
        // allow a context click on the title bar showing a menu
        // with the path up to the file
-       setWindowFilePath(toqstr(wa->bufferView().buffer().absFileName()));
-#endif
+       setWindowFilePath(toqstr(buf.absFileName()));
+       // Tell Qt whether the current document is changed
+       setWindowModified(!buf.isClean());
+
+       if (buf.hasReadonlyFlag())
+               read_only_->show();
+       else
+               read_only_->hide();
+
+       if (buf.lyxvc().inUse()) {
+               version_control_->show();
+               version_control_->setText(toqstr(buf.lyxvc().vcstatus()));
+       } else
+               version_control_->hide();
 }
 
 
 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
 {
        if (d.current_work_area_)
-               QObject::disconnect(d.current_work_area_, SIGNAL(busy(bool)),
-                       this, SLOT(setBusy(bool)));
+               // disconnect the current work area from all slots
+               QObject::disconnect(d.current_work_area_, 0, this, 0);
        disconnectBuffer();
        disconnectBufferView();
        connectBufferView(wa->bufferView());
        connectBuffer(wa->bufferView().buffer());
        d.current_work_area_ = wa;
        QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
-               this, SLOT(updateWindowTitle(GuiWorkArea *)));
-       QObject::connect(wa, SIGNAL(busy(bool)), this, SLOT(setBusy(bool)));
-       updateWindowTitle(wa);
-
-       structureChanged();
+                        this, SLOT(updateWindowTitle(GuiWorkArea *)));
+       QObject::connect(wa, SIGNAL(busy(bool)),
+                        this, SLOT(setBusy(bool)));
+       // connection of a signal to a signal
+       QObject::connect(wa, SIGNAL(bufferViewChanged()),
+                        this, SIGNAL(bufferViewChanged()));
+       Q_EMIT updateWindowTitle(wa);
+       Q_EMIT bufferViewChanged();
+}
 
-       // The document settings needs to be reinitialised.
-       updateDialog("document", "");
 
+void GuiView::onBufferViewChanged()
+{
+       structureChanged();
        // Buffer-dependent dialogs must be updated. This is done here because
        // some dialogs require buffer()->text.
        updateDialogs();
@@ -1154,11 +1213,9 @@ void GuiView::on_lastWorkAreaRemoved()
                return;
 
        // Reset and updates the dialogs.
-       d.toc_models_.reset(0);
-       updateDialog("document", "");
-       updateDialogs();
+       Q_EMIT bufferViewChanged();
 
-       resetWindowTitleAndIconText();
+       resetWindowTitle();
        updateStatusBar();
 
        if (lyxrc.open_buffers_in_tabs)
@@ -1236,19 +1293,10 @@ bool GuiView::event(QEvent * e)
                        cap::saveSelection(old_view->currentBufferView()->cursor());
                }
                guiApp->setCurrentView(this);
-               if (d.current_work_area_) {
-                       BufferView & bv = d.current_work_area_->bufferView();
-                       connectBufferView(bv);
-                       connectBuffer(bv.buffer());
-                       // The document structure, name and dialogs might have
-                       // changed in another view.
-                       structureChanged();
-                       // The document settings needs to be reinitialised.
-                       updateDialog("document", "");
-                       updateDialogs();
-               } else {
-                       resetWindowTitleAndIconText();
-               }
+               if (d.current_work_area_)
+                       on_currentWorkAreaChanged(d.current_work_area_);
+               else
+                       resetWindowTitle();
                setFocus();
                return QMainWindow::event(e);
        }
@@ -1274,10 +1322,9 @@ bool GuiView::event(QEvent * e)
        }
 }
 
-void GuiView::resetWindowTitleAndIconText()
+void GuiView::resetWindowTitle()
 {
        setWindowTitle(qt_("LyX"));
-       setWindowIconText(qt_("LyX"));
 }
 
 bool GuiView::focusNextPrevChild(bool /*next*/)
@@ -1306,25 +1353,32 @@ void GuiView::setBusy(bool busy)
                return;
        }
        QApplication::restoreOverrideCursor();
-       updateLayoutList();     
+       updateLayoutList();
+}
+
+
+void GuiView::resetCommandExecute()
+{
+       command_execute_ = false;
+       updateToolbars();
 }
 
 
 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;
 }
 
@@ -1333,7 +1387,7 @@ GuiWorkArea * GuiView::workArea(Buffer & buffer)
 {
        if (currentWorkArea()
                && &currentWorkArea()->bufferView().buffer() == &buffer)
-               return (GuiWorkArea *) currentWorkArea();
+               return currentWorkArea();
        if (TabWorkArea * twa = d.currentTabWorkArea())
                return twa->workArea(buffer);
        return 0;
@@ -1397,6 +1451,7 @@ void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
        if (!wa) {
                d.current_work_area_ = 0;
                d.setBackground();
+               Q_EMIT bufferViewChanged();
                return;
        }
 
@@ -1412,7 +1467,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.
@@ -1421,14 +1476,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();
@@ -1501,32 +1556,42 @@ void GuiView::updateToolbars()
 {
        ToolbarMap::iterator end = d.toolbars_.end();
        if (d.current_work_area_) {
-               bool const math =
-                       d.current_work_area_->bufferView().cursor().inMathed()
-                       && !d.current_work_area_->bufferView().cursor().inRegexped();
-               bool const table =
-                       lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
-               bool const review =
-                       lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
-                       lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true);
-               bool const mathmacrotemplate =
-                       lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled();
-               bool const ipa =
-                       lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled();
+               int context = 0;
+               if (d.current_work_area_->bufferView().cursor().inMathed()
+                       && !d.current_work_area_->bufferView().cursor().inRegexped())
+                       context |= Toolbars::MATH;
+               if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
+                       context |= Toolbars::TABLE;
+               if (currentBufferView()->buffer().areChangesPresent()
+                   || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
+                       && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
+                   || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
+                       && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
+                       context |= Toolbars::REVIEW;
+               if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
+                       context |= Toolbars::MATHMACROTEMPLATE;
+               if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
+                       context |= Toolbars::IPA;
+               if (command_execute_)
+                       context |= Toolbars::MINIBUFFER;
+               if (minibuffer_focus_) {
+                       context |= Toolbars::MINIBUFFER_FOCUS;
+                       minibuffer_focus_ = false;
+               }
 
                for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
-                       it->second->update(math, table, review, mathmacrotemplate, ipa);
+                       it->second->update(context);
        } else
                for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
-                       it->second->update(false, false, false, false, false);
+                       it->second->update();
 }
 
 
-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);
@@ -1545,7 +1610,8 @@ void GuiView::setBuffer(Buffer * newBuffer)
        }
        connectBuffer(*newBuffer);
        connectBufferView(wa->bufferView());
-       setCurrentWorkArea(wa);
+       if (switch_to)
+               setCurrentWorkArea(wa);
 }
 
 
@@ -1613,6 +1679,12 @@ 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());
        // Navigator needs more than a simple update in this case. It needs to be
        // rebuilt.
@@ -1709,15 +1781,20 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
        Buffer * doc_buffer = documentBufferView()
                ? &(documentBufferView()->buffer()) : 0;
 
+#ifdef Q_OS_MAC
        /* In LyX/Mac, when a dialog is open, the menus of the
           application can still be accessed without giving focus to
           the main window. In this case, we want to disable the menu
           entries that are buffer-related.
+          This code must not be used on Linux and Windows, since it
+          would disable buffer-related entries when hovering over the
+          menu (see bug #9574).
         */
        if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
                buf = 0;
                doc_buffer = 0;
        }
+#endif
 
        // Check whether we need a buffer
        if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
@@ -1756,19 +1833,17 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                string format = to_utf8(cmd.argument());
                if (cmd.argument().empty())
                        format = doc_buffer->params().getDefaultOutputFormat();
-               enable = doc_buffer->params().isExportableFormat(format);
+               enable = doc_buffer->params().isExportable(format, true);
                break;
        }
 
        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->fileName().exists() && !doc_buffer->isClean();
                break;
 
        case LFUN_BUFFER_CHILD_OPEN:
-               enable = doc_buffer;
+               enable = doc_buffer != 0;
                break;
 
        case LFUN_BUFFER_WRITE:
@@ -1794,14 +1869,18 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                break;
        }
 
+       case LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR:
+               enable = doc_buffer && doc_buffer->notifiesExternalModification();
+               break;
+
        case LFUN_BUFFER_WRITE_AS:
        case LFUN_BUFFER_EXPORT_AS:
-               enable = doc_buffer;
+               enable = doc_buffer != 0;
                break;
 
        case LFUN_BUFFER_CLOSE:
        case LFUN_VIEW_CLOSE:
-               enable = doc_buffer;
+               enable = doc_buffer != 0;
                break;
 
        case LFUN_BUFFER_CLOSE_ALL:
@@ -1834,8 +1913,29 @@ 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;
+               enable = buf != 0;
                break;
 
        case LFUN_UI_TOGGLE:
@@ -1861,9 +1961,6 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                                || name == "texinfo"
                                || name == "progress"
                                || name == "compare";
-               else if (name == "print")
-                       enable = doc_buffer->params().isExportable("dvi")
-                               && lyxrc.print_command != "none";
                else if (name == "character" || name == "symbols"
                        || name == "mathdelimiter" || name == "mathmatrix") {
                        if (!buf || buf->isReadonly())
@@ -1876,7 +1973,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")
@@ -1934,12 +2031,35 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                break;
 
        case LFUN_BUFFER_ZOOM_OUT:
-               enable = doc_buffer && lyxrc.zoom > 10;
+       case LFUN_BUFFER_ZOOM_IN: {
+               // only diff between these two is that the default for ZOOM_OUT
+               // is a neg. number
+               bool const neg_zoom =
+                       convert<int>(cmd.argument()) < 0 ||
+                       (cmd.action() == LFUN_BUFFER_ZOOM_OUT && cmd.argument().empty());
+               if (lyxrc.currentZoom <= zoom_min_ && neg_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_ZOOM_IN:
-               enable = doc_buffer;
+       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:
@@ -1973,12 +2093,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();
@@ -1998,6 +2119,7 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                break;
 
        case LFUN_SERVER_GOTO_FILE_ROW:
+       case LFUN_LYX_ACTIVATE:
                break;
        case LFUN_FORWARD_SEARCH:
                enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
@@ -2084,8 +2206,7 @@ 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.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
 
                QStringList const filter(qt_("LyX Documents (*.lyx)"));
                FileDialog::Result result =
@@ -2162,7 +2283,7 @@ static bool import(GuiView * lv, FileName const & filename,
 
                        string const tofile =
                                support::changeExtension(filename.absFileName(),
-                               formats.extension(*it));
+                               theFormats().extension(*it));
                        if (!theConverters().convert(0, filename, FileName(tofile),
                                filename, format, *it, errorList))
                                return false;
@@ -2172,7 +2293,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
@@ -2190,7 +2311,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);
@@ -2219,17 +2340,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.setButton2(qt_("Examples|#E#e"), 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 =
@@ -2357,9 +2477,7 @@ 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.setButton2(qt_("Examples|#E#e"), toqstr(lyxrc.example_path));
 
                FileDialog::Result result = dlg.open(toqstr(initpath),
                                         QStringList(qt_("LyX Documents (*.lyx)")));
@@ -2511,15 +2629,6 @@ bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kin
 }
 
 
-struct PrettyNameComparator
-{
-       bool operator()(Format const *first, Format const *second) const {
-               return compare_no_case(translateIfPossible(from_ascii(first->prettyname())),
-                                      translateIfPossible(from_ascii(second->prettyname()))) <= 0;
-       }
-};
-
-
 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
 {
        FileName fname = b.fileName();
@@ -2530,28 +2639,25 @@ bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
        QStringList types;
        QString const anyformat = qt_("Guess from extension (*.*)");
        types << anyformat;
-       Formats::const_iterator it = formats.begin();
+
        vector<Format const *> export_formats;
-       for (; it != formats.end(); ++it)
-               if (it->documentFormat())
-                       export_formats.push_back(&(*it));
-       PrettyNameComparator cmp;
-       sort(export_formats.begin(), export_formats.end(), cmp);
-       vector<Format const *>::const_iterator fit = export_formats.begin();
+       for (Format const & f : theFormats())
+               if (f.documentFormat())
+                       export_formats.push_back(&f);
+       sort(export_formats.begin(), export_formats.end(), Format::formatSorter);
        map<QString, string> fmap;
        QString filter;
        string ext;
-       for (; fit != export_formats.end(); ++fit) {
-               docstring const loc_prettyname =
-                       translateIfPossible(from_utf8((*fit)->prettyname()));
+       for (Format const * f : export_formats) {
+               docstring const loc_prettyname = translateIfPossible(f->prettyname());
                QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
                                                     loc_prettyname,
-                                                    from_ascii((*fit)->extension())));
+                                                    from_ascii(f->extension())));
                types << loc_filter;
-               fmap[loc_filter] = (*fit)->name();
-               if (from_ascii((*fit)->name()) == iformat) {
+               fmap[loc_filter] = f->name();
+               if (from_ascii(f->name()) == iformat) {
                        filter = loc_filter;
-                       ext = (*fit)->extension();
+                       ext = f->extension();
                }
        }
        string ofname = fname.onlyFileName();
@@ -2568,7 +2674,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)
@@ -2696,9 +2802,13 @@ bool GuiView::closeWorkArea(GuiWorkArea * wa)
 bool GuiView::closeBuffer()
 {
        GuiWorkArea * wa = currentMainWorkArea();
+       // coverity complained about this
+       // it seems unnecessary, but perhaps is worth the check
+       LASSERT(wa, return false);
+
        setCurrentWorkArea(wa);
        Buffer & buf = wa->bufferView().buffer();
-       return wa && closeWorkArea(wa, !buf.parent());
+       return closeWorkArea(wa, !buf.parent());
 }
 
 
@@ -2707,7 +2817,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);
                }
@@ -2747,7 +2857,7 @@ bool GuiView::closeWorkAreaAll()
 
        // We have to call count() each time, because it can happen that
        // more than one splitter will disappear in one iteration (bug 5998).
-       for (; d.splitter_->count() > empty_twa; ) {
+       while (d.splitter_->count() > empty_twa) {
                TabWorkArea * twa = d.tabWorkArea(empty_twa);
 
                if (twa->count() == 0)
@@ -2769,8 +2879,8 @@ bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
 
        Buffer & buf = wa->bufferView().buffer();
 
-       if (close_buffer && GuiViewPrivate::busyBuffers.contains(&buf)) {
-               Alert::warning(_("Close document"), 
+       if (GuiViewPrivate::busyBuffers.contains(&buf)) {
+               Alert::warning(_("Close document"),
                        _("Document could not be closed because it is being processed by LyX."));
                return false;
        }
@@ -2799,23 +2909,31 @@ bool GuiView::closeBuffer(Buffer & buf)
                ListOfBuffers::const_iterator it = clist.begin();
                ListOfBuffers::const_iterator const bend = clist.end();
                for (; it != bend; ++it) {
-                       // If a child is dirty, do not close
-                       // without user intervention
-                       //FIXME: should we look in other tabworkareas?
                        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) {
-                               if (!closeWorkArea(child_wa, true)) {
-                                       success = false;
+                               success = closeWorkArea(child_wa, true);
+                               if (!success)
                                        break;
-                               }
-                       } else
-                               theBufferList().releaseChild(&buf, child_buf);
+                       } else {
+                               // In this case the child buffer is open but hidden.
+                               // It therefore should not (MUST NOT) be dirty!
+                               LATTEST(child_buf->isClean());
+                               theBufferList().release(child_buf);
+                       }
                }
        }
        if (success) {
                // goto bookmark to update bookmark pit.
-               //FIXME: we should update only the bookmarks related to this buffer!
+               // FIXME: we should update only the bookmarks related to this buffer!
                LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
                for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
                        guiApp->gotoBookmark(i+1, false, false);
@@ -2836,7 +2954,7 @@ bool GuiView::closeBuffer(Buffer & buf)
 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
 {
        while (twa == d.currentTabWorkArea()) {
-               twa->setCurrentIndex(twa->count()-1);
+               twa->setCurrentIndex(twa->count() - 1);
 
                GuiWorkArea * wa = twa->currentWorkArea();
                Buffer & b = wa->bufferView().buffer();
@@ -2863,11 +2981,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();
@@ -2883,10 +3007,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) {
@@ -2944,7 +3075,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();
@@ -3010,8 +3141,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()));
@@ -3045,7 +3175,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
@@ -3074,7 +3204,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);
@@ -3098,7 +3228,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();
@@ -3178,7 +3308,6 @@ void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
                }
 
        case LFUN_VC_COMPARE: {
-
                if (cmd.argument().empty()) {
                        lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
                        break;
@@ -3186,6 +3315,7 @@ void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
 
                string rev1 = cmd.getArg(0);
                string f1, f2;
+               LATTEST(buffer)
 
                // f1
                if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
@@ -3296,8 +3426,23 @@ bool GuiView::goToFileRow(string const & argument)
                return false;
        }
        setBuffer(buf);
-       documentBufferView()->setCursorFromRow(row);
-       return true;
+       bool success = documentBufferView()->setCursorFromRow(row);
+       if (!success) {
+               LYXERR(Debug::LATEX,
+                      "setCursorFromRow: invalid position for row " << row);
+               frontend::Alert::error(_("Inverse Search Failed"),
+                                      _("Invalid position requested by inverse search.\n"
+                                        "You may need to update the viewed document."));
+       }
+       return success;
+}
+
+
+void GuiView::toolBarPopup(const QPoint & /*pos*/)
+{
+       QMenu * menu = new QMenu;
+       menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
+       menu->exec(QCursor::pos());
 }
 
 
@@ -3377,15 +3522,14 @@ bool GuiView::GuiViewPrivate::asyncBufferProcessing(
 #else
        Buffer::ExportStatus status;
        if (syncFunc) {
-               // TODO check here if it breaks exporting with Qt < 4.4
                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
 }
@@ -3460,22 +3604,30 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                case LFUN_BUFFER_EXPORT: {
                        if (!doc_buffer)
                                break;
-                       FileName target_dir = doc_buffer->fileName().onlyPath();
-                       string const dest = cmd.getArg(1);
-                       if (!dest.empty() && FileName::isAbsolute(dest))
-                               target_dir = FileName(support::onlyPath(dest));
                        // GCC only sees strfwd.h when building merged
                        if (::lyx::operator==(cmd.argument(), "custom")) {
                                dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
                                break;
                        }
-                       if (!target_dir.isDirWritable()) {
-                               exportBufferAs(*doc_buffer, cmd.argument());
+
+                       string const dest = cmd.getArg(1);
+                       FileName target_dir;
+                       if (!dest.empty() && FileName::isAbsolute(dest))
+                               target_dir = FileName(support::onlyPath(dest));
+                       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, 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,
@@ -3557,7 +3709,9 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        for (; i != ids.size(); ++i) {
                                GuiView & gv = guiApp->view(ids[i]);
                                if (gv.workArea(*buffer)) {
+                                       gv.raise();
                                        gv.activateWindow();
+                                       gv.setFocus();
                                        gv.setBuffer(buffer);
                                        break;
                                }
@@ -3588,14 +3742,8 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        break;
 
                case LFUN_COMMAND_EXECUTE: {
-                       bool const show_it = cmd.argument() != "off";
-                       // FIXME: this is a hack, "minibuffer" should not be
-                       // hardcoded.
-                       if (GuiToolbar * t = toolbar("minibuffer")) {
-                               t->setVisible(show_it);
-                               if (show_it && t->commandBuffer())
-                                       t->commandBuffer()->setFocus();
-                       }
+                       command_execute_ = true;
+                       minibuffer_focus_ = true;
                        break;
                }
                case LFUN_DROP_LAYOUTS_CHOICE:
@@ -3618,14 +3766,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;
@@ -3648,11 +3796,21 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        if (!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) {
@@ -3691,6 +3849,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;
@@ -3706,6 +3869,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")
@@ -3751,6 +3948,8 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                                if (!data.empty())
                                        showDialog("character", data);
                        } else if (name == "latexlog") {
+                               // getStatus checks that
+                               LATTEST(doc_buffer);
                                Buffer::LogType type;
                                string const logfile = doc_buffer->logName(&type);
                                switch (type) {
@@ -3764,6 +3963,8 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                                data += Lexer::quoteString(logfile);
                                showDialog("log", data);
                        } else if (name == "vclog") {
+                               // getStatus checks that
+                               LATTEST(doc_buffer);
                                string const data = "vc " +
                                        Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
                                showDialog("log", data);
@@ -3867,16 +4068,31 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
 
                case LFUN_BUFFER_ZOOM_IN:
                case LFUN_BUFFER_ZOOM_OUT:
+               case LFUN_BUFFER_ZOOM: {
+                       // use a signed temp to avoid overflow
+                       int zoom = lyxrc.currentZoom;
                        if (cmd.argument().empty()) {
-                               if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
-                                       lyxrc.zoom += 20;
+                               if (cmd.action() == LFUN_BUFFER_ZOOM)
+                                       zoom = lyxrc.zoom;
+                               else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
+                                       zoom += 20;
                                else
-                                       lyxrc.zoom -= 20;
-                       } else
-                               lyxrc.zoom += convert<int>(cmd.argument());
+                                       zoom -= 20;
+                       } else {
+                               if (cmd.action() == LFUN_BUFFER_ZOOM)
+                                       zoom = convert<int>(cmd.argument());
+                               else if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
+                                       zoom += convert<int>(cmd.argument());
+                               else
+                                       zoom -= convert<int>(cmd.argument());
+                       }
 
-                       if (lyxrc.zoom < 10)
-                               lyxrc.zoom = 10;
+                       if (zoom < static_cast<int>(zoom_min_))
+                               zoom = zoom_min_;
+
+                       lyxrc.currentZoom = zoom;
+
+                       dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.currentZoom));
 
                        // The global QPixmapCache is used in GuiPainter to cache text
                        // painting so we must reset it.
@@ -3884,6 +4100,7 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        guiApp->fontLoader().update();
                        lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
                        break;
+               }
 
                case LFUN_VC_REGISTER:
                case LFUN_VC_RENAME:
@@ -3900,10 +4117,18 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        break;
 
                case LFUN_SERVER_GOTO_FILE_ROW:
-                       goToFileRow(to_utf8(cmd.argument()));
+                       if(goToFileRow(to_utf8(cmd.argument())))
+                               dr.screenUpdate(Update::Force | Update::FitCursor);
+                       break;
+
+               case LFUN_LYX_ACTIVATE:
+                       activateWindow();
                        break;
 
                case LFUN_FORWARD_SEARCH: {
+                       // 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)
@@ -3911,7 +4136,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());
@@ -3933,15 +4158,11 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                                command = lyxrc.forward_search_pdf;
                        }
 
-                       DocIterator tmpcur = bv->cursor();
-                       // Leave math first
-                       while (tmpcur.inMathed())
-                               tmpcur.pop_back();
-                       int row = tmpcur.inMathed() ? 0 : doc_buffer->texrow().getRowFromIdPos(
-                                                               tmpcur.paragraph().id(), tmpcur.pos());
+                       DocIterator cur = bv->cursor();
+                       int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
                        LYXERR(Debug::ACTION, "Forward search: row:" << row
-                               << " id:" << tmpcur.paragraph().id());
-                       if (!row || command.empty()) {
+                                  << " cur:" << cur);
+                       if (row == -1 || command.empty()) {
                                dr.setMessage(_("Couldn't proceed."));
                                break;
                        }
@@ -3960,7 +4181,7 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
 
                case LFUN_SPELLING_CONTINUOUSLY:
                        lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
-                       dr.screenUpdate(Update::Force | Update::FitCursor);
+                       dr.screenUpdate(Update::Force);
                        break;
 
                default:
@@ -3980,7 +4201,7 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
        bv = currentBufferView();
 
        // Clear non-empty selections
-        // (e.g. from a "char-forward-select" followed by "char-backward-select")
+       // (e.g. from a "char-forward-select" followed by "char-backward-select")
        if (bv) {
                Cursor & cur = bv->cursor();
                if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
@@ -4114,7 +4335,7 @@ char const * const dialognames[] = {
 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
 "href", "include", "index", "index_print", "info", "listings", "label", "line",
 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
-"nomencl_print", "note", "paragraph", "phantom", "prefs", "print", "ref",
+"nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
 
@@ -4391,8 +4612,6 @@ Dialog * GuiView::build(string const & name)
                return createGuiPhantom(*this);
        if (name == "prefs")
                return createGuiPreferences(*this);
-       if (name == "print")
-               return createGuiPrint(*this);
        if (name == "ref")
                return createGuiRef(*this);
        if (name == "sendto")