Make StyledMessage more (space) efficient, clean up ChatlineModelItem
authorManuel Nickschas <sputnick@quassel-irc.org>
Fri, 3 Oct 2008 21:16:10 +0000 (23:16 +0200)
committerManuel Nickschas <sputnick@quassel-irc.org>
Sat, 4 Oct 2008 21:53:28 +0000 (23:53 +0200)
* We really don't need to store styled strings for sender and timestamp
* We use only one format for all timestamps and one for all senders, no need to duplicate
* EditRole now provides raw undecorated data (i.e. QDateTime for timestamp and nickname for sender)
* QDateTime is shorter than a stringified timestamp
* No need to store constant strings (for sender col in non-plain msgs) in every StyledMessage
* Could simplify ChatlineModelItem as a result

src/qtui/chatlinemodelitem.cpp
src/qtui/chatmonitorfilter.cpp
src/uisupport/uistyle.cpp
src/uisupport/uistyle.h

index 1e12cd1..993a76f 100644 (file)
@@ -39,80 +39,83 @@ typedef struct {
 
 // PRIVATE DATA FOR CHATLINE MODEL ITEM
 class ChatLineModelItemPrivate {
 
 // 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:
 
 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 {
     if(_msgBuffer) {
       delete _msgBuffer;
     } else {
-      delete timestamp;
-      delete sender;
-      delete contents;
+      delete _styledMsg;
     }
   }
 
   inline bool needsStyling() { return (bool)_msgBuffer; }
 
     }
   }
 
   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();
     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:
   }
 
 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;
   }
 
 
     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
       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;
 
     int idx;
     int oldidx = 0;
@@ -123,36 +126,36 @@ private:
     word.start = 0;
     qreal wordstartx = 0;
 
     word.start = 0;
     qreal wordstartx = 0;
 
-    QTextLayout layout(contents->plainText);
+    QTextLayout layout(_styledMsg->contents());
     QTextOption option;
     option.setWrapMode(QTextOption::NoWrap);
     layout.setTextOption(option);
 
     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();
     layout.beginLayout();
     QTextLine line = layout.createLine();
-    line.setNumColumns(contents->plainText.length());
+    line.setNumColumns(length);
     layout.endLayout();
 
     do {
       idx = finder.toNextBoundary();
       if(idx < 0) {
     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 {
       } 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(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);
       }
       qreal wordendx = line.cursorToX(oldidx);
       qreal trailingendx = line.cursorToX(idx);
@@ -163,12 +166,12 @@ private:
       wplist.append(word);
 
       if(wordStart) {
       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
       }
       // 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());
 
     // A QVector needs less space than a QList
     _wrapList.resize(wplist.count());
@@ -179,7 +182,7 @@ private:
 
   ChatLineModel::WrapList _wrapList;
   Message *_msgBuffer;
 
   ChatLineModel::WrapList _wrapList;
   Message *_msgBuffer;
-  ChatLinePart *timestamp, *sender, *contents;
+  UiStyle::StyledMessage *_styledMsg;
 
   static unsigned char *TextBoundaryFinderBuffer;
   static int TextBoundaryFinderBufferSize;
 
   static unsigned char *TextBoundaryFinderBuffer;
   static int TextBoundaryFinderBufferSize;
@@ -203,20 +206,7 @@ ChatLineModelItem::~ChatLineModelItem() {
 }
 
 QVariant ChatLineModelItem::data(int column, int role) const {
 }
 
 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;
 }
 }
-
index c93c235..547ea27 100644 (file)
@@ -72,9 +72,8 @@ QVariant ChatMonitorFilter::data(const QModelIndex &index, int role) const {
 
   Message::Type messageType = (Message::Type)sourceModel()->data(source_index, MessageModel::TypeRole).toInt();
   if(messageType & (Message::Plain | Message::Notice)) {
 
   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(":"));
 }
   }
   return QString("<%1>").arg(fields.join(":"));
 }
index 08190ed..b277438 100644 (file)
@@ -312,39 +312,45 @@ QString UiStyle::mircToInternal(const QString &mirc_) const {
 }
 
 UiStyle::StyledMessage UiStyle::styleMessage(const Message &msg) {
 }
 
 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 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... -_-
 
   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:
   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:
     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:
     case Message::Server:
-      s = tr("%Ds*"); t = tr("%Ds%1").arg(txt); break;
+      t = tr("%Ds%1").arg(txt); break;
     case Message::Error:
     case Message::Error:
-      s = tr("%De*"); t = tr("%De%1").arg(txt); break;
+      t = tr("%De%1").arg(txt); break;
     case Message::Join:
     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:
     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:
       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;
       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);
         QString victim = txt.section(" ", 0, 0);
         //if(victim == ui.ownNick->currentText()) victim = tr("you");
         QString kickmsg = txt.section(" ", 1);
@@ -353,28 +359,113 @@ UiStyle::StyledMessage UiStyle::styleMessage(const Message &msg) {
       }
       break;
     case Message::Nick:
       }
       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:
       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:
       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:
       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);
   }
       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();
 QDataStream &operator<<(QDataStream &out, const UiStyle::FormatList &formatList) {
   out << formatList.count();
   UiStyle::FormatList::const_iterator it = formatList.begin();
index 2aae59b..64ad506 100644 (file)
@@ -31,7 +31,7 @@
 #include "settings.h"
 
 class UiStyle {
 #include "settings.h"
 
 class UiStyle {
-  Q_DECLARE_TR_FUNCTIONS (UiStyle)
+  Q_DECLARE_TR_FUNCTIONS(UiStyle)
 
   public:
     UiStyle(const QString &settingsKey);
 
   public:
     UiStyle(const QString &settingsKey);
@@ -122,11 +122,7 @@ class UiStyle {
       FormatList formatList;  // starting pos, ftypes
     };
 
       FormatList formatList;  // starting pos, ftypes
     };
 
-    struct StyledMessage {
-      StyledString timestamp;
-      StyledString sender;
-      StyledString contents;
-    };
+    class StyledMessage;
 
     StyledString styleString(const QString &);
     StyledMessage styleMessage(const Message &);
 
     StyledString styleString(const QString &);
     StyledMessage styleMessage(const Message &);
@@ -160,6 +156,30 @@ class UiStyle {
     QString _settingsKey;
 };
 
     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);
 
 QDataStream &operator<<(QDataStream &out, const UiStyle::FormatList &formatList);
 QDataStream &operator>>(QDataStream &in, UiStyle::FormatList &formatList);