- length = 6;
- }
- } else if(s[pos+1] == 'O') { // reset formatting
- curfmt &= 0x0000000f; // we keep message type-specific formatting
- length = 2;
- } else if(s[pos+1] == 'R') { // reverse
- // TODO: implement reverse formatting
-
- 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 == Invalid) {
- qWarning() << (QString("Invalid format code in string: %1").arg(s));
- continue;
- }
- curfmt ^= ftype;
- length = code.length();
- }
- s.remove(pos, length);
- if(pos == result.formatList.last().first)
- result.formatList.last().second = curfmt;
- else
- result.formatList.append(qMakePair((quint16)pos, curfmt));
- }
- result.plainText = s;
- return result;
-}
-
-QString UiStyle::mircToInternal(const QString &mirc_) {
- QString mirc = mirc_;
- mirc.replace('%', "%%"); // escape % just to be sure
- mirc.replace('\x02', "%B");
- mirc.replace('\x0f', "%O");
- mirc.replace('\x12', "%R");
- mirc.replace('\x16', "%R");
- mirc.replace('\x1d', "%S");
- mirc.replace('\x1f', "%U");
-
- // Now we bring the color codes (\x03) in a sane format that can be parsed more easily later.
- // %Dcfxx is foreground, %Dcbxx is background color, where xx is a 2 digit dec number denoting the color code.
- // %Dc- turns color off.
- // Note: We use the "mirc standard" as described in <http://www.mirc.co.uk/help/color.txt>.
- // This means that we don't accept something like \x03,5 (even though others, like WeeChat, do).
- int pos = 0;
- for(;;) {
- pos = mirc.indexOf('\x03', pos);
- if(pos < 0) break; // no more mirc color codes
- QString ins, num;
- int l = mirc.length();
- int i = pos + 1;
- // check for fg color
- if(i < l && mirc[i].isDigit()) {
- num = mirc[i++];
- if(i < l && mirc[i].isDigit()) num.append(mirc[i++]);
- else num.prepend('0');
- ins = QString("%Dcf%1").arg(num);
-
- if(i+1 < l && mirc[i] == ',' && mirc[i+1].isDigit()) {
- i++;
- num = mirc[i++];
- if(i < l && mirc[i].isDigit()) num.append(mirc[i++]);
- else num.prepend('0');
- ins += QString("%Dcb%1").arg(num);
- }
- } else {
- ins = "%Dc-";
- }
- mirc.replace(pos, i-pos, ins);
- }
- return mirc;
+ 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();
+ }
+ s.remove(pos, length);
+ if (pos == result.formatList.back().first)
+ result.formatList.back().second = curfmt;
+ else
+ result.formatList.emplace_back(std::make_pair(pos, curfmt));
+ }
+ result.plainText = s;
+ return result;