]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/GuiView.cpp
Don't load bibtopic with Biblatex
[lyx.git] / src / frontends / qt4 / GuiView.cpp
index 5e8893bfc87770c210a7a5763ef3e327bf09be79..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>
@@ -149,7 +150,8 @@ namespace {
 class BackgroundWidget : public QWidget
 {
 public:
-       BackgroundWidget()
+       BackgroundWidget(int width, int height)
+               : width_(width), height_(height)
        {
                LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
                if (!lyxrc.show_banner)
@@ -157,32 +159,44 @@ public:
                /// The text to be written on top of the pixmap
                QString const text = lyx_version ?
                        qt_("version ") + lyx_version : qt_("unknown version");
+#if QT_VERSION >= 0x050000
+               QString imagedir = "images/";
+               FileName fname = imageLibFileSearch(imagedir, "banner", "svgz");
+               QSvgRenderer svgRenderer(toqstr(fname.absFileName()));
+               if (svgRenderer.isValid()) {
+                       splash_ = QPixmap(splashSize());
+                       QPainter painter(&splash_);
+                       svgRenderer.render(&painter);
+                       splash_.setDevicePixelRatio(pixelRatio());
+               } else {
+                       splash_ = getPixmap("images/", "banner", "png");
+               }
+#else
                splash_ = getPixmap("images/", "banner", "svgz,png");
+#endif
 
                QPainter pain(&splash_);
                pain.setPen(QColor(0, 0, 0));
-               double const multiplier = splashPixelRatio() / pixelRatio();
-               int const size = static_cast<int>(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble() * multiplier);
-               int const x = static_cast<int>(190 * multiplier);
-               int const y = static_cast<int>(225 * multiplier);
+               qreal const fsize = fontSize();
+               QPointF const position = textPosition();
                LYXERR(Debug::GUI,
                        "widget pixel ratio: " << pixelRatio() <<
                        " splash pixel ratio: " << splashPixelRatio() <<
-                       " version text size,position: " << size << "@" << x << "+" << y);
+                       " version text size,position: " << fsize << "@" << position.x() << "+" << position.y());
                QFont font;
                // The font used to display the version info
                font.setStyleHint(QFont::SansSerif);
                font.setWeight(QFont::Bold);
-               font.setPointSize(size);
+               font.setPointSizeF(fsize);
                pain.setFont(font);
-               pain.drawText(x, y, text);
+               pain.drawText(position, text);
                setFocusPolicy(Qt::StrongFocus);
        }
 
        void paintEvent(QPaintEvent *)
        {
-               int const w = static_cast<int>(splash_.width() / splashPixelRatio());
-               int const h = static_cast<int>(splash_.height() / splashPixelRatio());
+               int const w = width_;
+               int const h = height_;
                int const x = (width() - w) / 2;
                int const y = (height() - h) / 2;
                LYXERR(Debug::GUI,
@@ -207,6 +221,8 @@ public:
 
 private:
        QPixmap splash_;
+       int const width_;
+       int const height_;
 
        /// Current ratio between physical pixels and device-independent pixels
        double pixelRatio() const {
@@ -217,6 +233,20 @@ private:
 #endif
        }
 
+       qreal fontSize() const {
+               return toqstr(lyxrc.font_sizes[FONT_SIZE_NORMAL]).toDouble();
+       }
+
+       QPointF textPosition() const {
+               return QPointF(width_/2 - 18, height_/2 + 45);
+       }
+
+       QSize splashSize() const {
+               return QSize(
+                       static_cast<unsigned int>(width_ * pixelRatio()),
+                       static_cast<unsigned int>(height_ * pixelRatio()));
+       }
+
        /// Ratio between physical pixels and device-independent pixels of splash image
        double splashPixelRatio() const {
 #if QT_VERSION >= 0x050000
@@ -236,8 +266,12 @@ typedef shared_ptr<Dialog> DialogPtr;
 } // namespace anon
 
 
-struct GuiView::GuiViewPrivate
+class GuiView::GuiViewPrivate
 {
+       /// noncopyable
+       GuiViewPrivate(GuiViewPrivate const &);
+       void operator=(GuiViewPrivate const &);
+public:
        GuiViewPrivate(GuiView * gv)
                : gv_(gv), current_work_area_(0), current_main_work_area_(0),
                layout_(0), autosave_timeout_(5000),
@@ -264,7 +298,7 @@ struct GuiView::GuiViewPrivate
                }
 
                splitter_ = new QSplitter;
-               bg_widget_ = new BackgroundWidget;
+               bg_widget_ = new BackgroundWidget(400, 250);
                stack_widget_ = new QStackedWidget;
                stack_widget_->addWidget(bg_widget_);
                stack_widget_->addWidget(splitter_);
@@ -293,62 +327,6 @@ struct GuiView::GuiViewPrivate
                delete stack_widget_;
        }
 
-       QMenu * toolBarPopup(GuiView * parent)
-       {
-               // FIXME: translation
-               QMenu * menu = new QMenu(parent);
-               QActionGroup * iconSizeGroup = new QActionGroup(parent);
-
-               QAction * smallIcons = new QAction(iconSizeGroup);
-               smallIcons->setText(qt_("Small-sized icons"));
-               smallIcons->setCheckable(true);
-               QObject::connect(smallIcons, SIGNAL(triggered()),
-                       parent, SLOT(smallSizedIcons()));
-               menu->addAction(smallIcons);
-
-               QAction * normalIcons = new QAction(iconSizeGroup);
-               normalIcons->setText(qt_("Normal-sized icons"));
-               normalIcons->setCheckable(true);
-               QObject::connect(normalIcons, SIGNAL(triggered()),
-                       parent, SLOT(normalSizedIcons()));
-               menu->addAction(normalIcons);
-
-               QAction * bigIcons = new QAction(iconSizeGroup);
-               bigIcons->setText(qt_("Big-sized icons"));
-               bigIcons->setCheckable(true);
-               QObject::connect(bigIcons, SIGNAL(triggered()),
-                       parent, SLOT(bigSizedIcons()));
-               menu->addAction(bigIcons);
-
-               QAction * hugeIcons = new QAction(iconSizeGroup);
-               hugeIcons->setText(qt_("Huge-sized icons"));
-               hugeIcons->setCheckable(true);
-               QObject::connect(hugeIcons, SIGNAL(triggered()),
-                       parent, SLOT(hugeSizedIcons()));
-               menu->addAction(hugeIcons);
-
-               QAction * giantIcons = new QAction(iconSizeGroup);
-               giantIcons->setText(qt_("Giant-sized icons"));
-               giantIcons->setCheckable(true);
-               QObject::connect(giantIcons, SIGNAL(triggered()),
-                       parent, SLOT(giantSizedIcons()));
-               menu->addAction(giantIcons);
-
-               unsigned int cur = parent->iconSize().width();
-               if ( cur == parent->d.smallIconSize)
-                       smallIcons->setChecked(true);
-               else if (cur == parent->d.normalIconSize)
-                       normalIcons->setChecked(true);
-               else if (cur == parent->d.bigIconSize)
-                       bigIcons->setChecked(true);
-               else if (cur == parent->d.hugeIconSize)
-                       hugeIcons->setChecked(true);
-               else if (cur == parent->d.giantIconSize)
-                       giantIcons->setChecked(true);
-
-               return menu;
-       }
-
        void setBackground()
        {
                stack_widget_->setCurrentWidget(bg_widget_);
@@ -405,6 +383,60 @@ struct GuiView::GuiViewPrivate
                processing_thread_watcher_.setFuture(f);
        }
 
+       QSize iconSize(docstring const & icon_size)
+       {
+               unsigned int size;
+               if (icon_size == "small")
+                       size = smallIconSize;
+               else if (icon_size == "normal")
+                       size = normalIconSize;
+               else if (icon_size == "big")
+                       size = bigIconSize;
+               else if (icon_size == "huge")
+                       size = hugeIconSize;
+               else if (icon_size == "giant")
+                       size = giantIconSize;
+               else
+                       size = icon_size.empty() ? normalIconSize : convert<int>(icon_size);
+
+               if (size < smallIconSize)
+                       size = smallIconSize;
+
+               return QSize(size, size);
+       }
+
+       QSize iconSize(QString const & icon_size)
+       {
+               return iconSize(qstring_to_ucs4(icon_size));
+       }
+
+       string & iconSize(QSize const & qsize)
+       {
+               LATTEST(qsize.width() == qsize.height());
+
+               static string icon_size;
+
+               unsigned int size = qsize.width();
+
+               if (size < smallIconSize)
+                       size = smallIconSize;
+
+               if (size == smallIconSize)
+                       icon_size = "small";
+               else if (size == normalIconSize)
+                       icon_size = "normal";
+               else if (size == bigIconSize)
+                       icon_size = "big";
+               else if (size == hugeIconSize)
+                       icon_size = "huge";
+               else if (size == giantIconSize)
+                       icon_size = "giant";
+               else
+                       icon_size = convert<string>(size);
+
+               return icon_size;
+       }
+
 public:
        GuiView * gv_;
        GuiWorkArea * current_work_area_;
@@ -475,10 +507,14 @@ QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
 
 
 GuiView::GuiView(int id)
-       : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0)
+       : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0),
+         command_execute_(false), minibuffer_focus_(false)
 {
+       connect(this, SIGNAL(bufferViewChanged()),
+               this, SLOT(onBufferViewChanged()));
+
        // GuiToolbars *must* be initialised before the menu bar.
-       normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
+       setIconSize(QSize(d.normalIconSize, d.normalIconSize)); // at least on Mac the default is 32 otherwise, which is huge
        constructToolbars();
 
        // set ourself as the current view. This is needed for the menu bar
@@ -515,7 +551,7 @@ GuiView::GuiView(int id)
 #endif
 
 #endif
-       resetWindowTitleAndIconText();
+       resetWindowTitle();
 
        // use tabbed dock area for multiple docks
        // (such as "source" and "messages")
@@ -539,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();
 
@@ -553,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);
@@ -664,7 +722,7 @@ void GuiView::saveLayout() const
        settings.setValue("geometry", saveGeometry());
 #endif
        settings.setValue("layout", saveState(0));
-       settings.setValue("icon_size", iconSize());
+       settings.setValue("icon_size", toqstr(d.iconSize(iconSize())));
 }
 
 
@@ -691,17 +749,7 @@ bool GuiView::restoreLayout()
                return false;
 
        //code below is skipped when when ~/.config/LyX is (re)created
-       QSize icon_size = settings.value(icon_key).toSize();
-       // Check whether session size changed.
-       if (icon_size.width() != int(d.smallIconSize) &&
-           icon_size.width() != int(d.normalIconSize) &&
-           icon_size.width() != int(d.bigIconSize) &&
-           icon_size.width() != int(d.hugeIconSize) &&
-           icon_size.width() != int(d.giantIconSize)) {
-               icon_size.setWidth(d.normalIconSize);
-               icon_size.setHeight(d.normalIconSize);
-       }
-       setIconSize(icon_size);
+       setIconSize(d.iconSize(settings.value(icon_key).toString()));
 
 #if defined(Q_WS_X11) || defined(QPA_XCB)
        QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
@@ -841,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);
@@ -856,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 "
@@ -1049,36 +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::hugeSizedIcons()
-{
-       setIconSize(QSize(d.hugeIconSize, d.hugeIconSize));
-}
-
-
-void GuiView::giantSizedIcons()
-{
-       setIconSize(QSize(d.giantIconSize, d.giantIconSize));
-}
-
-
 void GuiView::clearMessage()
 {
        // FIXME: This code was introduced in r19643 to fix bug #4123. However,
@@ -1096,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();
@@ -1144,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)
@@ -1226,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);
        }
@@ -1264,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*/)
@@ -1300,6 +1331,13 @@ void GuiView::setBusy(bool busy)
 }
 
 
+void GuiView::resetCommandExecute()
+{
+       command_execute_ = false;
+       updateToolbars();
+}
+
+
 double GuiView::pixelRatio() const
 {
 #if QT_VERSION >= 0x050000
@@ -1323,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;
@@ -1387,6 +1425,7 @@ void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
        if (!wa) {
                d.current_work_area_ = 0;
                d.setBackground();
+               Q_EMIT bufferViewChanged();
                return;
        }
 
@@ -1491,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();
 }
 
 
@@ -1603,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.
@@ -1699,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
@@ -1736,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;
        }
 
@@ -1748,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:
@@ -1776,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:
@@ -1814,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:
@@ -1841,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())
@@ -1914,13 +1985,24 @@ 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:
                // because we cycle, it doesn't matter whether on first or last
@@ -1975,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());
@@ -2488,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();
@@ -2507,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();
@@ -2673,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());
 }
 
 
@@ -2724,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)
@@ -2746,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;
@@ -2776,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);
@@ -2813,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();
@@ -2917,7 +3000,7 @@ bool GuiView::inOtherView(Buffer & buf)
 }
 
 
-void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np)
+void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
 {
        if (!documentBufferView())
                return;
@@ -2932,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;
                        }
                }
@@ -3152,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;
@@ -3162,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;
 
@@ -3270,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());
 }
 
 
@@ -3351,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); 
@@ -3434,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;
                        }
@@ -3531,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;
                                }
@@ -3546,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:
@@ -3672,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")
@@ -3832,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 (lyxrc.zoom < 10)
-                               lyxrc.zoom = 10;
+                       if (zoom < static_cast<int>(zoom_min_))
+                               zoom = zoom_min_;
+                       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.
@@ -3850,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:
@@ -3866,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)
@@ -3899,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;
                        }
@@ -3926,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:
@@ -3946,7 +4074,7 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
        bv = currentBufferView();
 
        // Clear non-empty selections
-        // (e.g. from a "char-forward-select" followed by "char-backward-select")
+       // (e.g. from a "char-forward-select" followed by "char-backward-select")
        if (bv) {
                Cursor & cur = bv->cursor();
                if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
@@ -4080,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"};
 
@@ -4357,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")