]> git.lyx.org Git - features.git/blobdiff - src/frontends/qt/GuiApplication.cpp
#10571 improved handling of WM's signal when switching from or to full-screen window
[features.git] / src / frontends / qt / GuiApplication.cpp
index 9c15d7a770dcc157683745d64f20a058fff5d0b2..76cf364ab2c25a501faeb95682f6d861c978ca6d 100644 (file)
 #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"
@@ -82,6 +82,7 @@
 #endif
 
 #include <queue>
+#include <tuple>
 
 #include <QByteArray>
 #include <QDateTime>
@@ -156,16 +157,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[])
@@ -486,100 +477,80 @@ QString themeIconName(QString const & action)
 }
 
 
-QString iconName(FuncRequest const & f, bool unknown)
+// the returned bool is true if the icon needs to be flipped
+pair<QString,bool> iconName(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;
+       QStringList names;
+       QString lfunname = toqstr(lyxaction.getActionName(f.action()));
 
-               if (!f.argument().empty()) {
-                       name1 = name2 + ' ' + toqstr(f.argument());
+       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");
+               names << name;
+
+               // 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 \""
+       // 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())
+                                       return make_pair(toqstr(fname.absFileName()),
+                                                        rtl && suffix.isEmpty());
+                       }
+
+       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();
-}
-
-
-bool getPixmap(QPixmap & pixmap, QString const & path)
-{
-       return pixmap.load(path);
+       return make_pair(QString(), false);
 }
 
 
@@ -590,28 +561,19 @@ 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))
                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 +590,23 @@ QIcon getIcon(FuncRequest const & f, bool unknown)
        }
 #endif
 
-       QString icon = iconName(f, unknown);
+       QString icon;
+       bool flip;
+       tie(icon, flip) = iconName(f, unknown, rtl);
        if (icon.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(icon)) {
+               LYXERR0("Cannot load icon " << icon << ".");
                return QIcon();
        }
 
-       return QIcon(pixmap);
+       if (flip)
+               return QIcon(pixmap.transformed(QTransform().scale(-1, 1)));
+       else
+               return QIcon(pixmap);
 }
 
 
@@ -928,7 +895,7 @@ public:
 struct GuiApplication::Private
 {
        Private(): language_model_(0), meta_fake_bit(NoModifier),
-                  global_menubar_(0)
+               global_menubar_(0)
        {
        #if (QT_VERSION < 0x050000) || (QT_VERSION >= 0x050400)
        #if defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)
@@ -1036,6 +1003,10 @@ GuiApplication::GuiApplication(int & argc, char ** argv)
        QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
 #endif
 
+#if QT_VERSION >= 0x050700
+       setDesktopFileName(lyx_package);
+#endif
+
        qsrand(QDateTime::currentDateTime().toTime_t());
 
        // Install LyX translator for missing Qt translations
@@ -1135,7 +1106,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::iconName(f, unknown, false).first);
 }
 
 
@@ -1292,24 +1263,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 =
@@ -1476,9 +1429,6 @@ void GuiApplication::updateCurrentView(FuncRequest const & cmd, DispatchResult &
                if (dr.needBufferUpdate()) {
                        bv->cursor().clearBufferUpdate();
                        bv->buffer().updateBuffer();
-               } else if (dr.needChangesUpdate()) {
-                       // updateBuffer() already updates the change-tracking presence flag
-                       bv->buffer().updateChangesPresent();
                }
                // BufferView::update() updates the ViewMetricsInfo and
                // also initializes the position cache for all insets in
@@ -1836,7 +1786,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)
@@ -1901,7 +1851,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;
@@ -2053,18 +2002,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));
@@ -2278,7 +2215,7 @@ void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state)
                LYXERR(Debug::KEY, "action now set to [" << 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 ?
@@ -2434,6 +2371,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);
@@ -2443,7 +2390,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();
@@ -2889,7 +2836,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;
@@ -2934,7 +2881,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.
@@ -3355,7 +3302,13 @@ bool GuiApplication::nativeEventFilter(const QByteArray & eventType,
                                // not doing that, maybe because of our
                                // "persistent selection" implementation
                                // (see comments in GuiSelection.cpp).
-                               xcb_selection_notify_event_t nev;
+                               // 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
+                               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;