]> git.lyx.org Git - features.git/blobdiff - src/frontends/qt/GuiApplication.cpp
Give the user visible feedback when reconfiguring.
[features.git] / src / frontends / qt / GuiApplication.cpp
index 2cdd5f6e3b37134ad1c96a0a1f751fc23e5d09fb..01a646de37372e84bc6b62219456b2d124859129 100644 (file)
@@ -16,9 +16,7 @@
 
 #include "ToolTipFormatter.h"
 #include "ColorCache.h"
-#include "ColorSet.h"
 #include "GuiClipboard.h"
-#include "GuiKeySymbol.h"
 #include "GuiSelection.h"
 #include "GuiView.h"
 #include "Menus.h"
@@ -37,6 +35,7 @@
 #include "CmdDef.h"
 #include "Color.h"
 #include "Converter.h"
+#include "Cursor.h"
 #include "CutAndPaste.h"
 #include "ErrorList.h"
 #include "Font.h"
 
 #include "insets/InsetText.h"
 
+#include "support/checksum.h"
 #include "support/convert.h"
 #include "support/debug.h"
 #include "support/ExceptionMessage.h"
+#include "support/environment.h"
 #include "support/FileName.h"
 #include "support/filetools.h"
 #include "support/ForkedCalls.h"
 #include "support/lassert.h"
 #include "support/lstrings.h"
 #include "support/lyxalgo.h" // sorted
+#include "support/textutils.h"
 #include "support/Messages.h"
 #include "support/os.h"
 #include "support/Package.h"
 #include "support/TempFile.h"
-#include "support/textutils.h"
 
 #ifdef Q_OS_MAC
 #include "support/AppleScript.h"
+#include "support/AppleSupport.h"
 #include "support/linkback/LinkBackProxy.h"
 #endif
 
 #include <queue>
+#include <tuple>
 
 #include <QByteArray>
+#include <QBitmap>
 #include <QDateTime>
+#if QT_VERSION < 0x060000
 #include <QDesktopWidget>
-#include <QDir>
+#endif
 #include <QEvent>
 #include <QFileOpenEvent>
 #include <QFileInfo>
 #include <QMenuBar>
 #include <QMimeData>
 #include <QObject>
+#include <QPainter>
 #include <QPixmap>
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
+#include <QRandomGenerator>
+#endif
 #include <QRegExp>
 #include <QSessionManager>
 #include <QSettings>
 
 #if (QT_VERSION < 0x050000) || (QT_VERSION >= 0x050400)
 #if defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)
+#if (QT_VERSION < 0x060000)
 #if (QT_VERSION < 0x050000)
 #include <QWindowsMime>
 #define QWINDOWSMIME QWindowsMime
 #include <QWinMime>
 #define QWINDOWSMIME QWinMime
 #endif
+#endif
 #ifdef Q_CC_GNU
 #include <wtypes.h>
 #endif
 #endif
 #endif
 
-#ifdef Q_OS_MAC
+#if defined(Q_OS_MAC) && (QT_VERSION < 0x060000)
 #include <QMacPasteboardMime>
 #endif // Q_OS_MAC
 
-#include <boost/crc.hpp>
-
 #include <exception>
 #include <sstream>
 #include <vector>
@@ -156,16 +165,6 @@ using namespace std;
 using namespace lyx::support;
 
 
-static void initializeResources()
-{
-       static bool initialized = false;
-       if (!initialized) {
-               Q_INIT_RESOURCE(Resources);
-               initialized = true;
-       }
-}
-
-
 namespace lyx {
 
 frontend::Application * createApplication(int & argc, char * argv[])
@@ -189,6 +188,24 @@ frontend::Application * createApplication(int & argc, char * argv[])
        AllowSetForegroundWindow(ASFW_ANY);
 #endif
 
+
+// Setup high DPI handling. This is a bit complicated, but will be default in Qt6.
+// macOS does it by itself.
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && !defined(Q_OS_MAC)
+#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
+    // Attribute Qt::AA_EnableHighDpiScaling must be set before QCoreApplication is created
+    if (getEnv("QT_ENABLE_HIGHDPI_SCALING").empty()
+               && getEnv("QT_AUTO_SCREEN_SCALE_FACTOR").empty())
+        QApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
+#endif
+
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+    // HighDPI scale factor policy must be set before QGuiApplication is created
+    if (getEnv("QT_SCALE_FACTOR_ROUNDING_POLICY").empty())
+        QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
+#endif
+#endif
+
        frontend::GuiApplication * guiApp = new frontend::GuiApplication(argc, argv);
        // I'd rather do that in the constructor, but I do not think that
        // the palette is accessible there.
@@ -486,100 +503,127 @@ QString themeIconName(QString const & action)
 }
 
 
-QString iconName(FuncRequest const & f, bool unknown)
+IconInfo iconInfo(FuncRequest const & f, bool unknown, bool rtl)
 {
-       initializeResources();
-       QString name1;
-       QString name2;
-       QString path;
-       switch (f.action()) {
-       case LFUN_MATH_INSERT:
-               if (!f.argument().empty()) {
-                       path = "math/";
-                       name1 = findImg(toqstr(f.argument()).mid(1));
-               }
-               break;
-       case LFUN_MATH_DELIM:
-       case LFUN_MATH_BIGDELIM:
-               path = "math/";
-               name1 = findImg(toqstr(f.argument()));
-               break;
-       case LFUN_CALL:
-               path = "commands/";
-               name1 = toqstr(f.argument());
-               break;
-       case LFUN_COMMAND_ALTERNATIVES: {
-               // use the first of the alternative commands
-               docstring firstcom;
-               docstring dummy = split(f.argument(), firstcom, ';');
-               name1 = toqstr(firstcom);
-               name1.replace(' ', '_');
-               break;
-       }
-       default:
-               name2 = toqstr(lyxaction.getActionName(f.action()));
-               name1 = name2;
+       IconInfo res;
+
+       QStringList names;
+       QString lfunname = toqstr(lyxaction.getActionName(f.action()));
+
+       if (!f.argument().empty()) {
+               // if there are arguments, first search an icon which name is the full thing
+               QString name = lfunname  + ' ' + toqstr(f.argument());
+               name.replace(' ', '_');
+               name.replace(';', '_');
+               name.replace('\\', "backslash");
+               // avoid duplication for these
+               name.replace("dialog-toggle", "dialog-show");
+               names << name;
 
-               if (!f.argument().empty()) {
-                       name1 = name2 + ' ' + toqstr(f.argument());
+               // then special default icon for some lfuns
+               switch (f.action()) {
+               case LFUN_MATH_INSERT:
+                       names <<  "math/" + findImg(toqstr(f.argument()).mid(1));
+                       break;
+               case LFUN_MATH_DELIM:
+               case LFUN_MATH_BIGDELIM:
+                       names << "math/" + findImg(toqstr(f.argument()));
+                       break;
+               case LFUN_CALL:
+                       names << "commands/" + toqstr(f.argument());
+                       break;
+               case LFUN_COMMAND_ALTERNATIVES: {
+                       // use the first of the alternative commands
+                       docstring firstcom;
+                       docstring dummy = split(f.argument(), firstcom, ';');
+                       QString name1 = toqstr(firstcom);
                        name1.replace(' ', '_');
+                       name1.replace(';', '_');
                        name1.replace('\\', "backslash");
+                       names << name1;
+                       break;
+               }
+               default:
+                       break;
                }
        }
 
+       // next thing to try is function name alone
+       names << lfunname;
+
+       // and finally maybe the unknown icon
+       if (unknown)
+               names << "unknown";
+
+       search_mode const mode = theGuiApp()->imageSearchMode();
+       // The folders where icons are searched for
        QStringList imagedirs;
-       imagedirs << "images/" << "images/ipa/";
-       search_mode mode = theGuiApp()->imageSearchMode();
-       for (int i = 0; i < imagedirs.size(); ++i) {
-               QString imagedir = imagedirs.at(i) + path;
-               FileName fname = imageLibFileSearch(imagedir, name1, "svgz,png", mode);
-               if (fname.exists())
-                       return toqstr(fname.absFileName());
-
-               fname = imageLibFileSearch(imagedir, name2, "svgz,png", mode);
-               if (fname.exists())
-                       return toqstr(fname.absFileName());
-       }
-
-       path = ":/images/" + path;
-       QDir res(path);
-       if (!res.exists()) {
-               LYXERR0("Directory " << path << " not found in resource!");
-               return QString();
-       }
-       if (res.exists(name1 + ".svgz"))
-               return path + name1 + ".svgz";
-       else if (res.exists(name1 + ".png"))
-               return path + name1 + ".png";
-
-       if (res.exists(name2 + ".svgz"))
-               return path + name2 + ".svgz";
-       else if (res.exists(name2 + ".png"))
-               return path + name2 + ".png";
-
-       LYXERR(Debug::GUI, "Cannot find icon with filename "
-                          << "\"" << name1 << ".{svgz,png}\""
-                          << " or filename "
-                          << "\"" << name2 << ".{svgz,png}\""
-                          << " for command \""
+       imagedirs << "images/ipa/" << "images/";
+       // This is used to search for rtl version of icons which have the +rrtl suffix.
+       QStringList suffixes;
+       if (rtl)
+               suffixes << "+rtl";
+       suffixes << QString();
+
+       for (QString const & imagedir : imagedirs)
+               for (QString const & name : names)
+                       for (QString const & suffix : suffixes) {
+                               QString id = imagedir;
+                               FileName fname = imageLibFileSearch(id, name + suffix, "svgz,png", mode);
+                               if (fname.exists()) {
+                                       docstring const fpath = fname.absoluteFilePath();
+                                       res.filepath = toqstr(fname.absFileName());
+                                       // these icons are subject to inversion in dark mode
+                                       res.invert = (contains(fpath, from_ascii("math")) || contains(fpath, from_ascii("ert-insert"))
+                                                     || suffixIs(fname.onlyPath().absoluteFilePath(), from_ascii("ipa")));
+                                       res.swap = rtl && suffix.isEmpty();
+                                       return res;
+                               }
+                       }
+
+       LYXERR(Debug::GUI, "Cannot find icon for command \""
                           << lyxaction.getActionName(f.action())
                           << '(' << to_utf8(f.argument()) << ")\"");
 
-       if (unknown) {
-               QString imagedir = "images/";
-               FileName fname = imageLibFileSearch(imagedir, "unknown", "svgz,png", mode);
-               if (fname.exists())
-                       return toqstr(fname.absFileName());
-               return QString(":/images/unknown.svgz");
-       }
-
-       return QString();
+       return res;
 }
 
 
-bool getPixmap(QPixmap & pixmap, QString const & path)
+QPixmap prepareForDarkMode(QPixmap pixmap)
 {
-       return pixmap.load(path);
+       QPalette palette = QPalette();
+       QColor text_color = palette.color(QPalette::Active, QPalette::WindowText);
+       QColor bg_color = palette.color(QPalette::Active, QPalette::Window);
+
+       // guess whether we are in dark mode
+       if (text_color.black() > bg_color.black())
+               // not in dark mode, do nothing
+               return pixmap;
+
+       // create a layer with black text turned to QPalette::WindowText
+       QPixmap black_overlay(pixmap.size());
+       black_overlay.fill(text_color);
+       black_overlay.setMask(pixmap.createMaskFromColor(Qt::black, Qt::MaskOutColor));
+
+       // create a layer with blue text turned to lighter blue
+       QPixmap blue_overlay(pixmap.size());
+       QColor math_blue(0, 0, 255);
+       blue_overlay.fill(guiApp->colorCache().get(Color(Color_math)));
+       blue_overlay.setMask(pixmap.createMaskFromColor(math_blue, Qt::MaskOutColor));
+
+       // create a layer with ("latex") red text turned to lighter red
+       QPixmap red_overlay(pixmap.size());
+       QColor math_red(128, 0, 0);
+       red_overlay.fill(guiApp->colorCache().get(Color(Color_latex)));
+       red_overlay.setMask(pixmap.createMaskFromColor(math_red, Qt::MaskOutColor));
+
+       // put layers on top of existing pixmap
+       QPainter painter(&pixmap);
+       painter.drawPixmap(pixmap.rect(), black_overlay);
+       painter.drawPixmap(pixmap.rect(), blue_overlay);
+       painter.drawPixmap(pixmap.rect(), red_overlay);
+
+       return pixmap;
 }
 
 
@@ -590,28 +634,23 @@ QPixmap getPixmap(QString const & path, QString const & name, QString const & ex
        QString fpath = toqstr(fname.absFileName());
        QPixmap pixmap = QPixmap();
 
-       if (getPixmap(pixmap, fpath)) {
+       if (pixmap.load(fpath)) {
+               if (fpath.contains("math") || fpath.contains("ipa")
+                   || fpath.contains("bullets"))
+                       return prepareForDarkMode(pixmap);
                return pixmap;
        }
 
-       QStringList exts = ext.split(",");
-       fpath = ":/" + path + name + ".";
-       for (int i = 0; i < exts.size(); ++i) {
-               if (getPixmap(pixmap, fpath + exts.at(i))) {
-                       return pixmap;
-               }
-       }
-
        bool const list = ext.contains(",");
-       LYXERR0("Cannot load pixmap \""
-               << path << name << "." << (list ? "{" : "") << ext
-               << (list ? "}" : "") << "\", please verify resource system!");
+       LYXERR(Debug::GUI, "Cannot load pixmap \""
+                       << path << "/" << name << "." << (list ? "{" : "") << ext
+               << (list ? "}" : "") << "\".");
 
        return QPixmap();
 }
 
 
-QIcon getIcon(FuncRequest const & f, bool unknown)
+QIcon getIcon(FuncRequest const & f, bool unknown, bool rtl)
 {
 #if (QT_VERSION >= 0x040600)
        if (lyxrc.use_system_theme_icons) {
@@ -628,18 +667,24 @@ QIcon getIcon(FuncRequest const & f, bool unknown)
        }
 #endif
 
-       QString icon = iconName(f, unknown);
-       if (icon.isEmpty())
+       IconInfo icondata = iconInfo(f, unknown, rtl);
+       if (icondata.filepath.isEmpty())
                return QIcon();
 
        //LYXERR(Debug::GUI, "Found icon: " << icon);
        QPixmap pixmap = QPixmap();
-       if (!getPixmap(pixmap,icon)) {
-               LYXERR0("Cannot load icon " << icon << " please verify resource system!");
+       if (!pixmap.load(icondata.filepath)) {
+               LYXERR0("Cannot load icon " << icondata.filepath << ".");
                return QIcon();
        }
 
-       return QIcon(pixmap);
+       if (icondata.invert)
+               pixmap = prepareForDarkMode(pixmap);
+
+       if (icondata.swap)
+               return QIcon(pixmap.transformed(QTransform().scale(-1, 1)));
+       else
+               return QIcon(pixmap);
 }
 
 
@@ -670,12 +715,12 @@ public:
                : QTranslator(parent)
        {}
 
-       virtual QString translate(const char * /* context */,
+       QString translate(const char * /* context */,
                const char *sourceText,
 #if QT_VERSION >= 0x050000
-               const char * /* disambiguation */ = 0, int /* n */ = -1) const
+               const char * /* disambiguation */ = nullptr, int /* n */ = -1) const override
 #else
-               const char * /*comment*/ = 0) const
+               const char * /*comment*/ = 0) const override
 #endif
        {
                // Here we declare the strings that need to be translated from Qt own GUI
@@ -707,7 +752,7 @@ public:
 //
 ////////////////////////////////////////////////////////////////////////
 
-#ifdef Q_OS_MAC
+#if defined(Q_OS_MAC) && (QT_VERSION < 0x060000)
 // QMacPasteboardMimeGraphics can only be compiled on Mac.
 
 class QMacPasteboardMimeGraphics : public QMacPasteboardMime
@@ -767,7 +812,7 @@ public:
 ////////////////////////////////////////////////////////////////////////
 // Windows specific stuff goes here...
 
-#if (QT_VERSION < 0x050000) || (QT_VERSION >= 0x050400)
+#if (QT_VERSION < 0x050000) || (QT_VERSION >= 0x050400 && QT_VERSION < 0x060000)
 #if defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)
 // QWindowsMimeMetafile can only be compiled on Windows.
 
@@ -793,13 +838,13 @@ public:
        QWindowsMimeMetafile() {}
 
        bool canConvertFromMime(FORMATETC const & /*formatetc*/,
-               QMimeData const * /*mimedata*/) const
+               QMimeData const * /*mimedata*/) const override
        {
                return false;
        }
 
        bool canConvertToMime(QString const & mimetype,
-               IDataObject * pDataObj) const
+               IDataObject * pDataObj) const override
        {
                if (mimetype != emfMimeType() && mimetype != wmfMimeType())
                        return false;
@@ -808,13 +853,13 @@ public:
        }
 
        bool convertFromMime(FORMATETC const & /*formatetc*/,
-               const QMimeData * /*mimedata*/, STGMEDIUM * /*pmedium*/) const
+               const QMimeData * /*mimedata*/, STGMEDIUM * /*pmedium*/) const override
        {
                return false;
        }
 
        QVariant convertToMime(QString const & mimetype, IDataObject * pDataObj,
-               QVariant::Type /*preferredType*/) const
+               QVariant::Type /*preferredType*/) const override
        {
                QByteArray data;
                if (!canConvertToMime(mimetype, pDataObj))
@@ -845,7 +890,7 @@ public:
 
 
        QVector<FORMATETC> formatsForMime(QString const & mimetype,
-               QMimeData const * /*mimedata*/) const
+               QMimeData const * /*mimedata*/) const override
        {
                QVector<FORMATETC> formats;
                if (mimetype == emfMimeType() || mimetype == wmfMimeType())
@@ -853,7 +898,7 @@ public:
                return formats;
        }
 
-       QString mimeForFormat(FORMATETC const & formatetc) const
+       QString mimeForFormat(FORMATETC const & formatetc) const override
        {
                switch (formatetc.cfFormat) {
                case CF_ENHMETAFILE:
@@ -893,7 +938,7 @@ public:
        bool started() const {
                return started_;
        }
-       bool eventFilter(QObject *obj, QEvent *event) {
+       bool eventFilter(QObject *obj, QEvent *event) override {
                LYXERR(Debug::ACTION, "Event Type: " << event->type());
                switch (event->type()) {
                case QEvent::Show:
@@ -927,10 +972,13 @@ public:
 
 struct GuiApplication::Private
 {
-       Private(): language_model_(0), meta_fake_bit(NoModifier),
-                  global_menubar_(0)
+       Private(): language_model_(nullptr), meta_fake_bit(NoModifier),
+               global_menubar_(nullptr)
+       #if (QT_VERSION >= QT_VERSION_CHECK(5, 1, 0))
+               , last_state_(Qt::ApplicationInactive)
+       #endif
        {
-       #if (QT_VERSION < 0x050000) || (QT_VERSION >= 0x050400)
+       #if (QT_VERSION < 0x050000) || (QT_VERSION >= 0x050400 && QT_VERSION < 0x060000)
        #if defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)
                /// WMF Mime handler for Windows clipboard.
                wmf_mime_ = new QWindowsMimeMetafile;
@@ -995,13 +1043,17 @@ struct GuiApplication::Private
 
        /// Only used on mac.
        QMenuBar * global_menubar_;
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 1, 0))
+       /// Holds previous application state on Mac
+       Qt::ApplicationState last_state_;
+#endif
 
-#ifdef Q_OS_MAC
+#if defined(Q_OS_MAC) && (QT_VERSION < 0x060000)
        /// Linkback mime handler for MacOSX.
        QMacPasteboardMimeGraphics mac_pasteboard_mime_;
 #endif
 
-#if (QT_VERSION < 0x050000) || (QT_VERSION >= 0x050400)
+#if (QT_VERSION < 0x050000) || (QT_VERSION >= 0x050400 && QT_VERSION < 0x060000)
 #if defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)
        /// WMF Mime handler for Windows clipboard.
        QWindowsMimeMetafile * wmf_mime_;
@@ -1025,18 +1077,26 @@ GuiApplication::~GuiApplication()
 
 
 GuiApplication::GuiApplication(int & argc, char ** argv)
-       : QApplication(argc, argv), current_view_(0),
+       : QApplication(argc, argv), current_view_(nullptr),
          d(new GuiApplication::Private)
 {
        QString app_name = "LyX";
        QCoreApplication::setOrganizationName(app_name);
        QCoreApplication::setOrganizationDomain("lyx.org");
        QCoreApplication::setApplicationName(lyx_package);
-#if QT_VERSION >= 0x050000
+#if QT_VERSION >= 0x050000 && QT_VERSION < 0x060000
        QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
 #endif
 
+#if QT_VERSION >= 0x050700
+       setDesktopFileName(lyx_package);
+#endif
+
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
+       QRandomGenerator(QDateTime::currentDateTime().toSecsSinceEpoch());
+#else
        qsrand(QDateTime::currentDateTime().toTime_t());
+#endif
 
        // Install LyX translator for missing Qt translations
        installTranslator(&d->gui_trans_);
@@ -1061,6 +1121,12 @@ GuiApplication::GuiApplication(int & argc, char ** argv)
        setQuitOnLastWindowClosed(false);
        ///
        setupApplescript();
+       appleCleanupEditMenu();
+       appleCleanupViewMenu();
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 1, 0))
+       connect(this, SIGNAL(applicationStateChanged(Qt::ApplicationState)),
+                       this, SLOT(onApplicationStateChanged(Qt::ApplicationState)));
+#endif
 #endif
 
 #if defined(Q_WS_X11) || defined(QPA_XCB)
@@ -1116,6 +1182,29 @@ GuiApplication * theGuiApp()
 }
 
 
+#if QT_VERSION < 0x050000
+// Emulate platformName() for Qt4
+
+// FIXME: when ditching this method, remove all tests
+//     platformName() == "qt4x11"
+// in the code
+QString GuiApplication::platformName() const
+{
+# if defined(Q_WS_X11)
+       // Note that this one does not really exist
+       return "qt4x11";
+# elif defined(Q_OS_MAC)
+       return "cocoa";
+# elif defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)
+       return "windows";
+# else
+       LYXERR0("Unknown platform!");
+       return "unknown";
+# endif
+}
+#endif
+
+
 double GuiApplication::pixelRatio() const
 {
 #if QT_VERSION >= 0x050000
@@ -1135,7 +1224,7 @@ void GuiApplication::clearSession()
 
 docstring Application::iconName(FuncRequest const & f, bool unknown)
 {
-       return qstring_to_ucs4(lyx::frontend::iconName(f, unknown));
+       return qstring_to_ucs4(lyx::frontend::iconInfo(f, unknown, false).filepath);
 }
 
 
@@ -1149,8 +1238,8 @@ FuncStatus GuiApplication::getStatus(FuncRequest const & cmd) const
 {
        FuncStatus status;
 
-       BufferView * bv = 0;
-       BufferView * doc_bv = 0;
+       BufferView * bv = nullptr;
+       BufferView * doc_bv = nullptr;
 
        if (cmd.action() == LFUN_NOACTION) {
                status.message(from_utf8(N_("Nothing to do")));
@@ -1292,24 +1381,6 @@ bool GuiApplication::getStatus(FuncRequest const & cmd, FuncStatus & flag) const
                break;
        }
 
-       case LFUN_BIDI: {
-               string const dir = cmd.getArg(0);
-               string const lfun = cmd.getLongArg(1);
-               BufferView const * bv =
-                       current_view_ ? current_view_->currentBufferView() : nullptr;
-               bool rtl = bv ? bv->cursor().innerParagraph().isRTL(bv->buffer().params())
-                             : layoutDirection() == Qt::RightToLeft;
-               if (((rtl && dir != "rtl") || (!rtl && dir != "ltr"))) {
-                       flag.setUnknown(true);
-                       flag.setEnabled(false);
-               } else {
-                       FuncRequest func(lyxaction.lookupFunc(lfun));
-                       func.setOrigin(cmd.origin());
-                       flag = getStatus(func);
-               }
-               break;
-       }
-
        case LFUN_IF_RELATIVES: {
                string const lfun = to_utf8(cmd.argument());
                BufferView const * bv =
@@ -1328,17 +1399,25 @@ bool GuiApplication::getStatus(FuncRequest const & cmd, FuncStatus & flag) const
        case LFUN_REPEAT:
        case LFUN_PREFERENCES_SAVE:
        case LFUN_BUFFER_SAVE_AS_DEFAULT:
-       case LFUN_DEBUG_LEVEL_SET:
                // these are handled in our dispatch()
                break;
 
+       case LFUN_DEBUG_LEVEL_SET: {
+               string bad = Debug::badValue(to_utf8(cmd.argument()));
+               enable = bad.empty();
+               if (!bad.empty())
+                       flag.message(bformat(_("Bad debug value `%1$s'."), from_utf8(bad)));
+               break;
+       }
+
        case LFUN_WINDOW_CLOSE:
-               enable = d->views_.size() > 0;
+               enable = !d->views_.empty();
                break;
 
        case LFUN_BUFFER_NEW:
        case LFUN_BUFFER_NEW_TEMPLATE:
        case LFUN_FILE_OPEN:
+       case LFUN_LYXFILES_OPEN:
        case LFUN_HELP_OPEN:
        case LFUN_SCREEN_FONT_UPDATE:
        case LFUN_SET_COLOR:
@@ -1436,7 +1515,7 @@ DispatchResult const & GuiApplication::dispatch(FuncRequest const & cmd)
 {
        DispatchResult dr;
 
-       Buffer * buffer = 0;
+       Buffer * buffer = nullptr;
        if (cmd.view_origin() && current_view_ != cmd.view_origin()) {
                //setCurrentView(cmd.view_origin); //does not work
                dr.setError(true);
@@ -1451,13 +1530,16 @@ DispatchResult const & GuiApplication::dispatch(FuncRequest const & cmd)
 
        dr.screenUpdate(Update::FitCursor);
        {
-               // This handles undo groups automagically
+               // All the code is kept inside the undo group because
+               // updateBuffer can create undo actions (see #11292)
                UndoGroupHelper ugh(buffer);
                dispatch(cmd, dr);
-               // redraw the screen at the end (first of the two drawing steps).
-               // This is done unless explicitly requested otherwise.
-               // This code is kept inside the undo group because updateBuffer
-               // can create undo actions (see #11292)
+               if (dr.screenUpdate() & Update::ForceAll) {
+                       for (Buffer const * b : theBufferList())
+                               b->changed(true);
+                       dr.screenUpdate(dr.screenUpdate() & ~Update::ForceAll);
+               }
+
                updateCurrentView(cmd, dr);
        }
 
@@ -1473,7 +1555,7 @@ void GuiApplication::updateCurrentView(FuncRequest const & cmd, DispatchResult &
 
        BufferView * bv = current_view_->currentBufferView();
        if (bv) {
-               if (dr.needBufferUpdate()) {
+               if (dr.needBufferUpdate() || bv->buffer().needUpdate()) {
                        bv->cursor().clearBufferUpdate();
                        bv->buffer().updateBuffer();
                }
@@ -1526,8 +1608,8 @@ void GuiApplication::gotoBookmark(unsigned int idx, bool openFile,
 
        // if the current buffer is not that one, switch to it.
        BufferView * doc_bv = current_view_ ?
-               current_view_->documentBufferView() : 0;
-       Cursor const * old = doc_bv ? &doc_bv->cursor() : 0;
+               current_view_->documentBufferView() : nullptr;
+       Cursor const * old = doc_bv ? &doc_bv->cursor() : nullptr;
        if (!doc_bv || doc_bv->buffer().fileName() != tmp.filename) {
                if (switchToBuffer) {
                        dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
@@ -1570,8 +1652,10 @@ void GuiApplication::gotoBookmark(unsigned int idx, bool openFile,
 void GuiApplication::reconfigure(string const & option)
 {
        // emit message signal.
-       if (current_view_)
+       if (current_view_) {
                current_view_->message(_("Running configure..."));
+               current_view_->setCursor(Qt::WaitCursor);
+       }
 
        // Run configure in user lyx directory
        string const lock_file = package().getConfigureLockName();
@@ -1585,6 +1669,9 @@ void GuiApplication::reconfigure(string const & option)
        LaTeXPackages::getAvailable();
        fileUnlock(fd, lock_file.c_str());
 
+       if (current_view_)
+               current_view_->unsetCursor();
+
        if (ret)
                Alert::information(_("System reconfiguration failed"),
                           _("The system reconfiguration has failed.\n"
@@ -1604,7 +1691,7 @@ void GuiApplication::validateCurrentView()
                // currently at least one view exists but no view has the focus.
                // choose the last view to make it current.
                // a view without any open document is preferred.
-               GuiView * candidate = 0;
+               GuiView * candidate = nullptr;
                QHash<int, GuiView *>::const_iterator it = d->views_.begin();
                QHash<int, GuiView *>::const_iterator end = d->views_.end();
                for (; it != end; ++it) {
@@ -1657,9 +1744,10 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                break;
 
        case LFUN_WINDOW_CLOSE:
+               // FIXME: this is done also in GuiView::closeBuffer()!
                // update bookmark pit of the current buffer before window close
-               for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
-                       gotoBookmark(i+1, false, false);
+               for (size_t i = 1; i < theSession().bookmarks().size(); ++i)
+                       gotoBookmark(i, false, false);
                // clear the last opened list, because
                // maybe this will end the session
                theSession().lastOpened().clear();
@@ -1691,7 +1779,7 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
        case LFUN_BUFFER_NEW:
                validateCurrentView();
                if (!current_view_
-                  || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) {
+                  || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != nullptr)) {
                        createView(QString(), false); // keep hidden
                        current_view_->newDocument(to_utf8(cmd.argument()));
                        current_view_->show();
@@ -1706,7 +1794,7 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                string const temp = cmd.getArg(1);
                validateCurrentView();
                if (!current_view_
-                  || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) {
+                  || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != nullptr)) {
                        createView();
                        current_view_->newDocument(file, temp, true);
                        if (!current_view_->documentBufferView())
@@ -1722,18 +1810,16 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                //   current_view_ is not null.
                validateCurrentView();
                // FIXME: create a new method shared with LFUN_HELP_OPEN.
-               string const fname = to_utf8(cmd.argument());
+               string const fname = trim(to_utf8(cmd.argument()), "\"");
                bool const is_open = FileName::isAbsolute(fname)
                        && theBufferList().getBuffer(FileName(fname));
                if (!current_view_
                    || (!lyxrc.open_buffers_in_tabs
-                       && current_view_->documentBufferView() != 0
+                       && current_view_->documentBufferView() != nullptr
                        && !is_open)) {
                        // We want the ui session to be saved per document and not per
                        // window number. The filename crc is a good enough identifier.
-                       boost::crc_32_type crc;
-                       crc = for_each(fname.begin(), fname.end(), crc);
-                       createView(crc.checksum());
+                       createView(support::checksum(fname));
                        current_view_->openDocument(fname);
                        if (!current_view_->documentBufferView())
                                current_view_->close();
@@ -1755,7 +1841,7 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
 
        case LFUN_HELP_OPEN: {
                // FIXME: create a new method shared with LFUN_FILE_OPEN.
-               if (current_view_ == 0)
+               if (current_view_ == nullptr)
                        createView();
                string const arg = to_utf8(cmd.argument());
                if (arg.empty()) {
@@ -1779,13 +1865,33 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                break;
        }
 
+       case LFUN_LYXFILES_OPEN: {
+               // This is the actual reason for this method (#12106).
+               validateCurrentView();
+               if (!current_view_
+                  || (!lyxrc.open_buffers_in_tabs
+                      && current_view_->documentBufferView() != nullptr))
+                       createView();
+               string arg = to_utf8(cmd.argument());
+               if (arg.empty())
+                       // set default
+                       arg = "templates";
+               if (arg != "templates" && arg != "examples") {
+                       current_view_->message(_("Wrong argument. Must be 'examples' or 'templates'."));
+                       break;
+               }
+               lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "lyxfiles " + arg));
+               break;
+       }
+
        case LFUN_SET_COLOR: {
                string const lyx_name = cmd.getArg(0);
-               string const x11_name = cmd.getArg(1);
+               string x11_name = cmd.getArg(1);
+               string x11_darkname = cmd.getArg(2);
                if (lyx_name.empty() || x11_name.empty()) {
                        if (current_view_)
                                current_view_->message(
-                                       _("Syntax: set-color <lyx_name> <x11_name>"));
+                                       _("Syntax: set-color <lyx_name> <x11_name> <x11_darkname>"));
                        break;
                }
 
@@ -1798,7 +1904,11 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        graphics::GCache::get().changeDisplay(true);
 #endif
 
-               if (!lcolor.setColor(lyx_name, x11_name)) {
+               if (x11_darkname.empty() && colorCache().isDarkMode()) {
+                       x11_darkname = x11_name;
+                       x11_name.clear();
+               }
+               if (!lcolor.setColor(lyx_name, x11_name, x11_darkname)) {
                        if (current_view_)
                                current_view_->message(
                                        bformat(_("Set-color \"%1$s\" failed "
@@ -1833,7 +1943,7 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                actOnUpdatedPrefs(lyxrc_orig, lyxrc);
 
                // If the request comes from the minibuffer, then we can't reset
-               // the GUI, since that would destory the minibuffer itself and
+               // the GUI, since that would destroy the minibuffer itself and
                // cause a crash, since we are currently in one of the methods of
                // GuiCommandBuffer. See bug #8540.
                if (cmd.origin() != FuncRequest::COMMANDBUFFER)
@@ -1898,7 +2008,6 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
                break;
 
-       // --- syntax commands ----------------------------
        case LFUN_REPEAT: {
                // repeat command
                string countstr;
@@ -1924,7 +2033,7 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                string arg = argument;
                // FIXME: this LFUN should also work without any view.
                Buffer * buffer = (current_view_ && current_view_->documentBufferView())
-                                 ? &(current_view_->documentBufferView()->buffer()) : 0;
+                                 ? &(current_view_->documentBufferView()->buffer()) : nullptr;
                // This handles undo groups automagically
                UndoGroupHelper ugh(buffer);
                while (!arg.empty()) {
@@ -1954,7 +2063,7 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        // all of the buffers might be locally hidden. That is, there is no
                        // active buffer.
                        if (!view || !view->currentBufferView())
-                               activeBuffers[view] = 0;
+                               activeBuffers[view] = nullptr;
                        else
                                activeBuffers[view] = &view->currentBufferView()->buffer();
 
@@ -1979,7 +2088,7 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
 
                GuiView * const homeView = currentView();
                Buffer * b = theBufferList().first();
-               Buffer * nextBuf = 0;
+               Buffer * nextBuf = nullptr;
                int numProcessed = 0;
                while (true) {
                        if (b != last)
@@ -2050,18 +2159,6 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                break;
        }
 
-       case LFUN_BIDI: {
-               string const lfun = cmd.getLongArg(1);
-               FuncRequest func(lyxaction.lookupFunc(cmd.getLongArg(1)));
-               func.setOrigin(cmd.origin());
-               FuncStatus const stat = getStatus(func);
-               if (stat.enabled()) {
-                       dispatch(func);
-                       break;
-               }
-               break;
-       }
-
        case LFUN_IF_RELATIVES: {
                string const lfun = to_utf8(cmd.argument());
                FuncRequest func(lyxaction.lookupFunc(lfun));
@@ -2094,9 +2191,8 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                DocumentClassConstPtr olddc = defaults.params().documentClassPtr();
                int const unknown_tokens = defaults.readHeader(lex);
                DocumentClassConstPtr newdc = defaults.params().documentClassPtr();
-               ErrorList el;
                InsetText & theinset = static_cast<InsetText &>(defaults.inset());
-               cap::switchBetweenClasses(olddc, newdc, theinset, el);
+               cap::switchBetweenClasses(olddc, newdc, theinset);
 
                if (unknown_tokens != 0) {
                        lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
@@ -2123,6 +2219,7 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
 
        case LFUN_BOOKMARK_CLEAR:
                theSession().bookmarks().clear();
+               dr.screenUpdate(Update::Force);
                break;
 
        case LFUN_DEBUG_LEVEL_SET:
@@ -2142,7 +2239,7 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        // is not terminated when closing the last view.
                        // Create a new one to be able to dispatch the
                        // LFUN_DIALOG_SHOW to this view.
-                       if (current_view_ == 0)
+                       if (current_view_ == nullptr)
                                createView();
                }
        }
@@ -2155,6 +2252,11 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                break;
        }
 
+       if (current_view_ && current_view_->isFullScreen()) {
+               if (current_view_->menuBar()->isVisible() && lyxrc.full_screen_menubar)
+                       current_view_->menuBar()->hide();
+       }
+
        if (cmd.origin() == FuncRequest::LYXSERVER)
                updateCurrentView(cmd, dr);
 }
@@ -2237,7 +2339,8 @@ bool GuiApplication::queryKeySym(KeySymbol const & keysym,
                // seq has been reset at this point
                func = seq.addkey(keysym, NoModifier);
 
-       LYXERR(Debug::KEY, " Key (queried) [action=" << func.action() << "]["
+       LYXERR(Debug::KEY, " Key (queried) [action="
+              << lyxaction.getActionName(func.action()) << "]["
               << seq.print(KeySequence::Portable) << ']');
        return func.action() != LFUN_UNKNOWN_ACTION;
 }
@@ -2264,7 +2367,8 @@ void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state)
        d->cancel_meta_seq.reset();
 
        FuncRequest func = d->cancel_meta_seq.addkey(keysym, state);
-       LYXERR(Debug::KEY, "action first set to [" << func.action() << ']');
+       LYXERR(Debug::KEY, "action first set to ["
+              << lyxaction.getActionName(func.action()) << ']');
 
        // When not cancel or meta-fake, do the normal lookup.
        // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
@@ -2272,18 +2376,20 @@ void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state)
        if ((func.action() != LFUN_CANCEL) && (func.action() != LFUN_META_PREFIX)) {
                // remove Caps Lock and Mod2 as a modifiers
                func = d->keyseq.addkey(keysym, (state | d->meta_fake_bit));
-               LYXERR(Debug::KEY, "action now set to [" << func.action() << ']');
+               LYXERR(Debug::KEY, "action now set to ["
+                      << lyxaction.getActionName(func.action()) << ']');
        }
 
-       // Dont remove this unless you know what you are doing.
+       // Don't remove this unless you know what you are doing.
        d->meta_fake_bit = NoModifier;
 
        // Can this happen now ?
        if (func.action() == LFUN_NOACTION)
                func = FuncRequest(LFUN_COMMAND_PREFIX);
 
-       LYXERR(Debug::KEY, " Key [action=" << func.action() << "]["
-               << d->keyseq.print(KeySequence::Portable) << ']');
+       LYXERR(Debug::KEY, " Key [action="
+              << lyxaction.getActionName(func.action()) << "]["
+              << d->keyseq.print(KeySequence::Portable) << ']');
 
        // already here we know if it any point in going further
        // why not return already here if action == -1 and
@@ -2300,7 +2406,7 @@ void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state)
                // If addkey looked up a command and did not find further commands then
                // seq has been reset at this point
                func = d->keyseq.addkey(keysym, NoModifier);
-               LYXERR(Debug::KEY, "Action now " << func.action());
+               LYXERR(Debug::KEY, "Action now " << lyxaction.getActionName(func.action()));
        }
 
        if (func.action() == LFUN_UNKNOWN_ACTION) {
@@ -2417,7 +2523,7 @@ void GuiApplication::resetGui()
                return;
 
        if (d->global_menubar_)
-               d->menus_.fillMenuBar(d->global_menubar_, 0, false);
+               d->menus_.fillMenuBar(d->global_menubar_, nullptr, false);
 
        QHash<int, GuiView *>::iterator it;
        for (it = d->views_.begin(); it != d->views_.end(); ++it) {
@@ -2431,6 +2537,16 @@ void GuiApplication::resetGui()
 }
 
 
+bool GuiApplication::rtlContext() const
+{
+       if (current_view_ && current_view_->currentBufferView()) {
+               BufferView const * bv = current_view_->currentBufferView();
+               return bv->cursor().innerParagraph().isRTL(bv->buffer().params());
+       } else
+               return layoutDirection() == Qt::RightToLeft;
+}
+
+
 void GuiApplication::createView(int view_id)
 {
        createView(QString(), true, view_id);
@@ -2440,7 +2556,7 @@ void GuiApplication::createView(int view_id)
 void GuiApplication::createView(QString const & geometry_arg, bool autoShow,
        int view_id)
 {
-       // release the keyboard which might have been grabed by the global
+       // release the keyboard which might have been grabbed by the global
        // menubar on Mac to catch shortcuts even without any GuiView.
        if (d->global_menubar_)
                d->global_menubar_->releaseKeyboard();
@@ -2467,6 +2583,7 @@ void GuiApplication::createView(QString const & geometry_arg, bool autoShow,
                int x, y;
                int w, h;
                QChar sx, sy;
+#if QT_VERSION < 0x060000
                QRegExp re( "[=]*(?:([0-9]+)[xX]([0-9]+)){0,1}[ ]*(?:([+-][0-9]*)){0,1}(?:([+-][0-9]*)){0,1}" );
                re.indexIn(geometry_arg);
                w = re.cap(1).toInt();
@@ -2475,6 +2592,16 @@ void GuiApplication::createView(QString const & geometry_arg, bool autoShow,
                y = re.cap(4).toInt();
                sx = re.cap(3).isEmpty() ? '+' : re.cap(3).at(0);
                sy = re.cap(4).isEmpty() ? '+' : re.cap(4).at(0);
+#else
+               QRegularExpression re( "[=]*(?:([0-9]+)[xX]([0-9]+)){0,1}[ ]*(?:([+-][0-9]*)){0,1}(?:([+-][0-9]*)){0,1}" );
+               QRegularExpressionMatch match = re.match(geometry_arg);
+               w = match.captured(1).toInt();
+               h = match.captured(2).toInt();
+               x = match.captured(3).toInt();
+               y = match.captured(4).toInt();
+               sx = match.captured(3).isEmpty() ? '+' : match.captured(3).at(0);
+               sy = match.captured(4).isEmpty() ? '+' : match.captured(4).at(0);
+#endif
                // Set initial geometry such that we can get the frame size.
                view->setGeometry(x, y, w, h);
                int framewidth = view->geometry().x() - view->x();
@@ -2482,7 +2609,11 @@ void GuiApplication::createView(QString const & geometry_arg, bool autoShow,
                // Negative displacements must be interpreted as distances
                // from the right or bottom screen borders.
                if (sx == '-' || sy == '-') {
+#if QT_VERSION < 0x060000
                        QRect rec = QApplication::desktop()->screenGeometry();
+#else
+                       QRect rec = QGuiApplication::primaryScreen()->geometry();
+#endif
                        if (sx == '-')
                                x += rec.width() - w - framewidth;
                        if (sy == '-')
@@ -2594,7 +2725,11 @@ void GuiApplication::setGuiLanguage()
        // opposite. Therefore, long name should be used without truncation.
        // c.f. http://doc.trolltech.com/4.1/qtranslator.html#load
        if (!d->qt_trans_.load(language_name,
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+                       QLibraryInfo::path(QLibraryInfo::TranslationsPath))) {
+#else
                        QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
+#endif
                LYXERR(Debug::LOCALE, "Could not find Qt translations for locale "
                        << language_name);
        } else {
@@ -2692,18 +2827,16 @@ void GuiApplication::restoreGuiSession()
        // do not add to the lastfile list since these files are restored from
        // last session, and should be already there (regular files), or should
        // not be added at all (help files).
-       for (size_t i = 0; i < lastopened.size(); ++i) {
-               FileName const & file_name = lastopened[i].file_name;
+       for (auto const & last : lastopened) {
+               FileName const & file_name = last.file_name;
                if (!current_view_ || (!lyxrc.open_buffers_in_tabs
-                         && current_view_->documentBufferView() != 0)) {
-                       boost::crc_32_type crc;
+                         && current_view_->documentBufferView() != nullptr)) {
                        string const & fname = file_name.absFileName();
-                       crc = for_each(fname.begin(), fname.end(), crc);
-                       createView(crc.checksum());
+                       createView(support::checksum(fname));
                }
                current_view_->loadDocument(file_name, false);
 
-               if (lastopened[i].active)
+               if (last.active)
                        active_file = file_name;
        }
 
@@ -2867,6 +3000,11 @@ bool GuiApplication::notify(QObject * receiver, QEvent * event)
        return false;
 }
 
+bool GuiApplication::isInDarkMode()
+{
+       return colorCache().isDarkMode();
+}
+
 
 bool GuiApplication::getRgbColor(ColorCode col, RGBColor & rgbcol)
 {
@@ -2886,7 +3024,7 @@ bool GuiApplication::getRgbColor(ColorCode col, RGBColor & rgbcol)
 
 bool Application::getRgbColorUncached(ColorCode col, RGBColor & rgbcol)
 {
-       QColor const qcol(lcolor.getX11Name(col).c_str());
+       QColor const qcol(lcolor.getX11HexName(col).c_str());
        if (!qcol.isValid()) {
                rgbcol.r = 0;
                rgbcol.g = 0;
@@ -2931,7 +3069,7 @@ void GuiApplication::commitData(QSessionManager & sm)
        /** The implementation is required to avoid an application exit
         ** when session state save is triggered by session manager.
         ** The default implementation sends a close event to all
-        ** visible top level widgets when session managment allows
+        ** visible top level widgets when session management allows
         ** interaction.
         ** We are changing that to check the state of each buffer in all
         ** views and ask the users what to do if buffers are dirty.
@@ -2961,7 +3099,7 @@ void GuiApplication::unregisterView(GuiView * gv)
        if(d->views_.contains(gv->id()) && d->views_.value(gv->id()) == gv) {
                d->views_.remove(gv->id());
                if (current_view_ == gv)
-                       current_view_ = 0;
+                       current_view_ = nullptr;
        }
 }
 
@@ -3018,7 +3156,7 @@ void GuiApplication::hideDialogs(string const & name, Inset * inset) const
 
 Buffer const * GuiApplication::updateInset(Inset const * inset) const
 {
-       Buffer const * buf = 0;
+       Buffer const * buf = nullptr;
        QHash<int, GuiView *>::const_iterator end = d->views_.end();
        for (QHash<int, GuiView *>::iterator it = d->views_.begin(); it != end; ++it) {
                if (Buffer const * ptr = (*it)->updateInset(inset))
@@ -3031,7 +3169,7 @@ Buffer const * GuiApplication::updateInset(Inset const * inset) const
 bool GuiApplication::searchMenu(FuncRequest const & func,
        docstring_list & names) const
 {
-       BufferView * bv = 0;
+       BufferView * bv = nullptr;
        if (current_view_)
                bv = current_view_->currentBufferView();
        return d->menus_.searchMenu(func, names, bv);
@@ -3050,7 +3188,7 @@ static QStringList uifiles;
 static QStringList toolbar_uifiles;
 
 
-GuiApplication::ReturnValues GuiApplication::readUIFile(FileName ui_path)
+GuiApplication::ReturnValues GuiApplication::readUIFile(FileName const & ui_path)
 {
        enum {
                ui_menuset = 1,
@@ -3252,6 +3390,36 @@ void GuiApplication::onLastWindowClosed()
 }
 
 
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 1, 0))
+void GuiApplication::onApplicationStateChanged(Qt::ApplicationState state)
+{
+       std::string name = "unknown";
+       switch (state) {
+       case Qt::ApplicationSuspended:
+               name = "ApplicationSuspended";
+               break;
+       case Qt::ApplicationHidden:
+               name = "ApplicationHidden";
+               break;
+       case Qt::ApplicationInactive:
+               name = "ApplicationInactive";
+               break;
+       case Qt::ApplicationActive:
+               name = "ApplicationActive";
+               /// The Dock icon click produces 2 sequential QEvent::ApplicationStateChangeEvent events.
+               /// cmd+tab only one QEvent::ApplicationStateChangeEvent event
+               if (d->views_.empty() && d->last_state_ == state) {
+                       LYXERR(Debug::GUI, "Open new window...");
+                       createView();
+               }
+               break;
+       }
+       LYXERR(Debug::GUI, "onApplicationStateChanged..." << name);
+       d->last_state_ = state;
+}
+#endif
+
+
 void GuiApplication::startLongOperation() {
        d->key_checker_.start();
 }
@@ -3352,26 +3520,24 @@ bool GuiApplication::nativeEventFilter(const QByteArray & eventType,
                                // not doing that, maybe because of our
                                // "persistent selection" implementation
                                // (see comments in GuiSelection.cpp).
-
-                               // It is expected that every X11 event is 32 bytes long,
-                               // even if not all 32 bytes are needed. See:
+                               // It is expected that every X11 event is
+                               // 32 bytes long, even if not all 32 bytes are
+                               // needed. See:
                                // https://www.x.org/releases/current/doc/man/man3/xcb_send_event.3.xhtml
-                               // TODO switch to Q_DECLARE_XCB_EVENT(event, xcb_selection_notify_event_t)
-                               //      once we require qt >= 5.6.3 or just copy the macro def.
-                               xcb_selection_notify_event_t *nev = (xcb_selection_notify_event_t*) calloc(32, 1);
-
-                               nev->response_type = XCB_SELECTION_NOTIFY;
-                               nev->requestor = srev->requestor;
-                               nev->selection = srev->selection;
-                               nev->target = srev->target;
-                               nev->property = XCB_NONE;
-                               nev->time = XCB_CURRENT_TIME;
+                               struct alignas(32) padded_event
+                                       : xcb_selection_notify_event_t {};
+                               padded_event nev = {};
+                               nev.response_type = XCB_SELECTION_NOTIFY;
+                               nev.requestor = srev->requestor;
+                               nev.selection = srev->selection;
+                               nev.target = srev->target;
+                               nev.property = XCB_NONE;
+                               nev.time = XCB_CURRENT_TIME;
                                xcb_connection_t * con = QX11Info::connection();
                                xcb_send_event(con, 0, srev->requestor,
                                        XCB_EVENT_MASK_NO_EVENT,
-                                       reinterpret_cast<char const *>(nev));
+                                       reinterpret_cast<char const *>(&nev));
                                xcb_flush(con);
-                               free(nev);
 #endif
                                return true;
                        }