]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/GuiView.cpp
Fix dangling inset pointers after buffer reload
[lyx.git] / src / frontends / qt4 / GuiView.cpp
index a7cbbe49fed44c0026419e1a63c88d1aa6974ee4..2c481245680727876bc3d2c5f4171cec0a3608fb 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>
 
 
 
+// sync with GuiAlert.cpp
 #define EXPORT_in_THREAD 1
 
 
@@ -148,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)
@@ -156,26 +159,52 @@ public:
                /// The text to be written on top of the pixmap
                QString const text = lyx_version ?
                        qt_("version ") + lyx_version : qt_("unknown version");
-               splash_ = getPixmap("images/", "banner", "png");
+#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));
+               qreal const fsize = fontSize();
+               QPointF const position = textPosition();
+               LYXERR(Debug::GUI,
+                       "widget pixel ratio: " << pixelRatio() <<
+                       " splash pixel ratio: " << splashPixelRatio() <<
+                       " 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(int(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble()));
+               font.setPointSizeF(fsize);
                pain.setFont(font);
-               pain.drawText(190, 225, text);
+               pain.drawText(position, text);
                setFocusPolicy(Qt::StrongFocus);
        }
 
        void paintEvent(QPaintEvent *)
        {
-               int x = (width() - splash_.width()) / 2;
-               int y = (height() - splash_.height()) / 2;
+               int const w = width_;
+               int const h = height_;
+               int const x = (width() - w) / 2;
+               int const y = (height() - h) / 2;
+               LYXERR(Debug::GUI,
+                       "widget pixel ratio: " << pixelRatio() <<
+                       " splash pixel ratio: " << splashPixelRatio() <<
+                       " paint pixmap: " << w << "x" << h << "@" << x << "+" << y);
                QPainter pain(this);
-               pain.drawPixmap(x, y, splash_);
+               pain.drawPixmap(x, y, w, h, splash_);
        }
 
        void keyPressEvent(QKeyEvent * ev)
@@ -192,6 +221,40 @@ 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();
+#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
+               return splash_.devicePixelRatio();
+#else
+               return 1.0;
+#endif
+       }
 };
 
 
@@ -203,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),
@@ -214,6 +281,8 @@ struct GuiView::GuiViewPrivate
                smallIconSize = 16;  // scaling problems
                normalIconSize = 20; // ok, default if iconsize.png is missing
                bigIconSize = 26;       // better for some math icons
+               hugeIconSize = 32;      // better for hires displays
+               giantIconSize = 48;
 
                // if it exists, use width of iconsize.png as normal size
                QString const dir = toqstr(addPath("images", lyxrc.icon_set));
@@ -222,14 +291,14 @@ struct GuiView::GuiViewPrivate
                        QImage image(toqstr(fn.absFileName()));
                        if (image.width() < int(smallIconSize))
                                normalIconSize = smallIconSize;
-                       else if (image.width() > int(bigIconSize))
-                               normalIconSize = bigIconSize;
+                       else if (image.width() > int(giantIconSize))
+                               normalIconSize = giantIconSize;
                        else
                                normalIconSize = image.width();
                }
 
                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_);
@@ -258,44 +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);
-
-               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);
-
-               return menu;
-       }
-
        void setBackground()
        {
                stack_widget_->setCurrentWidget(bg_widget_);
@@ -352,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_;
@@ -379,6 +464,8 @@ public:
        unsigned int smallIconSize;
        unsigned int normalIconSize;
        unsigned int bigIconSize;
+       unsigned int hugeIconSize;
+       unsigned int giantIconSize;
        ///
        QTimer statusbar_timer_;
        /// auto-saving of buffers
@@ -420,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
@@ -448,19 +539,19 @@ GuiView::GuiView(int id)
        // We don't want to keep the window in memory if it is closed.
        setAttribute(Qt::WA_DeleteOnClose, true);
 
-#if (!defined(Q_WS_WIN) && !defined(Q_WS_MACX))
+#if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
        // QIcon::fromTheme was introduced in Qt 4.6
 #if (QT_VERSION >= 0x040600)
        // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
        // since the icon is provided in the application bundle. We use a themed
        // version when available and use the bundled one as fallback.
-       setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "png")));
+       setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
 #else
-       setWindowIcon(getPixmap("images/", "lyx", "png"));
+       setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
 #endif
 
 #endif
-       resetWindowTitleAndIconText();
+       resetWindowTitle();
 
        // use tabbed dock area for multiple docks
        // (such as "source" and "messages")
@@ -472,7 +563,8 @@ GuiView::GuiView(int id)
        // add busy indicator to statusbar
        QLabel * busylabel = new QLabel(statusBar());
        statusBar()->addPermanentWidget(busylabel);
-       QString fn = toqstr(lyx::libFileSearch("images", "busy.gif").absFileName());
+       search_mode mode = theGuiApp()->imageSearchMode();
+       QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
        QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
        busylabel->setMovie(busyanim);
        busyanim->start();
@@ -483,6 +575,23 @@ GuiView::GuiView(int id)
        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();
 
@@ -497,6 +606,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);
@@ -601,14 +715,14 @@ void GuiView::saveLayout() const
        QSettings settings;
        settings.beginGroup("views");
        settings.beginGroup(QString::number(id_));
-#ifdef Q_WS_X11
+#if defined(Q_WS_X11) || defined(QPA_XCB)
        settings.setValue("pos", pos());
        settings.setValue("size", size());
 #else
        settings.setValue("geometry", saveGeometry());
 #endif
        settings.setValue("layout", saveState(0));
-       settings.setValue("icon_size", iconSize());
+       settings.setValue("icon_size", toqstr(d.iconSize(iconSize())));
 }
 
 
@@ -635,17 +749,9 @@ 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.setWidth(d.normalIconSize);
-               icon_size.setHeight(d.normalIconSize);
-       }
-       setIconSize(icon_size);
+       setIconSize(d.iconSize(settings.value(icon_key).toString()));
 
-#ifdef Q_WS_X11
+#if defined(Q_WS_X11) || defined(QPA_XCB)
        QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
        QSize size = settings.value("size", QSize(690, 510)).toSize();
        resize(size);
@@ -783,6 +889,16 @@ void GuiView::setFocus()
 }
 
 
+bool GuiView::hasFocus() const
+{
+       if (currentWorkArea())
+               return currentWorkArea()->hasFocus();
+       if (currentMainWorkArea())
+               return currentMainWorkArea()->hasFocus();
+       return d.bg_widget_->hasFocus();
+}
+
+
 void GuiView::focusInEvent(QFocusEvent * e)
 {
        LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
@@ -798,12 +914,6 @@ void GuiView::focusInEvent(QFocusEvent * e)
 }
 
 
-QMenu * GuiView::createPopupMenu()
-{
-       return d.toolBarPopup(this);
-}
-
-
 void GuiView::showEvent(QShowEvent * e)
 {
        LYXERR(Debug::GUI, "Passed Geometry "
@@ -826,6 +936,32 @@ bool GuiView::closeScheduled()
 }
 
 
+bool GuiView::prepareAllBuffersForLogout()
+{
+       Buffer * first = theBufferList().first();
+       if (!first)
+               return true;
+
+       // First, iterate over all buffers and ask the users if unsaved
+       // changes should be saved.
+       // We cannot use a for loop as the buffer list cycles.
+       Buffer * b = first;
+       do {
+               if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
+                       return false;
+               b = theBufferList().next(b);
+       } while (b != first);
+
+       // Next, save session state
+       // When a view/window was closed before without quitting LyX, there
+       // are already entries in the lastOpened list.
+       theSession().lastOpened().clear();
+       writeSession();
+
+       return true;
+}
+
+
 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
  ** is responsibility of the container (e.g., dialog)
  **/
@@ -965,24 +1101,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::clearMessage()
 {
        // FIXME: This code was introduced in r19643 to fix bug #4123. However,
@@ -1000,37 +1118,58 @@ 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("[*]");
+#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.isReadonly())
+               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();
@@ -1048,11 +1187,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)
@@ -1064,7 +1201,7 @@ void GuiView::on_lastWorkAreaRemoved()
                return;
        }
 
-#ifdef Q_WS_MACX
+#ifdef Q_OS_MAC
        // On Mac we also close the last window because the application stay
        // resident in memory. On other platforms we don't close the last
        // window because this would quit the application.
@@ -1130,19 +1267,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);
        }
@@ -1168,10 +1296,9 @@ bool GuiView::event(QEvent * e)
        }
 }
 
-void GuiView::resetWindowTitleAndIconText()
+void GuiView::resetWindowTitle()
 {
        setWindowTitle(qt_("LyX"));
-       setWindowIconText(qt_("LyX"));
 }
 
 bool GuiView::focusNextPrevChild(bool /*next*/)
@@ -1204,6 +1331,23 @@ void GuiView::setBusy(bool busy)
 }
 
 
+void GuiView::resetCommandExecute()
+{
+       command_execute_ = false;
+       updateToolbars();
+}
+
+
+double GuiView::pixelRatio() const
+{
+#if QT_VERSION >= 0x050000
+       return devicePixelRatio();
+#else
+       return 1.0;
+#endif
+}
+       
+       
 GuiWorkArea * GuiView::workArea(int index)
 {
        if (TabWorkArea * twa = d.currentTabWorkArea())
@@ -1217,7 +1361,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;
@@ -1281,6 +1425,7 @@ void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
        if (!wa) {
                d.current_work_area_ = 0;
                d.setBackground();
+               Q_EMIT bufferViewChanged();
                return;
        }
 
@@ -1385,24 +1530,34 @@ 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();
 }
 
 
@@ -1497,6 +1652,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.
@@ -1593,6 +1754,21 @@ 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) {
                // no, exit directly
@@ -1630,7 +1806,7 @@ 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;
        }
 
@@ -1642,7 +1818,7 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                break;
 
        case LFUN_BUFFER_CHILD_OPEN:
-               enable = doc_buffer;
+               enable = doc_buffer != 0;
                break;
 
        case LFUN_BUFFER_WRITE:
@@ -1670,12 +1846,12 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
 
        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:
@@ -1708,8 +1884,12 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                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:
@@ -1735,9 +1915,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())
@@ -1808,16 +1985,28 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                break;
 
        case LFUN_BUFFER_ZOOM_OUT:
-               enable = doc_buffer && lyxrc.zoom > 10;
-               break;
-
-       case LFUN_BUFFER_ZOOM_IN:
-               enable = doc_buffer;
+       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.zoom <= 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_MOVE_NEXT:
+       case LFUN_BUFFER_MOVE_PREVIOUS:
+               // we do not cycle when moving
        case LFUN_BUFFER_NEXT:
        case LFUN_BUFFER_PREVIOUS:
-               // FIXME: should we check is there is an previous or next buffer?
+               // because we cycle, it doesn't matter whether on first or last
+               enable = (d.currentTabWorkArea()->count() > 1);
                break;
        case LFUN_BUFFER_SWITCH:
                // toggle on the current buffer, but do not toggle off
@@ -1868,6 +2057,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());
@@ -2381,15 +2571,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();
@@ -2400,28 +2581,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 : formats)
+               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();
@@ -2566,9 +2744,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());
 }
 
 
@@ -2617,7 +2799,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)
@@ -2639,7 +2821,7 @@ bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
 
        Buffer & buf = wa->bufferView().buffer();
 
-       if (close_buffer && GuiViewPrivate::busyBuffers.contains(&buf)) {
+       if (GuiViewPrivate::busyBuffers.contains(&buf)) {
                Alert::warning(_("Close document"), 
                        _("Document could not be closed because it is being processed by LyX."));
                return false;
@@ -2669,23 +2851,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);
@@ -2706,7 +2896,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();
@@ -2810,7 +3000,7 @@ bool GuiView::inOtherView(Buffer & buf)
 }
 
 
-void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np)
+void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
 {
        if (!documentBufferView())
                return;
@@ -2825,7 +3015,10 @@ void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np)
                                        next_index = (i == nwa - 1 ? 0 : i + 1);
                                else
                                        next_index = (i == 0 ? nwa - 1 : i - 1);
-                               setBuffer(&workArea(next_index)->bufferView().buffer());
+                               if (move)
+                                       twa->moveTab(i, next_index);
+                               else
+                                       setBuffer(&workArea(next_index)->bufferView().buffer());
                                break;
                        }
                }
@@ -2946,7 +3139,7 @@ void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
                        LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
                        dr.setMessage(log);
                        // Only skip reloading if the checkin was cancelled or
-                       // an error occured before the real checkin VCS command
+                       // an error occurred before the real checkin VCS command
                        // was executed, since the VCS might have changed the
                        // file even if it could not checkin successfully.
                        if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
@@ -3045,7 +3238,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;
@@ -3055,6 +3247,8 @@ void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
                string f1, f2;
 
                // f1
+               // it seems safe to assume we have a buffer
+               // coverity[FORWARD_NULL]
                if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
                        break;
 
@@ -3163,8 +3357,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());
 }
 
 
@@ -3218,11 +3427,11 @@ bool GuiView::GuiViewPrivate::asyncBufferProcessing(
        if (format.empty())
                format = used_buffer->params().getDefaultOutputFormat();
        processing_format = format;
-#if EXPORT_in_THREAD
        if (!msg.empty()) {
                progress_->clearMessages();
                gv_->message(msg);
        }
+#if EXPORT_in_THREAD
        GuiViewPrivate::busyBuffers.insert(used_buffer);
        Buffer * cloned_buffer = used_buffer->cloneFromMaster();
        if (!cloned_buffer) {
@@ -3244,7 +3453,6 @@ 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); 
@@ -3327,16 +3535,21 @@ 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()) {
+
+                       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();
+
+                       if ((dest.empty() && doc_buffer->isUnnamed())
+                           || !target_dir.isDirWritable()) {
                                exportBufferAs(*doc_buffer, cmd.argument());
                                break;
                        }
@@ -3424,7 +3637,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;
                                }
@@ -3439,22 +3654,24 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                }
 
                case LFUN_BUFFER_NEXT:
-                       gotoNextOrPreviousBuffer(NEXTBUFFER);
+                       gotoNextOrPreviousBuffer(NEXTBUFFER, false);
+                       break;
+
+               case LFUN_BUFFER_MOVE_NEXT:
+                       gotoNextOrPreviousBuffer(NEXTBUFFER, true);
                        break;
 
                case LFUN_BUFFER_PREVIOUS:
-                       gotoNextOrPreviousBuffer(PREVBUFFER);
+                       gotoNextOrPreviousBuffer(PREVBUFFER, false);
+                       break;
+
+               case LFUN_BUFFER_MOVE_PREVIOUS:
+                       gotoNextOrPreviousBuffer(PREVBUFFER, true);
                        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:
@@ -3565,6 +3782,14 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        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")
@@ -3725,17 +3950,22 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        break;
 
                case LFUN_BUFFER_ZOOM_IN:
-               case LFUN_BUFFER_ZOOM_OUT:
+               case LFUN_BUFFER_ZOOM_OUT: {
+                       // use a signed temp to avoid overflow
+                       int zoom = lyxrc.zoom;
                        if (cmd.argument().empty()) {
                                if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
-                                       lyxrc.zoom += 20;
+                                       zoom += 20;
                                else
-                                       lyxrc.zoom -= 20;
+                                       zoom -= 20;
                        } else
-                               lyxrc.zoom += convert<int>(cmd.argument());
+                               zoom += convert<int>(cmd.argument());
+
+                       if (zoom < static_cast<int>(zoom_min_))
+                               zoom = zoom_min_;
+                       lyxrc.zoom = zoom;
 
-                       if (lyxrc.zoom < 10)
-                               lyxrc.zoom = 10;
+                       dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
 
                        // The global QPixmapCache is used in GuiPainter to cache text
                        // painting so we must reset it.
@@ -3743,6 +3973,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:
@@ -3759,10 +3990,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.
+               // coverity[FORWARD_NULL]
                        Buffer const * doc_master = doc_buffer->masterBuffer();
                        FileName const path(doc_master->temppath());
                        string const texname = doc_master->isChild(doc_buffer)
@@ -3792,15 +4031,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;
                        }
@@ -3819,7 +4054,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:
@@ -3834,6 +4069,18 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
                        menuBar()->hide();
        }
+
+       // Need to update bv because many LFUNs here might have destroyed it
+       bv = currentBufferView();
+
+       // Clear non-empty selections
+       // (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())) {
+                       cur.clearSelection();
+               }
+       }
 }
 
 
@@ -3961,7 +4208,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"};
 
@@ -4238,8 +4485,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")