-UiStyle::StyledString UiStyle::styleString(const QString &s_, quint32 baseFormat) {
- QString s = s_;
- if(s.length() > 65535) {
- qWarning() << QString("String too long to be styled: %1").arg(s);
- return StyledString();
- }
- StyledString result;
- result.formatList.append(qMakePair((quint16)0, baseFormat));
- quint32 curfmt = baseFormat;
- int pos = 0; quint16 length = 0;
- for(;;) {
- pos = s.indexOf('%', pos);
- if(pos < 0) break;
- if(s[pos+1] == '%') { // escaped %, we just remove one and continue
- s.remove(pos, 1);
- pos++;
- continue;
- }
- if(s[pos+1] == 'D' && s[pos+2] == 'c') { // color code
- if(s[pos+3] == '-') { // color off
- curfmt &= 0x003fffff;
- length = 4;
- } else {
- int color = 10 * s[pos+4].digitValue() + s[pos+5].digitValue();
- //TODO: use 99 as transparent color (re mirc color "standard")
- color &= 0x0f;
- if(s[pos+3] == 'f') {
- curfmt &= 0xf0ffffff;
- curfmt |= (color << 24) | 0x00400000;
- } else {
- curfmt &= 0x0fffffff;
- curfmt |= (color << 28) | 0x00800000;
+UiStyle::StyledString UiStyle::styleString(const QString& s_, FormatType baseFormat)
+{
+ QString s = s_;
+ StyledString result;
+ result.formatList.emplace_back(std::make_pair(quint16{0}, Format{baseFormat, {}, {}}));
+
+ if (s.length() > 65535) {
+ // We use quint16 for indexes
+ qWarning() << QString("String too long to be styled: %1").arg(s);
+ result.plainText = s;
+ return result;
+ }
+
+ Format curfmt{baseFormat, {}, {}};
+ QChar fgChar{'f'}; // character to indicate foreground color, changed when reversing
+
+ int pos = 0;
+ quint16 length = 0;
+ for (;;) {
+ pos = s.indexOf('%', pos);
+ if (pos < 0)
+ break;
+ if (s[pos + 1] == '%') { // escaped %, we just remove one and continue
+ s.remove(pos, 1);
+ pos++;
+ continue;
+ }
+ if (s[pos + 1] == 'D' && s[pos + 2] == 'c') { // mIRC color code
+ if (s[pos + 3] == '-') { // color off
+ curfmt.type &= 0x003fffff;
+ curfmt.foreground = QColor{};
+ curfmt.background = QColor{};
+ length = 4;
+ }
+ else {
+ quint32 color = 10 * s[pos + 4].digitValue() + s[pos + 5].digitValue();
+ // Color values 0-15 are traditional mIRC colors, defined in the stylesheet and thus going through the format engine
+ // Larger color values are hardcoded and applied separately (cf. https://modern.ircdocs.horse/formatting.html#colors-16-98)
+ if (s[pos + 3] == fgChar) {
+ if (color < 16) {
+ // Traditional mIRC color, defined in the stylesheet
+ curfmt.type &= 0xf0ffffff;
+ curfmt.type |= color << 24 | 0x00400000;
+ curfmt.foreground = QColor{};
+ }
+ else {
+ curfmt.type &= 0xf0bfffff; // mask out traditional foreground color
+ curfmt.foreground = extendedMircColor(color);
+ }
+ }
+ else {
+ if (color < 16) {
+ curfmt.type &= 0x0fffffff;
+ curfmt.type |= color << 28 | 0x00800000;
+ curfmt.background = QColor{};
+ }
+ else {
+ curfmt.type &= 0x0f7fffff; // mask out traditional background color
+ curfmt.background = extendedMircColor(color);
+ }
+ }
+ length = 6;
+ }
+ }
+ else if (s[pos + 1] == 'D' && s[pos + 2] == 'h') { // Hex color
+ QColor color{s.mid(pos + 4, 7)};
+ if (s[pos + 3] == fgChar) {
+ curfmt.type &= 0xf0bfffff; // mask out mIRC foreground color
+ curfmt.foreground = std::move(color);
+ }
+ else {
+ curfmt.type &= 0x0f7fffff; // mask out mIRC background color
+ curfmt.background = std::move(color);
+ }
+ length = 11;
+ }
+ else if (s[pos + 1] == 'O') { // reset formatting
+ curfmt.type &= 0x000000ff; // we keep message type-specific formatting
+ curfmt.foreground = QColor{};
+ curfmt.background = QColor{};
+ fgChar = 'f';
+ length = 2;
+ }
+ else if (s[pos + 1] == 'R') { // Reverse colors
+ fgChar = (fgChar == 'f' ? 'b' : 'f');
+ auto orig = static_cast<quint32>(curfmt.type & 0xffc00000);
+ curfmt.type &= 0x003fffff;
+ curfmt.type |= (orig & 0x00400000) << 1;
+ curfmt.type |= (orig & 0x0f000000) << 4;
+ curfmt.type |= (orig & 0x00800000) >> 1;
+ curfmt.type |= (orig & 0xf0000000) >> 4;
+ std::swap(curfmt.foreground, curfmt.background);
+ length = 2;
+ }
+ else { // all others are toggles
+ QString code = QString("%") + s[pos + 1];
+ if (s[pos + 1] == 'D')
+ code += s[pos + 2];
+ FormatType ftype = formatType(code);
+ if (ftype == FormatType::Invalid) {
+ pos++;
+ qWarning() << (QString("Invalid format code in string: %1").arg(s));
+ continue;
+ }
+ curfmt.type ^= ftype;
+ length = code.length();