Fix deprecation warning, to fix Werror build
[quassel.git] / src / uisupport / qssparser.cpp
index 1d8254e..057044c 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2005-2015 by the Quassel Project                        *
+ *   Copyright (C) 2005-2019 by the Quassel Project                        *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
  ***************************************************************************/
 
-#include <QApplication>
-
 #include "qssparser.h"
 
+#include <tuple>
+#include <utility>
+
+#include <QApplication>
+
 QssParser::QssParser()
 {
     _palette = QApplication::palette();
 
     // Init palette color roles
     _paletteColorRoles["alternate-base"] = QPalette::AlternateBase;
-    _paletteColorRoles["background"] = QPalette::Background;
+    _paletteColorRoles["background"] = QPalette::Window;
     _paletteColorRoles["base"] = QPalette::Base;
     _paletteColorRoles["bright-text"] = QPalette::BrightText;
     _paletteColorRoles["button"] = QPalette::Button;
     _paletteColorRoles["button-text"] = QPalette::ButtonText;
     _paletteColorRoles["dark"] = QPalette::Dark;
-    _paletteColorRoles["foreground"] = QPalette::Foreground;
+    _paletteColorRoles["foreground"] = QPalette::WindowText;
     _paletteColorRoles["highlight"] = QPalette::Highlight;
     _paletteColorRoles["highlighted-text"] = QPalette::HighlightedText;
     _paletteColorRoles["light"] = QPalette::Light;
@@ -49,24 +52,41 @@ QssParser::QssParser()
     _paletteColorRoles["window"] = QPalette::Window;
     _paletteColorRoles["window-text"] = QPalette::WindowText;
 
-    _uiStylePalette = QVector<QBrush>(UiStyle::NumRoles, QBrush());
-
-    _uiStyleColorRoles["marker-line"] = UiStyle::MarkerLine;
+    _uiStylePalette = QVector<QBrush>(static_cast<int>(UiStyle::ColorRole::NumRoles), QBrush());
+
+    _uiStyleColorRoles["marker-line"] = UiStyle::ColorRole::MarkerLine;
+    // Sender colors
+    _uiStyleColorRoles["sender-color-self"] = UiStyle::ColorRole::SenderColorSelf;
+    _uiStyleColorRoles["sender-color-00"] = UiStyle::ColorRole::SenderColor00;
+    _uiStyleColorRoles["sender-color-01"] = UiStyle::ColorRole::SenderColor01;
+    _uiStyleColorRoles["sender-color-02"] = UiStyle::ColorRole::SenderColor02;
+    _uiStyleColorRoles["sender-color-03"] = UiStyle::ColorRole::SenderColor03;
+    _uiStyleColorRoles["sender-color-04"] = UiStyle::ColorRole::SenderColor04;
+    _uiStyleColorRoles["sender-color-05"] = UiStyle::ColorRole::SenderColor05;
+    _uiStyleColorRoles["sender-color-06"] = UiStyle::ColorRole::SenderColor06;
+    _uiStyleColorRoles["sender-color-07"] = UiStyle::ColorRole::SenderColor07;
+    _uiStyleColorRoles["sender-color-08"] = UiStyle::ColorRole::SenderColor08;
+    _uiStyleColorRoles["sender-color-09"] = UiStyle::ColorRole::SenderColor09;
+    _uiStyleColorRoles["sender-color-0a"] = UiStyle::ColorRole::SenderColor0a;
+    _uiStyleColorRoles["sender-color-0b"] = UiStyle::ColorRole::SenderColor0b;
+    _uiStyleColorRoles["sender-color-0c"] = UiStyle::ColorRole::SenderColor0c;
+    _uiStyleColorRoles["sender-color-0d"] = UiStyle::ColorRole::SenderColor0d;
+    _uiStyleColorRoles["sender-color-0e"] = UiStyle::ColorRole::SenderColor0e;
+    _uiStyleColorRoles["sender-color-0f"] = UiStyle::ColorRole::SenderColor0f;
 }
 
-
-void QssParser::processStyleSheet(QString &ss)
+void QssParser::processStyleSheet(QString& ss)
 {
     if (ss.isEmpty())
         return;
 
     // Remove C-style comments /* */ or //
-    QRegExp commentRx("(//.*(\\n|$)|/\\*.*\\*/)");
+    static QRegExp commentRx(R"((//.*(\n|$)|/\*.*\*/))");
     commentRx.setMinimal(true);
     ss.remove(commentRx);
 
     // Palette definitions first, so we can apply roles later on
-    QRegExp paletterx("(Palette[^{]*)\\{([^}]+)\\}");
+    static const QRegExp paletterx("(Palette[^{]*)\\{([^}]+)\\}");
     int pos = 0;
     while ((pos = paletterx.indexIn(ss, pos)) >= 0) {
         parsePaletteBlock(paletterx.cap(1).trimmed(), paletterx.cap(2).trimmed());
@@ -74,10 +94,10 @@ void QssParser::processStyleSheet(QString &ss)
     }
 
     // Now we can parse the rest of our custom blocks
-    QRegExp blockrx("((?:ChatLine|ChatListItem|NickListItem)[^{]*)\\{([^}]+)\\}");
+    static const QRegExp blockrx("((?:ChatLine|ChatListItem|NickListItem)[^{]*)\\{([^}]+)\\}");
     pos = 0;
     while ((pos = blockrx.indexIn(ss, pos)) >= 0) {
-        //qDebug() << blockrx.cap(1) << blockrx.cap(2);
+        // qDebug() << blockrx.cap(1) << blockrx.cap(2);
         QString declaration = blockrx.cap(1).trimmed();
         QString contents = blockrx.cap(2).trimmed();
 
@@ -85,52 +105,51 @@ void QssParser::processStyleSheet(QString &ss)
             parseChatLineBlock(declaration, contents);
         else if (declaration.startsWith("ChatListItem") || declaration.startsWith("NickListItem"))
             parseListItemBlock(declaration, contents);
-        //else
+        // else
         // TODO: add moar here
 
         ss.remove(pos, blockrx.matchedLength());
     }
 }
 
-
 /******** Parse a whole block: declaration { contents } *******/
 
-void QssParser::parseChatLineBlock(const QString &decl, const QString &contents)
+void QssParser::parseChatLineBlock(const QString& decl, const QString& contents)
 {
-    quint64 fmtType = parseFormatType(decl);
-    if (fmtType == UiStyle::Invalid)
+    UiStyle::FormatType fmtType;
+    UiStyle::MessageLabel label;
+    std::tie(fmtType, label) = parseFormatType(decl);
+    if (fmtType == UiStyle::FormatType::Invalid)
         return;
 
-    _formats[fmtType].merge(parseFormat(contents));
+    _formats[fmtType | label].merge(parseFormat(contents));
 }
 
-
-void QssParser::parseListItemBlock(const QString &decl, const QString &contents)
+void QssParser::parseListItemBlock(const QString& decl, const QString& contents)
 {
-    quint32 fmtType = parseItemFormatType(decl);
-    if (fmtType == UiStyle::Invalid)
+    UiStyle::ItemFormatType fmtType = parseItemFormatType(decl);
+    if (fmtType == UiStyle::ItemFormatType::Invalid)
         return;
 
     _listItemFormats[fmtType].merge(parseFormat(contents));
 }
 
-
 // Palette { ... } specifies the application palette
 // ColorGroups can be specified like pseudo states, chaining is OR (contrary to normal CSS handling):
 //   Palette:inactive:disabled { ... } applies to both the Inactive and the Disabled state
-void QssParser::parsePaletteBlock(const QString &decl, const QString &contents)
+void QssParser::parsePaletteBlock(const QString& decl, const QString& contents)
 {
     QList<QPalette::ColorGroup> colorGroups;
 
     // Check if we want to apply this palette definition for particular ColorGroups
-    QRegExp rx("Palette((:(normal|active|inactive|disabled))*)");
+    static const QRegExp rx("Palette((:(normal|active|inactive|disabled))*)");
     if (!rx.exactMatch(decl)) {
         qWarning() << Q_FUNC_INFO << tr("Invalid block declaration: %1").arg(decl);
         return;
     }
     if (!rx.cap(1).isEmpty()) {
         QStringList groups = rx.cap(1).split(':', QString::SkipEmptyParts);
-        foreach(QString g, groups) {
+        foreach (QString g, groups) {
             if ((g == "normal" || g == "active") && !colorGroups.contains(QPalette::Active))
                 colorGroups.append(QPalette::Active);
             else if (g == "inactive" && !colorGroups.contains(QPalette::Inactive))
@@ -141,7 +160,7 @@ void QssParser::parsePaletteBlock(const QString &decl, const QString &contents)
     }
 
     // Now let's go through the roles
-    foreach(QString line, contents.split(';', QString::SkipEmptyParts)) {
+    foreach (QString line, contents.split(';', QString::SkipEmptyParts)) {
         int idx = line.indexOf(':');
         if (idx <= 0) {
             qWarning() << Q_FUNC_INFO << tr("Invalid palette role assignment: %1").arg(line.trimmed());
@@ -153,201 +172,207 @@ void QssParser::parsePaletteBlock(const QString &decl, const QString &contents)
         if (_paletteColorRoles.contains(rolestr)) {
             QBrush brush = parseBrush(brushstr);
             if (colorGroups.count()) {
-                foreach(QPalette::ColorGroup group, colorGroups)
-                _palette.setBrush(group, _paletteColorRoles.value(rolestr), brush);
+                foreach (QPalette::ColorGroup group, colorGroups)
+                    _palette.setBrush(group, _paletteColorRoles.value(rolestr), brush);
             }
             else
                 _palette.setBrush(_paletteColorRoles.value(rolestr), brush);
         }
         else if (_uiStyleColorRoles.contains(rolestr)) {
-            _uiStylePalette[_uiStyleColorRoles.value(rolestr)] = parseBrush(brushstr);
+            _uiStylePalette[static_cast<int>(_uiStyleColorRoles.value(rolestr))] = parseBrush(brushstr);
         }
         else
             qWarning() << Q_FUNC_INFO << tr("Unknown palette role name: %1").arg(rolestr);
     }
 }
 
-
 /******** Determine format types from a block declaration ********/
 
-quint64 QssParser::parseFormatType(const QString &decl)
+std::pair<UiStyle::FormatType, UiStyle::MessageLabel> QssParser::parseFormatType(const QString& decl)
 {
-    QRegExp rx("ChatLine(?:::(\\w+))?(?:#([\\w\\-]+))?(?:\\[([=-,\\\"\\w\\s]+)\\])?");
+    using FormatType = UiStyle::FormatType;
+    using MessageLabel = UiStyle::MessageLabel;
+
+    const std::pair<UiStyle::FormatType, UiStyle::MessageLabel> invalid{FormatType::Invalid, MessageLabel::None};
+
+    static const QRegExp rx(R"(ChatLine(?:::(\w+))?(?:#([\w\-]+))?(?:\[([=-,\"\w\s]+)\])?)");
     // $1: subelement; $2: msgtype; $3: conditionals
     if (!rx.exactMatch(decl)) {
         qWarning() << Q_FUNC_INFO << tr("Invalid block declaration: %1").arg(decl);
-        return UiStyle::Invalid;
+        return invalid;
     }
     QString subElement = rx.cap(1);
     QString msgType = rx.cap(2);
     QString conditions = rx.cap(3);
 
-    quint64 fmtType = 0;
+    FormatType fmtType{FormatType::Base};
+    MessageLabel label{MessageLabel::None};
 
     // First determine the subelement
     if (!subElement.isEmpty()) {
         if (subElement == "timestamp")
-            fmtType |= UiStyle::Timestamp;
+            fmtType |= FormatType::Timestamp;
         else if (subElement == "sender")
-            fmtType |= UiStyle::Sender;
+            fmtType |= FormatType::Sender;
         else if (subElement == "nick")
-            fmtType |= UiStyle::Nick;
+            fmtType |= FormatType::Nick;
         else if (subElement == "contents")
-            fmtType |= UiStyle::Contents;
+            fmtType |= FormatType::Contents;
         else if (subElement == "hostmask")
-            fmtType |= UiStyle::Hostmask;
+            fmtType |= FormatType::Hostmask;
         else if (subElement == "modeflags")
-            fmtType |= UiStyle::ModeFlags;
+            fmtType |= FormatType::ModeFlags;
         else if (subElement == "url")
-            fmtType |= UiStyle::Url;
+            fmtType |= FormatType::Url;
         else {
             qWarning() << Q_FUNC_INFO << tr("Invalid subelement name in %1").arg(decl);
-            return UiStyle::Invalid;
+            return invalid;
         }
     }
 
     // Now, figure out the message type
     if (!msgType.isEmpty()) {
         if (msgType == "plain")
-            fmtType |= UiStyle::PlainMsg;
+            fmtType |= FormatType::PlainMsg;
         else if (msgType == "notice")
-            fmtType |= UiStyle::NoticeMsg;
+            fmtType |= FormatType::NoticeMsg;
         else if (msgType == "action")
-            fmtType |= UiStyle::ActionMsg;
+            fmtType |= FormatType::ActionMsg;
         else if (msgType == "nick")
-            fmtType |= UiStyle::NickMsg;
+            fmtType |= FormatType::NickMsg;
         else if (msgType == "mode")
-            fmtType |= UiStyle::ModeMsg;
+            fmtType |= FormatType::ModeMsg;
         else if (msgType == "join")
-            fmtType |= UiStyle::JoinMsg;
+            fmtType |= FormatType::JoinMsg;
         else if (msgType == "part")
-            fmtType |= UiStyle::PartMsg;
+            fmtType |= FormatType::PartMsg;
         else if (msgType == "quit")
-            fmtType |= UiStyle::QuitMsg;
+            fmtType |= FormatType::QuitMsg;
         else if (msgType == "kick")
-            fmtType |= UiStyle::KickMsg;
+            fmtType |= FormatType::KickMsg;
         else if (msgType == "kill")
-            fmtType |= UiStyle::KillMsg;
+            fmtType |= FormatType::KillMsg;
         else if (msgType == "server")
-            fmtType |= UiStyle::ServerMsg;
+            fmtType |= FormatType::ServerMsg;
         else if (msgType == "info")
-            fmtType |= UiStyle::InfoMsg;
+            fmtType |= FormatType::InfoMsg;
         else if (msgType == "error")
-            fmtType |= UiStyle::ErrorMsg;
+            fmtType |= FormatType::ErrorMsg;
         else if (msgType == "daychange")
-            fmtType |= UiStyle::DayChangeMsg;
+            fmtType |= FormatType::DayChangeMsg;
         else if (msgType == "topic")
-            fmtType |= UiStyle::TopicMsg;
+            fmtType |= FormatType::TopicMsg;
         else if (msgType == "netsplit-join")
-            fmtType |= UiStyle::NetsplitJoinMsg;
+            fmtType |= FormatType::NetsplitJoinMsg;
         else if (msgType == "netsplit-quit")
-            fmtType |= UiStyle::NetsplitQuitMsg;
+            fmtType |= FormatType::NetsplitQuitMsg;
         else if (msgType == "invite")
-            fmtType |= UiStyle::InviteMsg;
+            fmtType |= FormatType::InviteMsg;
         else {
             qWarning() << Q_FUNC_INFO << tr("Invalid message type in %1").arg(decl);
         }
     }
 
     // Next up: conditional (formats, labels, nickhash)
-    QRegExp condRx("\\s*([\\w\\-]+)\\s*=\\s*\"(\\w+)\"\\s*");
+    static const QRegExp condRx(R"lit(\s*([\w\-]+)\s*=\s*"(\w+)"\s*)lit");
     if (!conditions.isEmpty()) {
-        foreach(const QString &cond, conditions.split(',', QString::SkipEmptyParts)) {
+        foreach (const QString& cond, conditions.split(',', QString::SkipEmptyParts)) {
             if (!condRx.exactMatch(cond)) {
                 qWarning() << Q_FUNC_INFO << tr("Invalid condition %1").arg(cond);
-                return UiStyle::Invalid;
+                return invalid;
             }
             QString condName = condRx.cap(1);
             QString condValue = condRx.cap(2);
             if (condName == "label") {
-                quint64 labeltype = 0;
                 if (condValue == "highlight")
-                    labeltype = UiStyle::Highlight;
+                    label |= MessageLabel::Highlight;
                 else if (condValue == "selected")
-                    labeltype = UiStyle::Selected;
+                    label |= MessageLabel::Selected;
+                else if (condValue == "hovered")
+                    label |= MessageLabel::Hovered;
                 else {
                     qWarning() << Q_FUNC_INFO << tr("Invalid message label: %1").arg(condValue);
-                    return UiStyle::Invalid;
+                    return invalid;
                 }
-                fmtType |= (labeltype << 32);
             }
             else if (condName == "sender") {
                 if (condValue == "self")
-                    fmtType |= (quint64) UiStyle::OwnMsg << 32;  // sender="self" is actually treated as a label
+                    label |= MessageLabel::OwnMsg;  // sender="self" is actually treated as a label
                 else {
                     bool ok = true;
-                    quint64 val = condValue.toUInt(&ok, 16);
+                    quint32 val = condValue.toUInt(&ok, 16);
                     if (!ok) {
                         qWarning() << Q_FUNC_INFO << tr("Invalid senderhash specification: %1").arg(condValue);
-                        return UiStyle::Invalid;
+                        return invalid;
                     }
                     if (val >= 16) {
                         qWarning() << Q_FUNC_INFO << tr("Senderhash can be at most \"0x0f\"!");
-                        return UiStyle::Invalid;
+                        return invalid;
                     }
-                    fmtType |= ++val << 48;
+                    label |= static_cast<MessageLabel>(++val << 16);
                 }
             }
             else if (condName == "format") {
                 if (condValue == "bold")
-                    fmtType |= UiStyle::Bold;
+                    fmtType |= FormatType::Bold;
                 else if (condValue == "italic")
-                    fmtType |= UiStyle::Italic;
+                    fmtType |= FormatType::Italic;
                 else if (condValue == "underline")
-                    fmtType |= UiStyle::Underline;
-                else if (condValue == "reverse")
-                    fmtType |= UiStyle::Reverse;
+                    fmtType |= FormatType::Underline;
+                else if (condValue == "strikethrough")
+                    fmtType |= FormatType::Strikethrough;
                 else {
                     qWarning() << Q_FUNC_INFO << tr("Invalid format name: %1").arg(condValue);
-                    return UiStyle::Invalid;
+                    return invalid;
                 }
             }
             else if (condName == "fg-color" || condName == "bg-color") {
                 bool ok;
-                quint8 col = condValue.toUInt(&ok, 16);
+                quint32 col = condValue.toUInt(&ok, 16);
                 if (!ok || col > 0x0f) {
                     qWarning() << Q_FUNC_INFO << tr("Illegal IRC color specification (must be between 00 and 0f): %1").arg(condValue);
-                    return UiStyle::Invalid;
+                    return invalid;
                 }
                 if (condName == "fg-color")
-                    fmtType |= 0x00400000 | (quint32)(col << 24);
+                    fmtType |= 0x00400000 | (col << 24);
                 else
-                    fmtType |= 0x00800000 | (quint32)(col << 28);
+                    fmtType |= 0x00800000 | (col << 28);
             }
             else {
                 qWarning() << Q_FUNC_INFO << tr("Unhandled condition: %1").arg(condName);
-                return UiStyle::Invalid;
+                return invalid;
             }
         }
     }
 
-    return fmtType;
+    return std::make_pair(fmtType, label);
 }
 
-
 // FIXME: Code duplication
-quint32 QssParser::parseItemFormatType(const QString &decl)
+UiStyle::ItemFormatType QssParser::parseItemFormatType(const QString& decl)
 {
-    QRegExp rx("(Chat|Nick)ListItem(?:\\[([=-,\\\"\\w\\s]+)\\])?");
+    using ItemFormatType = UiStyle::ItemFormatType;
+
+    static const QRegExp rx(R"((Chat|Nick)ListItem(?:\[([=-,\"\w\s]+)\])?)");
     // $1: item type; $2: properties
     if (!rx.exactMatch(decl)) {
         qWarning() << Q_FUNC_INFO << tr("Invalid block declaration: %1").arg(decl);
-        return UiStyle::Invalid;
+        return ItemFormatType::Invalid;
     }
     QString mainItemType = rx.cap(1);
     QString properties = rx.cap(2);
 
-    quint32 fmtType = 0;
+    ItemFormatType fmtType{ItemFormatType::None};
 
     // Next up: properties
     QString type, state;
     if (!properties.isEmpty()) {
         QHash<QString, QString> props;
-        QRegExp propRx("\\s*([\\w\\-]+)\\s*=\\s*\"([\\w\\-]+)\"\\s*");
-        foreach(const QString &prop, properties.split(',', QString::SkipEmptyParts)) {
+        static const QRegExp propRx(R"lit(\s*([\w\-]+)\s*=\s*"([\w\-]+)"\s*)lit");
+        foreach (const QString& prop, properties.split(',', QString::SkipEmptyParts)) {
             if (!propRx.exactMatch(prop)) {
                 qWarning() << Q_FUNC_INFO << tr("Invalid proplist %1").arg(prop);
-                return UiStyle::Invalid;
+                return ItemFormatType::Invalid;
             }
             props[propRx.cap(1)] = propRx.cap(2);
         }
@@ -356,59 +381,58 @@ quint32 QssParser::parseItemFormatType(const QString &decl)
     }
 
     if (mainItemType == "Chat") {
-        fmtType |= UiStyle::BufferViewItem;
+        fmtType |= ItemFormatType::BufferViewItem;
         if (!type.isEmpty()) {
             if (type == "network")
-                fmtType |= UiStyle::NetworkItem;
+                fmtType |= ItemFormatType::NetworkItem;
             else if (type == "channel")
-                fmtType |= UiStyle::ChannelBufferItem;
+                fmtType |= ItemFormatType::ChannelBufferItem;
             else if (type == "query")
-                fmtType |= UiStyle::QueryBufferItem;
+                fmtType |= ItemFormatType::QueryBufferItem;
             else {
                 qWarning() << Q_FUNC_INFO << tr("Invalid chatlist item type %1").arg(type);
-                return UiStyle::Invalid;
+                return ItemFormatType::Invalid;
             }
         }
         if (!state.isEmpty()) {
             if (state == "inactive")
-                fmtType |= UiStyle::InactiveBuffer;
+                fmtType |= ItemFormatType::InactiveBuffer;
             else if (state == "channel-event")
-                fmtType |= UiStyle::ActiveBuffer;
+                fmtType |= ItemFormatType::ActiveBuffer;
             else if (state == "unread-message")
-                fmtType |= UiStyle::UnreadBuffer;
+                fmtType |= ItemFormatType::UnreadBuffer;
             else if (state == "highlighted")
-                fmtType |= UiStyle::HighlightedBuffer;
+                fmtType |= ItemFormatType::HighlightedBuffer;
             else if (state == "away")
-                fmtType |= UiStyle::UserAway;
+                fmtType |= ItemFormatType::UserAway;
             else {
                 qWarning() << Q_FUNC_INFO << tr("Invalid chatlist state %1").arg(state);
-                return UiStyle::Invalid;
+                return ItemFormatType::Invalid;
             }
         }
     }
-    else { // NickList
-        fmtType |= UiStyle::NickViewItem;
+    else {  // NickList
+        fmtType |= ItemFormatType::NickViewItem;
         if (!type.isEmpty()) {
             if (type == "user") {
-                fmtType |= UiStyle::IrcUserItem;
+                fmtType |= ItemFormatType::IrcUserItem;
                 if (state == "away")
-                    fmtType |= UiStyle::UserAway;
+                    fmtType |= ItemFormatType::UserAway;
             }
             else if (type == "category")
-                fmtType |= UiStyle::UserCategoryItem;
+                fmtType |= ItemFormatType::UserCategoryItem;
         }
     }
     return fmtType;
 }
 
-
 /******** Parse a whole format attribute block ********/
 
-QTextCharFormat QssParser::parseFormat(const QString &qss)
+QTextCharFormat QssParser::parseFormat(const QStringqss)
 {
     QTextCharFormat format;
 
-    foreach(QString line, qss.split(';', QString::SkipEmptyParts)) {
+    foreach (QString line, qss.split(';', QString::SkipEmptyParts)) {
         int idx = line.indexOf(':');
         if (idx <= 0) {
             qWarning() << Q_FUNC_INFO << tr("Invalid property declaration: %1").arg(line.trimmed());
@@ -422,6 +446,20 @@ QTextCharFormat QssParser::parseFormat(const QString &qss)
         else if (property == "foreground" || property == "color")
             format.setForeground(parseBrush(value));
 
+        // Color code overrides
+        else if (property == "allow-foreground-override") {
+            bool ok;
+            bool v = parseBoolean(value, &ok);
+            if (ok)
+                format.setProperty(static_cast<int>(UiStyle::FormatProperty::AllowForegroundOverride), v);
+        }
+        else if (property == "allow-background-override") {
+            bool ok;
+            bool v = parseBoolean(value, &ok);
+            if (ok)
+                format.setProperty(static_cast<int>(UiStyle::FormatProperty::AllowBackgroundOverride), v);
+        }
+
         // font-related properties
         else if (property.startsWith("font")) {
             if (property == "font")
@@ -448,10 +486,27 @@ QTextCharFormat QssParser::parseFormat(const QString &qss)
     return format;
 }
 
+/******** Boolean value ********/
+
+bool QssParser::parseBoolean(const QString& str, bool* ok) const
+{
+    if (ok)
+        *ok = true;
+
+    if (str == "true")
+        return true;
+    if (str == "false")
+        return false;
+
+    qWarning() << Q_FUNC_INFO << tr("Invalid boolean value: %1").arg(str);
+    if (ok)
+        *ok = false;
+    return false;
+}
 
 /******** Brush ********/
 
-QBrush QssParser::parseBrush(const QString &str, bool *ok)
+QBrush QssParser::parseBrush(const QString& str, bool* ok)
 {
     if (ok)
         *ok = false;
@@ -462,8 +517,22 @@ QBrush QssParser::parseBrush(const QString &str, bool *ok)
         return QBrush(c);
     }
 
-    if (str.startsWith("palette")) { // Palette color role
-        QRegExp rx("palette\\s*\\(\\s*([a-z-]+)\\s*\\)");
+    if (str.startsWith("palette")) {  // Palette color role
+        // Does the palette follow the expected format?  For example:
+        // palette(marker-line)
+        // palette    ( system-color-0f  )
+        //
+        // Match the palette marker, grabbing the name inside in  case-sensitive manner
+        //   palette\s*\(\s*([a-z-0-9]+)\s*\)
+        //   palette   Match the string 'palette'
+        //   \s*       Match any amount of whitespace
+        //   \(, \)    Match literal '(' or ')' marks
+        //   (...+)    Match contents between 1 and unlimited number of times
+        //   [a-z-]    Match any character from a-z, case sensitive
+        //   [0-9]     Match any digit from 0-9
+        // Note that '\' must be escaped as '\\'
+        // Helpful interactive website for debugging and explaining:  https://regex101.com/
+        static const QRegExp rx(R"(palette\s*\(\s*([a-z-0-9]+)\s*\))");
         if (!rx.exactMatch(str)) {
             qWarning() << Q_FUNC_INFO << tr("Invalid palette color role specification: %1").arg(str);
             return QBrush();
@@ -471,13 +540,13 @@ QBrush QssParser::parseBrush(const QString &str, bool *ok)
         if (_paletteColorRoles.contains(rx.cap(1)))
             return QBrush(_palette.brush(_paletteColorRoles.value(rx.cap(1))));
         if (_uiStyleColorRoles.contains(rx.cap(1)))
-            return QBrush(_uiStylePalette.at(_uiStyleColorRoles.value(rx.cap(1))));
+            return QBrush(_uiStylePalette.at(static_cast<int>(_uiStyleColorRoles.value(rx.cap(1)))));
         qWarning() << Q_FUNC_INFO << tr("Unknown palette color role: %1").arg(rx.cap(1));
         return QBrush();
     }
     else if (str.startsWith("qlineargradient")) {
-        static QString rxFloat("\\s*(-?\\s*[0-9]*\\.?[0-9]+)\\s*");
-        QRegExp rx(QString("qlineargradient\\s*\\(\\s*x1:%1,\\s*y1:%1,\\s*x2:%1,\\s*y2:%1,(.+)\\)").arg(rxFloat));
+        static const QString rxFloat(R"(\s*(-?\s*[0-9]*\.?[0-9]+)\s*)");
+        static const QRegExp rx(QString(R"(qlineargradient\s*\(\s*x1:%1,\s*y1:%1,\s*x2:%1,\s*y2:%1,(.+)\))").arg(rxFloat));
         if (!rx.exactMatch(str)) {
             qWarning() << Q_FUNC_INFO << tr("Invalid gradient declaration: %1").arg(str);
             return QBrush();
@@ -499,8 +568,8 @@ QBrush QssParser::parseBrush(const QString &str, bool *ok)
         return QBrush(gradient);
     }
     else if (str.startsWith("qconicalgradient")) {
-        static QString rxFloat("\\s*(-?\\s*[0-9]*\\.?[0-9]+)\\s*");
-        QRegExp rx(QString("qconicalgradient\\s*\\(\\s*cx:%1,\\s*cy:%1,\\s*angle:%1,(.+)\\)").arg(rxFloat));
+        static const QString rxFloat(R"(\s*(-?\s*[0-9]*\.?[0-9]+)\s*)");
+        static const QRegExp rx(QString(R"(qconicalgradient\s*\(\s*cx:%1,\s*cy:%1,\s*angle:%1,(.+)\))").arg(rxFloat));
         if (!rx.exactMatch(str)) {
             qWarning() << Q_FUNC_INFO << tr("Invalid gradient declaration: %1").arg(str);
             return QBrush();
@@ -521,8 +590,8 @@ QBrush QssParser::parseBrush(const QString &str, bool *ok)
         return QBrush(gradient);
     }
     else if (str.startsWith("qradialgradient")) {
-        static QString rxFloat("\\s*(-?\\s*[0-9]*\\.?[0-9]+)\\s*");
-        QRegExp rx(QString("qradialgradient\\s*\\(\\s*cx:%1,\\s*cy:%1,\\s*radius:%1,\\s*fx:%1,\\s*fy:%1,(.+)\\)").arg(rxFloat));
+        static const QString rxFloat(R"(\s*(-?\s*[0-9]*\.?[0-9]+)\s*)");
+        static const QRegExp rx(QString(R"(qradialgradient\s*\(\s*cx:%1,\s*cy:%1,\s*radius:%1,\s*fx:%1,\s*fy:%1,(.+)\))").arg(rxFloat));
         if (!rx.exactMatch(str)) {
             qWarning() << Q_FUNC_INFO << tr("Invalid gradient declaration: %1").arg(str);
             return QBrush();
@@ -548,13 +617,12 @@ QBrush QssParser::parseBrush(const QString &str, bool *ok)
     return QBrush();
 }
 
-
-QColor QssParser::parseColor(const QString &str)
+QColor QssParser::parseColor(const QString& str)
 {
     if (str.startsWith("rgba")) {
         ColorTuple tuple = parseColorTuple(str.mid(4));
         if (tuple.count() == 4)
-            return QColor(tuple.at(0), tuple.at(1), tuple.at(2), tuple.at(3));
+            return QColor(tuple.at(0), tuple.at(1), tuple.at(2), tuple.at(3));  // NOLINT(modernize-return-braced-init-list)
     }
     else if (str.startsWith("rgb")) {
         ColorTuple tuple = parseColorTuple(str.mid(3));
@@ -578,24 +646,23 @@ QColor QssParser::parseColor(const QString &str)
         }
     }
     else {
-        QRegExp rx("#?[0-9A-Fa-z]+");
+        static const QRegExp rx("#?[0-9A-Fa-z]+");
         if (rx.exactMatch(str))
             return QColor(str);
     }
     return QColor();
 }
 
-
 // get a list of comma-separated int values or percentages (rel to 0-255)
-QssParser::ColorTuple QssParser::parseColorTuple(const QString &str)
+QssParser::ColorTuple QssParser::parseColorTuple(const QStringstr)
 {
     ColorTuple result;
-    QRegExp rx("\\(((\\s*[0-9]{1,3}%?\\s*)(,\\s*[0-9]{1,3}%?\\s*)*)\\)");
+    static const QRegExp rx(R"(\(((\s*[0-9]{1,3}%?\s*)(,\s*[0-9]{1,3}%?\s*)*)\))");
     if (!rx.exactMatch(str.trimmed())) {
         return ColorTuple();
     }
     QStringList values = rx.cap(1).split(',');
-    foreach(QString v, values) {
+    foreach (QString v, values) {
         qreal val;
         bool perc = false;
         bool ok;
@@ -608,19 +675,18 @@ QssParser::ColorTuple QssParser::parseColorTuple(const QString &str)
         if (!ok)
             return ColorTuple();
         if (perc)
-            val = 255 * val/100;
+            val = 255 * val / 100;
         result.append(val);
     }
     return result;
 }
 
-
-QGradientStops QssParser::parseGradientStops(const QString &str_)
+QGradientStops QssParser::parseGradientStops(const QString& str_)
 {
     QString str = str_;
     QGradientStops result;
-    static QString rxFloat("(0?\\.[0-9]+|[01])"); // values between 0 and 1
-    QRegExp rx(QString("\\s*,?\\s*stop:\\s*(%1)\\s+([^:]+)(,\\s*stop:|$)").arg(rxFloat));
+    static const QString rxFloat("(0?\\.[0-9]+|[01])");  // values between 0 and 1
+    static const QRegExp rx(QString(R"(\s*,?\s*stop:\s*(%1)\s+([^:]+)(,\s*stop:|$))").arg(rxFloat));
     int idx;
     while ((idx = rx.indexIn(str)) == 0) {
         qreal x = rx.cap(1).toDouble();
@@ -636,31 +702,38 @@ QGradientStops QssParser::parseGradientStops(const QString &str_)
     return result;
 }
 
-
 /******** Font Properties ********/
 
-void QssParser::parseFont(const QString &value, QTextCharFormat *format)
+void QssParser::parseFont(const QString& value, QTextCharFormat* format)
 {
-    QRegExp rx("((?:(?:normal|italic|oblique|underline|bold|100|200|300|400|500|600|700|800|900) ){0,2}) ?(\\d+)(pt|px)? \"(.*)\"");
+    static const QRegExp rx(
+        "((?:(?:normal|italic|oblique|underline|strikethrough|bold|100|200|300|400|500|600|700|800|900) ){0,2}) ?(\\d+)(pt|px)? \"(.*)\"");
     if (!rx.exactMatch(value)) {
         qWarning() << Q_FUNC_INFO << tr("Invalid font specification: %1").arg(value);
         return;
     }
     format->setFontItalic(false);
+    format->setFontUnderline(false);
+    format->setFontStrikeOut(false);
     format->setFontWeight(QFont::Normal);
     QStringList proplist = rx.cap(1).split(' ', QString::SkipEmptyParts);
-    foreach(QString prop, proplist) {
-        if (prop == "italic")
+    foreach (QString prop, proplist) {
+        if (prop == "normal")
+            ;  // pass
+        else if (prop == "italic")
             format->setFontItalic(true);
         else if (prop == "underline")
             format->setFontUnderline(true);
-        //else if(prop == "oblique")
-        //  format->setStyle(QFont::StyleOblique);
+        else if (prop == "strikethrough")
+            format->setFontStrikeOut(true);
+        else if (prop == "oblique")
+            // Oblique is not a property supported by QTextCharFormat
+            format->setFontItalic(true);
         else if (prop == "bold")
             format->setFontWeight(QFont::Bold);
-        else { // number
+        else {  // number
             int w = prop.toInt();
-            format->setFontWeight(qMin(w / 8, 99)); // taken from Qt's qss parser
+            format->setFontWeight(qMin(w / 8, 99));  // taken from Qt's qss parser
         }
     }
 
@@ -672,8 +745,7 @@ void QssParser::parseFont(const QString &value, QTextCharFormat *format)
     format->setFontFamily(rx.cap(4));
 }
 
-
-void QssParser::parseFontStyle(const QString &value, QTextCharFormat *format)
+void QssParser::parseFontStyle(const QString& value, QTextCharFormat* format)
 {
     if (value == "normal")
         format->setFontItalic(false);
@@ -681,15 +753,17 @@ void QssParser::parseFontStyle(const QString &value, QTextCharFormat *format)
         format->setFontItalic(true);
     else if (value == "underline")
         format->setFontUnderline(true);
-    //else if(value == "oblique")
-    //  format->setStyle(QFont::StyleOblique);
+    else if (value == "strikethrough")
+        format->setFontStrikeOut(true);
+    else if (value == "oblique")
+        // Oblique is not a property supported by QTextCharFormat
+        format->setFontItalic(true);
     else {
         qWarning() << Q_FUNC_INFO << tr("Invalid font style specification: %1").arg(value);
     }
 }
 
-
-void QssParser::parseFontWeight(const QString &value, QTextCharFormat *format)
+void QssParser::parseFontWeight(const QString& value, QTextCharFormat* format)
 {
     if (value == "normal")
         format->setFontWeight(QFont::Normal);
@@ -702,14 +776,13 @@ void QssParser::parseFontWeight(const QString &value, QTextCharFormat *format)
             qWarning() << Q_FUNC_INFO << tr("Invalid font weight specification: %1").arg(value);
             return;
         }
-        format->setFontWeight(qMin(w / 8, 99)); // taken from Qt's qss parser
+        format->setFontWeight(qMin(w / 8, 99));  // taken from Qt's qss parser
     }
 }
 
-
-void QssParser::parseFontSize(const QString &value, QTextCharFormat *format)
+void QssParser::parseFontSize(const QString& value, QTextCharFormat* format)
 {
-    QRegExp rx("(\\d+)(pt|px)");
+    static const QRegExp rx("(\\d+)(pt|px)");
     if (!rx.exactMatch(value)) {
         qWarning() << Q_FUNC_INFO << tr("Invalid font size specification: %1").arg(value);
         return;
@@ -720,8 +793,7 @@ void QssParser::parseFontSize(const QString &value, QTextCharFormat *format)
         format->setFontPointSize(rx.cap(1).toInt());
 }
 
-
-void QssParser::parseFontFamily(const QString &value, QTextCharFormat *format)
+void QssParser::parseFontFamily(const QString& value, QTextCharFormat* format)
 {
     QString family = value;
     if (family.startsWith('"') && family.endsWith('"')) {