X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fuisupport%2Fuistyle.cpp;h=9abdeb53d4229e379bde21f048b7592de6f5d45a;hp=e0d34c6657232d9a96e98394368c3c76f7afe206;hb=7fcfb895c67d3769e985905cbc0bc885f5e52b56;hpb=e017aca90eb3444df68fb365a5d50b05881b1c5c diff --git a/src/uisupport/uistyle.cpp b/src/uisupport/uistyle.cpp index e0d34c66..9abdeb53 100644 --- a/src/uisupport/uistyle.cpp +++ b/src/uisupport/uistyle.cpp @@ -21,8 +21,17 @@ #include "uistyle.h" #include "uistylesettings.h" +#include "util.h" UiStyle::UiStyle(const QString &settingsKey) : _settingsKey(settingsKey) { + // register FormatList if that hasn't happened yet + // FIXME I don't think this actually avoids double registration... then again... does it hurt? + if(QVariant::nameToType("UiStyle::FormatList") == QVariant::Invalid) { + qRegisterMetaType("UiStyle::FormatList"); + qRegisterMetaTypeStreamOperators("UiStyle::FormatList"); + Q_ASSERT(QVariant::nameToType("UiStyle::FormatList") != QVariant::Invalid); + } + // Default format _defaultPlainFormat.setForeground(QBrush("#000000")); _defaultPlainFormat.setFont(QFont("Monospace", QApplication::font().pointSize())); @@ -110,6 +119,8 @@ void UiStyle::setFormat(FormatType ftype, QTextCharFormat fmt, Settings::Mode mo s.removeCustomFormat(ftype); } } + // TODO: invalidate only affected cached formats... if that's possible with less overhead than just rebuilding them + _cachedFormats.clear(); } QTextCharFormat UiStyle::format(FormatType ftype, Settings::Mode mode) const { @@ -135,6 +146,7 @@ QTextCharFormat UiStyle::mergedFormat(quint32 ftype) { if(ftype & 0x00800000) fmt.merge(format((FormatType)(ftype & 0xf0800000))); // background // URL if(ftype & Url) fmt.merge(format(Url)); + _cachedFormats[ftype] = fmt; return fmt; } @@ -151,12 +163,16 @@ QString UiStyle::formatCode(FormatType ftype) const { // Since we create those ourselves, we should be pretty safe that nobody does something crappy here. UiStyle::StyledString UiStyle::styleString(const QString &s_) { QString s = s_; + if(s.length() > 65535) { + qWarning() << QString("String too long to be styled: %1").arg(s); + return StyledString(); + } StyledString result; - result.formats.append(qMakePair(0, (quint32)None)); + result.formatList.append(qMakePair((quint16)0, (quint32)None)); quint32 curfmt = (quint32)None; - int pos = 0; int length = 0; + int pos = 0; quint16 length = 0; for(;;) { - pos = s.indexOf('%', pos); + int pos = s.indexOf('%', pos); if(pos < 0) break; if(s[pos+1] == '%') { // escaped %, we just remove one and continue s.remove(pos, 1); @@ -196,11 +212,139 @@ UiStyle::StyledString UiStyle::styleString(const QString &s_) { length = code.length(); } s.remove(pos, length); - if(pos == result.formats.last().first) - result.formats.last().second = curfmt; + if(pos == result.formatList.last().first) + result.formatList.last().second = curfmt; else - result.formats.append(qMakePair(pos, curfmt)); + 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 . + // 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); } - result.text = s; + return mirc; +} + +UiStyle::StyledMessage UiStyle::styleMessage(const Message &msg) { + QString user = userFromMask(msg.sender()); + QString host = hostFromMask(msg.sender()); + QString nick = nickFromMask(msg.sender()); + QString txt = mircToInternal(msg.contents()); + QString bufferName = msg.bufferInfo().bufferName(); + + StyledMessage result; + + result.timestamp = styleString(tr("%DT[%1]").arg(msg.timestamp().toLocalTime().toString("hh:mm:ss"))); + + QString s, t; + switch(msg.type()) { + case Message::Plain: + s = tr("%DS<%1>").arg(nick); t = tr("%D0%1").arg(txt); break; + case Message::Notice: + s = tr("%Dn[%1]").arg(nick); t = tr("%Dn%1").arg(txt); break; + case Message::Server: + s = tr("%Ds*"); t = tr("%Ds%1").arg(txt); break; + case Message::Error: + s = tr("%De*"); t = tr("%De%1").arg(txt); break; + case Message::Join: + s = tr("%Dj-->"); t = tr("%Dj%DN%1%DN %DH(%2@%3)%DH has joined %DC%4%DC").arg(nick, user, host, bufferName); break; + case Message::Part: + s = tr("%Dp<--"); t = tr("%Dp%DN%1%DN %DH(%2@%3)%DH has left %DC%4%DC").arg(nick, user, host, bufferName); + if(!txt.isEmpty()) t = QString("%1 (%2)").arg(t).arg(txt); + break; + case Message::Quit: + s = tr("%Dq<--"); t = tr("%Dq%DN%DU%1%DU%DN %DH(%2@%3)%DH has quit").arg(nick, user, host); + if(!txt.isEmpty()) t = QString("%1 (%2)").arg(t).arg(txt); + break; + case Message::Kick: + { s = tr("%Dk<-*"); + QString victim = txt.section(" ", 0, 0); + //if(victim == ui.ownNick->currentText()) victim = tr("you"); + QString kickmsg = txt.section(" ", 1); + t = tr("%Dk%DN%1%DN has kicked %DN%2%DN from %DC%3%DC").arg(nick).arg(victim).arg(bufferName); + if(!kickmsg.isEmpty()) t = QString("%1 (%2)").arg(t).arg(kickmsg); + } + break; + case Message::Nick: + s = tr("%Dr<->"); + if(nick == msg.contents()) t = tr("%DrYou are now known as %DN%1%DN").arg(txt); + else t = tr("%Dr%DN%1%DN is now known as %DN%2%DN").arg(nick, txt); + break; + case Message::Mode: + s = tr("%Dm***"); + if(nick.isEmpty()) t = tr("%DmUser mode: %DM%1%DM").arg(txt); + else t = tr("%DmMode %DM%1%DM by %DN%2%DN").arg(txt, nick); + break; + case Message::Action: + s = tr("%Da-*-"); + t = tr("%Da%DN%1%DN %2").arg(nick).arg(txt); + break; + default: + s = tr("%De%1").arg(msg.sender()); + t = tr("%De[%1]").arg(txt); + } + result.sender = styleString(s); + result.contents = styleString(t); return result; } + +QDataStream &operator<<(QDataStream &out, const UiStyle::FormatList &formatList) { + out << formatList.count(); + UiStyle::FormatList::const_iterator it = formatList.begin(); + while(it != formatList.end()) { + out << (*it).first << (*it).second; + ++it; + } + return out; +} + +QDataStream &operator>>(QDataStream &in, UiStyle::FormatList &formatList) { + quint16 cnt; + in >> cnt; + for(quint16 i = 0; i < cnt; i++) { + quint16 pos; quint32 ftype; + in >> pos >> ftype; + formatList.append(qMakePair((quint16)pos, ftype)); + } + return in; +}