]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/GuiView.cpp
Speed up exit time
[lyx.git] / src / frontends / qt4 / GuiView.cpp
index 6f33da9f4b1fd4f99c6186fc212ddd5c614e085d..79cb8a4f9dacca382d1711ab8e5b41943ae26ef1 100644 (file)
@@ -1,4 +1,4 @@
-/**
+/**
  * \file GuiView.cpp
  * This file is part of LyX, the document processor.
  * Licence details can be found in the file COPYING.
@@ -32,6 +32,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 +151,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 +160,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 +222,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 +267,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 +282,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 +292,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 +328,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 +384,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 +465,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 +508,11 @@ 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)
 {
        // 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 +537,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 +561,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 +573,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 +604,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,27 +713,29 @@ 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())));
 }
 
 
 void GuiView::saveUISettings() const
 {
+       QSettings settings;
+
        // Save the toolbar private states
        ToolbarMap::iterator end = d.toolbars_.end();
        for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
-               it->second->saveSession();
+               it->second->saveSession(settings);
        // Now take care of all other dialogs
        map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
        for (; it!= d.dialogs_.end(); ++it)
-               it->second->saveSession();
+               it->second->saveSession(settings);
 }
 
 
@@ -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);
@@ -671,8 +777,6 @@ bool GuiView::restoreLayout()
                dialog->prepareView();
        if ((dialog = findOrBuild("progress", true)))
                dialog->prepareView();
-       if ((dialog = findOrBuild("findreplaceadv", true)))
-               dialog->prepareView();
 
        if (!restoreState(settings.value("layout").toByteArray(), 0))
                initToolbars();
@@ -785,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);
@@ -800,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 "
@@ -828,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)
  **/
@@ -967,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,
@@ -1002,8 +1118,30 @@ void GuiView::updateWindowTitle(GuiWorkArea * wa)
        if (wa != d.current_work_area_
                || wa->bufferView().buffer().isInternal())
                return;
-       setWindowTitle(qt_("LyX: ") + wa->windowTitle());
-       setWindowIconText(wa->windowIconText());
+       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(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();
 }
 
 
@@ -1048,7 +1186,7 @@ void GuiView::on_lastWorkAreaRemoved()
        updateDialog("document", "");
        updateDialogs();
 
-       resetWindowTitleAndIconText();
+       resetWindowTitle();
        updateStatusBar();
 
        if (lyxrc.open_buffers_in_tabs)
@@ -1060,7 +1198,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.
@@ -1137,7 +1275,7 @@ bool GuiView::event(QEvent * e)
                        updateDialog("document", "");
                        updateDialogs();
                } else {
-                       resetWindowTitleAndIconText();
+                       resetWindowTitle();
                }
                setFocus();
                return QMainWindow::event(e);
@@ -1164,10 +1302,9 @@ bool GuiView::event(QEvent * e)
        }
 }
 
-void GuiView::resetWindowTitleAndIconText()
+void GuiView::resetWindowTitle()
 {
        setWindowTitle(qt_("LyX"));
-       setWindowIconText(qt_("LyX"));
 }
 
 bool GuiView::focusNextPrevChild(bool /*next*/)
@@ -1200,6 +1337,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())
@@ -1213,7 +1367,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;
@@ -1381,24 +1535,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();
 }
 
 
@@ -1589,6 +1753,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
@@ -1626,7 +1805,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;
        }
 
@@ -1638,7 +1817,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:
@@ -1666,12 +1845,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:
@@ -1704,8 +1883,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:
@@ -1731,10 +1914,8 @@ 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") {
+               else if (name == "character" || name == "symbols"
+                       || name == "mathdelimiter" || name == "mathmatrix") {
                        if (!buf || buf->isReadonly())
                                enable = false;
                        else {
@@ -1780,7 +1961,7 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                        enable = false;
                break;
 
-       case LFUN_COMPLETION_COMPLETE:
+       case LFUN_COMPLETE:
                if (!d.current_work_area_
                        || !d.current_work_area_->completer().inlinePossible(
                        currentBufferView()->cursor()))
@@ -1804,15 +1985,21 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
 
        case LFUN_BUFFER_ZOOM_OUT:
                enable = doc_buffer && lyxrc.zoom > 10;
+               if (lyxrc.zoom <= 10)
+                       flag.message(_("Zoom level cannot be less than 10%."));
                break;
 
        case LFUN_BUFFER_ZOOM_IN:
-               enable = doc_buffer;
+               enable = doc_buffer != 0;
                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
@@ -1947,16 +2134,12 @@ void GuiView::openDocument(string const & fname)
        string filename;
 
        if (fname.empty()) {
-               FileDialog dlg(qt_("Select document to open"), LFUN_FILE_OPEN);
+               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")));
 
-               QStringList filter(qt_("LyX Documents (*.lyx)"));
-               filter << qt_("LyX-1.3.x Documents (*.lyx13)")
-                       << qt_("LyX-1.4.x Documents (*.lyx14)")
-                       << qt_("LyX-1.5.x Documents (*.lyx15)")
-                       << qt_("LyX-1.6.x Documents (*.lyx16)");
+               QStringList const filter(qt_("LyX Documents (*.lyx)"));
                FileDialog::Result result =
                        dlg.open(toqstr(initpath), filter);
 
@@ -2090,7 +2273,7 @@ void GuiView::importDocument(string const & argument)
                docstring const text = bformat(_("Select %1$s file to import"),
                        formats.prettyName(format));
 
-               FileDialog dlg(toqstr(text), LFUN_BUFFER_IMPORT);
+               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")));
@@ -2120,6 +2303,18 @@ void GuiView::importDocument(string const & argument)
        // get absolute path of file
        FileName const fullname(support::makeAbsPath(filename));
 
+       // Can happen if the user entered a path into the dialog
+       // (see bug #7437)
+       if (fullname.onlyFileName().empty()) {
+               docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
+                                         "Aborting import."),
+                                       from_utf8(fullname.absFileName()));
+               frontend::Alert::error(_("File name error"), msg);
+               message(_("Canceled."));
+               return;
+       }
+
+
        FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
 
        // Check if the document already is open
@@ -2212,7 +2407,7 @@ void GuiView::insertLyXFile(docstring const & fname)
                        initpath = trypath;
 
                // FIXME UNICODE
-               FileDialog dlg(qt_("Select LyX document to insert"), LFUN_FILE_INSERT);
+               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(),
@@ -2254,8 +2449,7 @@ bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kin
 
                // No argument? Ask user through dialog.
                // FIXME UNICODE
-               FileDialog dlg(qt_("Choose a filename to save document as"),
-                                  LFUN_BUFFER_WRITE_AS);
+               FileDialog dlg(qt_("Choose a filename to save document as"));
                dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
                dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
 
@@ -2364,7 +2558,7 @@ bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kin
 
        bool const saved = saveBuffer(b, fname);
        if (saved)
-               b.reload(false);
+               b.reload();
        return saved;
 }
 
@@ -2378,7 +2572,7 @@ struct PrettyNameComparator
 };
 
 
-bool GuiView::exportBufferAs(Buffer & b)
+bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
 {
        FileName fname = b.fileName();
 
@@ -2386,45 +2580,50 @@ bool GuiView::exportBufferAs(Buffer & b)
        dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
 
        QStringList types;
-       QString const anyformat = qt_("Any supported format (*.*)");
+       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() && it->inExportMenu())
+               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();
-       map<docstring, string> fmap;
+       map<QString, string> fmap;
+       QString filter;
+       string ext;
        for (; fit != export_formats.end(); ++fit) {
                docstring const loc_prettyname =
                        translateIfPossible(from_utf8((*fit)->prettyname()));
-               types << toqstr(bformat(_("%1$s (*.%2$s)"),
-                                       loc_prettyname,
-                                       from_ascii((*fit)->extension())));
-               fmap[loc_prettyname] = (*fit)->name();
+               QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
+                                                    loc_prettyname,
+                                                    from_ascii((*fit)->extension())));
+               types << loc_filter;
+               fmap[loc_filter] = (*fit)->name();
+               if (from_ascii((*fit)->name()) == iformat) {
+                       filter = loc_filter;
+                       ext = (*fit)->extension();
+               }
        }
-       QString filter;
+       string ofname = fname.onlyFileName();
+       if (!ext.empty())
+               ofname = support::changeExtension(ofname, ext);
        FileDialog::Result result =
                dlg.save(toqstr(fname.onlyPath().absFileName()),
                         types,
-                        toqstr(fname.onlyFileName()),
+                        toqstr(ofname),
                         &filter);
        if (result.first != FileDialog::Chosen)
                return false;
 
-       string s = fromqstr(filter);
-       size_t pos = s.find(" (*.");
-       LATTEST(pos != string::npos);
-       string fmt_prettyname = s.substr(0, pos);
        string fmt_name;
        fname.set(fromqstr(result.second));
        if (filter == anyformat)
                fmt_name = formats.getFormatFromExtension(fname.extension());
        else
-               fmt_name = fmap[from_utf8(fmt_prettyname)];
-       LYXERR(Debug::FILES, "fmt_prettyname=" << fmt_prettyname
+               fmt_name = fmap[filter];
+       LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
               << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
 
        if (fmt_name.empty() || fname.empty())
@@ -2441,7 +2640,7 @@ bool GuiView::exportBufferAs(Buffer & b)
                        text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
                switch (ret) {
                case 0: break;
-               case 1: return exportBufferAs(b);
+               case 1: return exportBufferAs(b, from_ascii(fmt_name));
                case 2: return false;
                }
        }
@@ -2600,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)
@@ -2622,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;
@@ -2652,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);
@@ -2689,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();
@@ -2793,7 +3000,7 @@ bool GuiView::inOtherView(Buffer & buf)
 }
 
 
-void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np)
+void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
 {
        if (!documentBufferView())
                return;
@@ -2808,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;
                        }
                }
@@ -2849,6 +3059,7 @@ static bool ensureBufferClean(Buffer * buffer)
 
 bool GuiView::reloadBuffer(Buffer & buf)
 {
+       currentBufferView()->cursor().reset();
        Buffer::ReadStatus status = buf.reload();
        return status == Buffer::ReadSuccess;
 }
@@ -2886,7 +3097,7 @@ void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
                if (!buffer->lyxvc().inUse()) {
                        if (buffer->lyxvc().registrer()) {
                                reloadBuffer(*buffer);
-                               dr.suppressMessageUpdate();
+                               dr.clearMessageUpdate();
                        }
                }
                break;
@@ -2929,7 +3140,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)
@@ -2966,7 +3177,7 @@ void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
                LASSERT(buffer, return);
                if (buffer->lyxvc().revert()) {
                        reloadBuffer(*buffer);
-                       dr.suppressMessageUpdate();
+                       dr.clearMessageUpdate();
                }
                break;
 
@@ -2974,7 +3185,7 @@ void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
                LASSERT(buffer, return);
                buffer->lyxvc().undoLast();
                reloadBuffer(*buffer);
-               dr.suppressMessageUpdate();
+               dr.clearMessageUpdate();
                break;
 
        case LFUN_VC_REPO_UPDATE:
@@ -3151,6 +3362,13 @@ bool GuiView::goToFileRow(string const & argument)
 }
 
 
+void GuiView::toolBarPopup(const QPoint & /*pos*/)
+{
+       QMenu * menu = guiApp->menus().menu(toqstr("context-toolbars"), * this);
+       menu->exec(QCursor::pos());
+}
+
+
 template<class T>
 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
 {
@@ -3201,11 +3419,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) {
@@ -3310,13 +3528,18 @@ 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 (!doc_buffer->fileName().onlyPath().isDirWritable()) {
-                               exportBufferAs(*doc_buffer);
+                       if ((dest.empty() && doc_buffer->isUnnamed())
+                           || !target_dir.isDirWritable()) {
+                               exportBufferAs(*doc_buffer, cmd.argument());
                                break;
                        }
                        /* TODO/Review: Is it a problem to also export the children?
@@ -3331,10 +3554,14 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        break;
                }
 
-               case LFUN_BUFFER_EXPORT_AS:
+               case LFUN_BUFFER_EXPORT_AS: {
                        LASSERT(doc_buffer, break);
-                       exportBufferAs(*doc_buffer);
+                       docstring f = cmd.argument();
+                       if (f.empty())
+                               f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
+                       exportBufferAs(*doc_buffer, f);
                        break;
+               }
 
                case LFUN_BUFFER_UPDATE: {
                        d.asyncBufferProcessing(argument,
@@ -3399,7 +3626,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;
                                }
@@ -3414,22 +3643,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:
@@ -3447,7 +3678,6 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
 
                case LFUN_FILE_INSERT_PLAINTEXT:
                case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
-                       bool const as_paragraph = (cmd.action() == LFUN_FILE_INSERT_PLAINTEXT_PARA);
                        string const fname = to_utf8(cmd.argument());
                        if (!fname.empty() && !FileName::isAbsolute(fname)) {
                                dr.setMessage(_("Absolute filename expected."));
@@ -3456,8 +3686,7 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        
                        FileName filename(fname);
                        if (fname.empty()) {
-                               FileDialog dlg(qt_("Select file to insert"), (as_paragraph ?
-                                       LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
+                               FileDialog dlg(qt_("Select file to insert"));
 
                                FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
                                        QStringList(qt_("All Files (*)")));
@@ -3542,6 +3771,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")
@@ -3682,7 +3919,7 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        break;
 
 
-               case LFUN_COMPLETION_COMPLETE:
+               case LFUN_COMPLETE:
                        if (d.current_work_area_)
                                d.current_work_area_->completer().tab();
                        break;
@@ -3702,17 +3939,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 (lyxrc.zoom < 10)
-                               lyxrc.zoom = 10;
+                       if (zoom < 10)
+                               zoom = 10;
+                       lyxrc.zoom = zoom;
+
+                       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.
@@ -3720,6 +3962,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:
@@ -3769,15 +4012,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;
                        }
@@ -3810,8 +4049,18 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
        if (isFullScreen()) {
                if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
                        menuBar()->hide();
-               if (statusBar()->isVisible())
-                       statusBar()->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();
+               }
        }
 }
 
@@ -3870,7 +4119,8 @@ void GuiView::toggleFullScreen()
                setContentsMargins(-2, -2, -2, -2);
                saveLayout();
                setWindowState(windowState() ^ Qt::WindowFullScreen);
-               statusBar()->hide();
+               if (lyxrc.full_screen_statusbar)
+                       statusBar()->hide();
                if (lyxrc.full_screen_menubar)
                        menuBar()->hide();
                if (lyxrc.full_screen_toolbars) {
@@ -3939,7 +4189,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"};
 
@@ -4216,8 +4466,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")