// PRIVATE DATA FOR CHATLINE MODEL ITEM
class ChatLineModelItemPrivate {
- struct ChatLinePart {
- QString plainText;
- UiStyle::FormatList formatList;
- inline ChatLinePart(const QString &pT, const UiStyle::FormatList &fL) : plainText(pT), formatList(fL) {}
- };
public:
- inline ChatLineModelItemPrivate(const Message &msg) : _msgBuffer(new Message(msg)), timestamp(0), sender(0), contents(0) {}
- inline ~ChatLineModelItemPrivate() {
+ inline ChatLineModelItemPrivate(const Message &msg) : _msgBuffer(new Message(msg)), _styledMsg(0) {}
+ ~ChatLineModelItemPrivate() {
if(_msgBuffer) {
delete _msgBuffer;
} else {
- delete timestamp;
- delete sender;
- delete contents;
+ delete _styledMsg;
}
}
inline bool needsStyling() { return (bool)_msgBuffer; }
- inline ChatLinePart *partByColumn(MessageModel::ColumnType column) {
- switch(column) {
- case ChatLineModel::TimestampColumn:
- return timestamp;
- case ChatLineModel::SenderColumn:
- return sender;
- case ChatLineModel::ContentsColumn:
- return contents;
- default:
- Q_ASSERT(false);
- return 0;
- }
- }
-
- inline const QString &plainText(MessageModel::ColumnType column) {
+ QVariant data(MessageModel::ColumnType column, int role) {
if(needsStyling())
style();
- return partByColumn(column)->plainText;
- }
-
- inline const UiStyle::FormatList &formatList(MessageModel::ColumnType column) {
- if(needsStyling())
- style();
- return partByColumn(column)->formatList;
- }
-
- inline const ChatLineModel::WrapList &wrapList() {
- if(needsStyling())
- style();
- if(_wrapList.isEmpty())
- computeWrapList();
- return _wrapList;
+ switch(column) {
+ case ChatLineModel::TimestampColumn:
+ switch(role) {
+ case ChatLineModel::DisplayRole:
+ return _styledMsg->decoratedTimestamp();
+ case ChatLineModel::EditRole:
+ return _styledMsg->timestamp();
+ case ChatLineModel::FormatRole:
+ return QVariant::fromValue<UiStyle::FormatList>(UiStyle::FormatList()
+ << qMakePair((quint16)0, (quint32)_styledMsg->timestampFormat()));
+ }
+ break;
+ case ChatLineModel::SenderColumn:
+ switch(role) {
+ case ChatLineModel::DisplayRole:
+ return _styledMsg->decoratedSender();
+ case ChatLineModel::EditRole:
+ return _styledMsg->sender();
+ case ChatLineModel::FormatRole:
+ return QVariant::fromValue<UiStyle::FormatList>(UiStyle::FormatList()
+ << qMakePair((quint16)0, (quint32)_styledMsg->senderFormat()));
+ }
+ break;
+ case ChatLineModel::ContentsColumn:
+ switch(role) {
+ case ChatLineModel::DisplayRole:
+ case ChatLineModel::EditRole:
+ return _styledMsg->contents();
+ case ChatLineModel::FormatRole:
+ return QVariant::fromValue<UiStyle::FormatList>(_styledMsg->contentsFormatList());
+ case ChatLineModel::WrapListRole:
+ if(_wrapList.isEmpty())
+ computeWrapList();
+ return QVariant::fromValue<ChatLineModel::WrapList>(_wrapList);
+ }
+ break;
+ default:
+ Q_ASSERT(false);
+ return 0;
+ }
+ return QVariant();
}
private:
- inline void style() {
- QtUiStyle::StyledMessage m = QtUi::style()->styleMessage(*_msgBuffer);
-
- timestamp = new ChatLinePart(m.timestamp.plainText, m.timestamp.formatList);
- sender = new ChatLinePart(m.sender.plainText, m.sender.formatList);
- contents = new ChatLinePart(m.contents.plainText, m.contents.formatList);
+ void style() {
+ _styledMsg = new QtUiStyle::StyledMessage(QtUi::style()->styleMessage(*_msgBuffer));
delete _msgBuffer;
_msgBuffer = 0;
}
- inline void computeWrapList() {
- if(contents->plainText.isEmpty())
+ void computeWrapList() {
+ int length = _styledMsg->contents().length();
+ if(!length)
return;
enum Mode { SearchStart, SearchEnd };
QList<ChatLineModel::Word> wplist; // use a temp list which we'll later copy into a QVector for efficiency
- QTextBoundaryFinder finder(QTextBoundaryFinder::Word, contents->plainText.unicode(), contents->plainText.length(), TextBoundaryFinderBuffer, TextBoundaryFinderBufferSize);
+ QTextBoundaryFinder finder(QTextBoundaryFinder::Word, _styledMsg->contents().unicode(), length,
+ TextBoundaryFinderBuffer, TextBoundaryFinderBufferSize);
int idx;
int oldidx = 0;
word.start = 0;
qreal wordstartx = 0;
- QTextLayout layout(contents->plainText);
+ QTextLayout layout(_styledMsg->contents());
QTextOption option;
option.setWrapMode(QTextOption::NoWrap);
layout.setTextOption(option);
- layout.setAdditionalFormats(QtUi::style()->toTextLayoutList(contents->formatList, contents->plainText.length()));
+ layout.setAdditionalFormats(QtUi::style()->toTextLayoutList(_styledMsg->contentsFormatList(), length));
layout.beginLayout();
QTextLine line = layout.createLine();
- line.setNumColumns(contents->plainText.length());
+ line.setNumColumns(length);
layout.endLayout();
do {
idx = finder.toNextBoundary();
if(idx < 0) {
- idx = contents->plainText.length();
- wordStart = false;
- wordEnd = false;
- mode = SearchStart;
+ idx = length;
+ wordStart = false;
+ wordEnd = false;
+ mode = SearchStart;
} else {
- wordStart = finder.boundaryReasons().testFlag(QTextBoundaryFinder::StartWord);
- wordEnd = finder.boundaryReasons().testFlag(QTextBoundaryFinder::EndWord);
+ wordStart = finder.boundaryReasons().testFlag(QTextBoundaryFinder::StartWord);
+ wordEnd = finder.boundaryReasons().testFlag(QTextBoundaryFinder::EndWord);
}
//if(flg) qDebug() << idx << mode << wordStart << wordEnd << contents->plainText.left(idx) << contents->plainText.mid(idx);
if(mode == SearchEnd || (!wordStart && wordEnd)) {
- if(wordStart || !wordEnd) continue;
- oldidx = idx;
- mode = SearchStart;
- continue;
+ if(wordStart || !wordEnd) continue;
+ oldidx = idx;
+ mode = SearchStart;
+ continue;
}
qreal wordendx = line.cursorToX(oldidx);
qreal trailingendx = line.cursorToX(idx);
wplist.append(word);
if(wordStart) {
- word.start = idx;
- mode = SearchEnd;
+ word.start = idx;
+ mode = SearchEnd;
}
// the part " || (finder.position() == contents->plainText.length())" shouldn't be necessary
// but in rare and indeterministic cases Qt states that the end of the text is not a boundary o_O
- } while(finder.isAtBoundary() || (finder.position() == contents->plainText.length()));
+ } while(finder.isAtBoundary() || (finder.position() == length));
// A QVector needs less space than a QList
_wrapList.resize(wplist.count());
ChatLineModel::WrapList _wrapList;
Message *_msgBuffer;
- ChatLinePart *timestamp, *sender, *contents;
+ UiStyle::StyledMessage *_styledMsg;
static unsigned char *TextBoundaryFinderBuffer;
static int TextBoundaryFinderBufferSize;
}
QVariant ChatLineModelItem::data(int column, int role) const {
- if(column < ChatLineModel::TimestampColumn || column > ChatLineModel::ContentsColumn)
- return MessageModelItem::data(column, role);
- MessageModel::ColumnType columnType = (MessageModel::ColumnType)column;
-
- switch(role) {
- case ChatLineModel::DisplayRole:
- return _data->plainText(columnType);
- case ChatLineModel::FormatRole:
- return QVariant::fromValue<UiStyle::FormatList>(_data->formatList(columnType));
- case ChatLineModel::WrapListRole:
- if(columnType != ChatLineModel::ContentsColumn)
- return QVariant();
- return QVariant::fromValue<ChatLineModel::WrapList>(_data->wrapList());
- }
- return MessageModelItem::data(column, role);
+ QVariant d = _data->data((MessageModel::ColumnType)column, role);
+ if(!d.isValid()) return MessageModelItem::data(column, role);
+ return d;
}
-
Message::Type messageType = (Message::Type)sourceModel()->data(source_index, MessageModel::TypeRole).toInt();
if(messageType & (Message::Plain | Message::Notice)) {
- QString sender = MessageFilter::data(index, role).toString();
- // we have to strip leading and traling < / >
- fields << sender.mid(1, sender.count() - 2);
+ QString sender = MessageFilter::data(index, ChatLineModel::EditRole).toString();
+ fields << sender;
}
return QString("<%1>").arg(fields.join(":"));
}
}
UiStyle::StyledMessage UiStyle::styleMessage(const Message &msg) {
+ return StyledMessage(msg, this);
+}
+
+/***********************************************************************************/
+
+UiStyle::StyledMessage::StyledMessage(const Message &msg, UiStyle *style) {
QString user = userFromMask(msg.sender());
QString host = hostFromMask(msg.sender());
QString nick = nickFromMask(msg.sender());
- QString txt = mircToInternal(msg.contents());
+ QString txt = style->mircToInternal(msg.contents());
QString bufferName = msg.bufferInfo().bufferName();
bufferName.replace('%', "%%"); // well, you _can_ have a % in a buffername apparently... -_-
- StyledMessage result;
-
- result.timestamp = styleString(tr("%DT[%1]").arg(msg.timestamp().toLocalTime().toString("hh:mm:ss")));
+ _msgType = msg.type();
+ _timestamp = msg.timestamp();
- QString s, t;
+ QString t;
switch(msg.type()) {
case Message::Plain:
- s = tr("%DS<%1>").arg(nick); t = tr("%D0%1").arg(txt); break;
+ _sender = nick;
+ t = tr("%D0%1").arg(txt); break;
case Message::Notice:
- s = tr("%Dn[%1]").arg(nick); t = tr("%Dn%1").arg(txt); break;
+ _sender = nick;
+ t = tr("%Dn%1").arg(txt); break;
case Message::Server:
- s = tr("%Ds*"); t = tr("%Ds%1").arg(txt); break;
+ t = tr("%Ds%1").arg(txt); break;
case Message::Error:
- s = tr("%De*"); t = tr("%De%1").arg(txt); break;
+ 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;
+ 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);
+ 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%1%DN %DH(%2@%3)%DH has quit").arg(nick, user, host);
+ t = tr("%Dq%DN%1%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<-*");
+ case Message::Kick: {
QString victim = txt.section(" ", 0, 0);
//if(victim == ui.ownNick->currentText()) victim = tr("you");
QString kickmsg = txt.section(" ", 1);
}
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());
+ _sender = msg.sender();
t = tr("%De[%1]").arg(txt);
}
- result.sender = styleString(s);
- result.contents = styleString(t);
- return result;
+ _contents = style->styleString(t);
+}
+
+QDateTime UiStyle::StyledMessage::timestamp() const {
+ return _timestamp;
+}
+
+QString UiStyle::StyledMessage::decoratedTimestamp() const {
+ return QString("[%1]").arg(_timestamp.toLocalTime().toString("hh:mm:ss"));
}
+QString UiStyle::StyledMessage::sender() const {
+ switch(type()) {
+ case Message::Plain:
+ case Message::Notice:
+ return _sender;
+ default:
+ return QString();
+ }
+}
+
+QString UiStyle::StyledMessage::decoratedSender() const {
+ switch(type()) {
+ case Message::Plain:
+ return tr("<%1>").arg(_sender); break;
+ case Message::Notice:
+ return tr("[%1]").arg(_sender); break;
+ case Message::Server:
+ return tr("*"); break;
+ case Message::Error:
+ return tr("*"); break;
+ case Message::Join:
+ return tr("-->"); break;
+ case Message::Part:
+ return tr("<--"); break;
+ case Message::Quit:
+ return tr("<--"); break;
+ case Message::Kick:
+ return tr("<-*"); break;
+ case Message::Nick:
+ return tr("<->"); break;
+ case Message::Mode:
+ return tr("***"); break;
+ case Message::Action:
+ return tr("-*-"); break;
+ default:
+ return tr("%1").arg(_sender);
+ }
+}
+
+QString UiStyle::StyledMessage::contents() const {
+ return _contents.plainText;
+}
+
+UiStyle::FormatType UiStyle::StyledMessage::timestampFormat() const {
+ return UiStyle::Timestamp;
+}
+
+UiStyle::FormatType UiStyle::StyledMessage::senderFormat() const {
+ switch(type()) {
+ case Message::Plain:
+ return UiStyle::Sender; break;
+ case Message::Notice:
+ return UiStyle::NoticeMsg; break;
+ case Message::Server:
+ return UiStyle::ServerMsg; break;
+ case Message::Error:
+ return UiStyle::ErrorMsg; break;
+ case Message::Join:
+ return UiStyle::JoinMsg; break;
+ case Message::Part:
+ return UiStyle::PartMsg; break;
+ case Message::Quit:
+ return UiStyle::QuitMsg; break;
+ case Message::Kick:
+ return UiStyle::KickMsg; break;
+ case Message::Nick:
+ return UiStyle::RenameMsg; break;
+ case Message::Mode:
+ return UiStyle::ModeMsg; break;
+ case Message::Action:
+ return UiStyle::ActionMsg; break;
+ default:
+ return UiStyle::ErrorMsg;
+ }
+}
+
+UiStyle::FormatList UiStyle::StyledMessage::contentsFormatList() const {
+ return _contents.formatList;
+}
+
+/***********************************************************************************/
+
QDataStream &operator<<(QDataStream &out, const UiStyle::FormatList &formatList) {
out << formatList.count();
UiStyle::FormatList::const_iterator it = formatList.begin();
#include "settings.h"
class UiStyle {
- Q_DECLARE_TR_FUNCTIONS (UiStyle)
+ Q_DECLARE_TR_FUNCTIONS(UiStyle)
public:
UiStyle(const QString &settingsKey);
FormatList formatList; // starting pos, ftypes
};
- struct StyledMessage {
- StyledString timestamp;
- StyledString sender;
- StyledString contents;
- };
+ class StyledMessage;
StyledString styleString(const QString &);
StyledMessage styleMessage(const Message &);
QString _settingsKey;
};
+class UiStyle::StyledMessage {
+
+ public:
+ explicit StyledMessage(const Message &, UiStyle *style);
+
+ QDateTime timestamp() const;
+ QString decoratedTimestamp() const;
+ QString sender() const; //!< Nickname (no decorations) for Plain and Notice, empty else
+ QString decoratedSender() const;
+ QString contents() const;
+
+ FormatType timestampFormat() const;
+ FormatType senderFormat() const;
+ FormatList contentsFormatList() const;
+
+ inline Message::Type type() const { return _msgType; }
+
+ private:
+ StyledString _contents;
+ QDateTime _timestamp;
+ QString _sender;
+ Message::Type _msgType;
+};
+
QDataStream &operator<<(QDataStream &out, const UiStyle::FormatList &formatList);
QDataStream &operator>>(QDataStream &in, UiStyle::FormatList &formatList);