-quint64 QssParser::parseFormatType(const QString &decl) {
- QRegExp rx("ChatLine(?:::(\\w+))?(?:#(\\w+))?(?:\\[([=-,\\\"\\w\\s]+)\\])?\\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;
- }
- QString subElement = rx.cap(1);
- QString msgType = rx.cap(2);
- QString conditions = rx.cap(3);
-
- quint64 fmtType = 0;
-
- // First determine the subelement
- if(!subElement.isEmpty()) {
- if(subElement == "timestamp")
- fmtType |= UiStyle::Timestamp;
- else if(subElement == "sender")
- fmtType |= UiStyle::Sender;
- else if(subElement == "nick")
- fmtType |= UiStyle::Nick;
- else if(subElement == "contents")
- fmtType |= UiStyle::Contents;
- else if(subElement == "hostmask")
- fmtType |= UiStyle::Hostmask;
- else if(subElement == "modeflags")
- fmtType |= UiStyle::ModeFlags;
- else {
- qWarning() << Q_FUNC_INFO << tr("Invalid subelement name in %1").arg(decl);
- return UiStyle::Invalid;
- }
- }
-
- // Now, figure out the message type
- if(!msgType.isEmpty()) {
- if(msgType == "plain")
- fmtType |= UiStyle::PlainMsg;
- else if(msgType == "notice")
- fmtType |= UiStyle::NoticeMsg;
- else if(msgType == "action")
- fmtType |= UiStyle::ActionMsg;
- else if(msgType == "nick")
- fmtType |= UiStyle::NickMsg;
- else if(msgType == "mode")
- fmtType |= UiStyle::ModeMsg;
- else if(msgType == "join")
- fmtType |= UiStyle::JoinMsg;
- else if(msgType == "part")
- fmtType |= UiStyle::PartMsg;
- else if(msgType == "quit")
- fmtType |= UiStyle::QuitMsg;
- else if(msgType == "kick")
- fmtType |= UiStyle::KickMsg;
- else if(msgType == "kill")
- fmtType |= UiStyle::KillMsg;
- else if(msgType == "server")
- fmtType |= UiStyle::ServerMsg;
- else if(msgType == "info")
- fmtType |= UiStyle::InfoMsg;
- else if(msgType == "error")
- fmtType |= UiStyle::ErrorMsg;
- else if(msgType == "daychange")
- fmtType |= UiStyle::DayChangeMsg;
- 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*");
- if(!conditions.isEmpty()) {
- 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;
- }
- QString condName = condRx.cap(1);
- QString condValue = condRx.cap(2);
- if(condName == "label") {
- quint64 labeltype = 0;
- if(condValue == "highlight")
- labeltype = UiStyle::Highlight;
+
+void QssParser::parseListItemBlock(const QString &decl, const QString &contents)
+{
+ 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)
+{
+ QList<QPalette::ColorGroup> colorGroups;
+
+ // Check if we want to apply this palette definition for particular ColorGroups
+ 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) {
+ if ((g == "normal" || g == "active") && !colorGroups.contains(QPalette::Active))
+ colorGroups.append(QPalette::Active);
+ else if (g == "inactive" && !colorGroups.contains(QPalette::Inactive))
+ colorGroups.append(QPalette::Inactive);
+ else if (g == "disabled" && !colorGroups.contains(QPalette::Disabled))
+ colorGroups.append(QPalette::Disabled);
+ }
+ }
+
+ // Now let's go through the roles
+ 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());
+ continue;
+ }
+ QString rolestr = line.left(idx).trimmed();
+ QString brushstr = line.mid(idx + 1).trimmed();
+
+ if (_paletteColorRoles.contains(rolestr)) {
+ QBrush brush = parseBrush(brushstr);
+ if (colorGroups.count()) {
+ 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[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 ********/
+
+std::pair<UiStyle::FormatType, UiStyle::MessageLabel> QssParser::parseFormatType(const QString &decl)
+{
+ using FormatType = UiStyle::FormatType;
+ using MessageLabel = UiStyle::MessageLabel;
+
+ const std::pair<UiStyle::FormatType, UiStyle::MessageLabel> invalid{FormatType::Invalid, MessageLabel::None};
+
+ static const QRegExp rx("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 invalid;
+ }
+ QString subElement = rx.cap(1);
+ QString msgType = rx.cap(2);
+ QString conditions = rx.cap(3);
+
+ FormatType fmtType{FormatType::Base};
+ MessageLabel label{MessageLabel::None};
+
+ // First determine the subelement
+ if (!subElement.isEmpty()) {
+ if (subElement == "timestamp")
+ fmtType |= FormatType::Timestamp;
+ else if (subElement == "sender")
+ fmtType |= FormatType::Sender;
+ else if (subElement == "nick")
+ fmtType |= FormatType::Nick;
+ else if (subElement == "contents")
+ fmtType |= FormatType::Contents;
+ else if (subElement == "hostmask")
+ fmtType |= FormatType::Hostmask;
+ else if (subElement == "modeflags")
+ fmtType |= FormatType::ModeFlags;
+ else if (subElement == "url")
+ fmtType |= FormatType::Url;
+ else {
+ qWarning() << Q_FUNC_INFO << tr("Invalid subelement name in %1").arg(decl);
+ return invalid;
+ }
+ }
+
+ // Now, figure out the message type
+ if (!msgType.isEmpty()) {
+ if (msgType == "plain")
+ fmtType |= FormatType::PlainMsg;
+ else if (msgType == "notice")
+ fmtType |= FormatType::NoticeMsg;
+ else if (msgType == "action")
+ fmtType |= FormatType::ActionMsg;
+ else if (msgType == "nick")
+ fmtType |= FormatType::NickMsg;
+ else if (msgType == "mode")
+ fmtType |= FormatType::ModeMsg;
+ else if (msgType == "join")
+ fmtType |= FormatType::JoinMsg;
+ else if (msgType == "part")
+ fmtType |= FormatType::PartMsg;
+ else if (msgType == "quit")
+ fmtType |= FormatType::QuitMsg;
+ else if (msgType == "kick")
+ fmtType |= FormatType::KickMsg;
+ else if (msgType == "kill")
+ fmtType |= FormatType::KillMsg;
+ else if (msgType == "server")
+ fmtType |= FormatType::ServerMsg;
+ else if (msgType == "info")
+ fmtType |= FormatType::InfoMsg;
+ else if (msgType == "error")
+ fmtType |= FormatType::ErrorMsg;
+ else if (msgType == "daychange")
+ fmtType |= FormatType::DayChangeMsg;
+ else if (msgType == "topic")
+ fmtType |= FormatType::TopicMsg;
+ else if (msgType == "netsplit-join")
+ fmtType |= FormatType::NetsplitJoinMsg;
+ else if (msgType == "netsplit-quit")
+ fmtType |= FormatType::NetsplitQuitMsg;
+ else if (msgType == "invite")
+ fmtType |= FormatType::InviteMsg;