Also hopefully fixing backlog replay which was borked in some channels.
#include "message.h"
-MessageModel::MessageModel(QObject *parent) : QAbstractItemModel(parent) {
-
-
-
-}
-
-MessageModel::~MessageModel() {
-
-
+MessageModel::MessageModel(QObject *parent)
+ : QAbstractItemModel(parent)
+{
}
QVariant MessageModel::data(const QModelIndex &index, int role) const {
int row = index.row(); int column = index.column();
- if(row < 0 || row >= _messageList.count() || column < 0) return QVariant();
- if(role == ColumnTypeRole) return column;
+ if(row < 0 || row >= _messageList.count() || column < 0)
+ return QVariant();
+
+ if(role == ColumnTypeRole)
+ return column;
+
return _messageList[row]->data(index.column(), role);
}
bool MessageModel::setData(const QModelIndex &index, const QVariant &value, int role) {
int row = index.row();
- if(row < 0 || row >= _messageList.count()) return false;
+ if(row < 0 || row >= _messageList.count())
+ return false;
+
if(_messageList[row]->setData(index.column(), value, role)) {
emit dataChanged(index, index);
return true;
}
+
return false;
}
MsgId id = msg.msgId();
int idx = indexForId(id);
if(!fakeMsg && idx < _messageList.count()) { // check for duplicate
- if(_messageList.value(idx)->data(0, MsgIdRole).value<MsgId>() == id) {
+ if(_messageList[idx]->msgId() == id)
return false;
- }
}
+
MessageModelItem *item = createMessageModelItem(msg);
beginInsertRows(QModelIndex(), idx, idx);
_messageList.insert(idx, item);
void MessageModel::insertMessages(const QList<Message> &msglist) {
if(msglist.isEmpty()) return;
// FIXME make this more efficient by grouping msgs
- foreach(Message msg, msglist) insertMessage(msg);
-
+ foreach(Message msg, msglist)
+ insertMessage(msg);
}
void MessageModel::clear() {
- reset();
+ beginRemoveRows(QModelIndex(), 0, rowCount() - 1);
qDeleteAll(_messageList);
_messageList.clear();
+ endRemoveRows();
}
// returns index of msg with given Id or of the next message after that (i.e., the index where we'd insert this msg)
/**********************************************************************************/
-MessageModelItem::MessageModelItem(const Message &msg) {
- _timestamp = msg.timestamp();
- _msgId = msg.msgId();
- _bufferId = msg.bufferInfo().bufferId();
- _type = msg.type();
- _flags = msg.flags();
-
-}
-
-MessageModelItem::~MessageModelItem() {
-
+MessageModelItem::MessageModelItem(const Message &msg) :
+ _timestamp(msg.timestamp()),
+ _msgId(msg.msgId()),
+ _bufferId(msg.bufferInfo().bufferId()),
+ _type(msg.type()),
+ _flags(msg.flags())
+{
}
QVariant MessageModelItem::data(int column, int role) const {
- if(column < MessageModel::TimestampColumn || column > MessageModel::ContentsColumn) return QVariant();
+ if(column < MessageModel::TimestampColumn || column > MessageModel::ContentsColumn)
+ return QVariant();
+
switch(role) {
- case MessageModel::MsgIdRole: return QVariant::fromValue<MsgId>(_msgId);
- case MessageModel::BufferIdRole: return QVariant::fromValue<BufferId>(_bufferId);
- case MessageModel::TypeRole: return _type;
- case MessageModel::FlagsRole: return (int)_flags;
- case MessageModel::TimestampRole: return _timestamp;
- default: return QVariant();
+ case MessageModel::MsgIdRole: return QVariant::fromValue<MsgId>(_msgId);
+ case MessageModel::BufferIdRole: return QVariant::fromValue<BufferId>(_bufferId);
+ case MessageModel::TypeRole: return _type;
+ case MessageModel::FlagsRole: return (int)_flags;
+ case MessageModel::TimestampRole: return _timestamp;
+ default: return QVariant();
}
}
class MessageModel : public QAbstractItemModel {
Q_OBJECT
- public:
- enum MessageRole {
- DisplayRole = Qt::DisplayRole,
- EditRole = Qt::EditRole,
- MsgIdRole = Qt::UserRole,
- BufferIdRole,
- TypeRole,
- FlagsRole,
- TimestampRole,
- FormatRole,
- ColumnTypeRole,
- UserRole
- };
+public:
+ enum MessageRole {
+ DisplayRole = Qt::DisplayRole,
+ EditRole = Qt::EditRole,
+ MsgIdRole = Qt::UserRole,
+ BufferIdRole,
+ TypeRole,
+ FlagsRole,
+ TimestampRole,
+ FormatRole,
+ ColumnTypeRole,
+ UserRole
+ };
- enum ColumnType {
- TimestampColumn, SenderColumn, ContentsColumn, UserColumnType
- };
+ enum ColumnType {
+ TimestampColumn, SenderColumn, ContentsColumn, UserColumnType
+ };
- MessageModel(QObject *parent);
- virtual ~MessageModel();
+ MessageModel(QObject *parent);
- inline QModelIndex index(int row, int column, const QModelIndex &/*parent*/ = QModelIndex()) const { return createIndex(row, column); }
- inline QModelIndex parent(const QModelIndex &) const { return QModelIndex(); }
- inline int rowCount(const QModelIndex &/*parent*/ = QModelIndex()) const { return _messageList.count(); }
- inline int columnCount(const QModelIndex &/*parent*/ = QModelIndex()) const { return 3; }
+ inline QModelIndex index(int row, int column, const QModelIndex &/*parent*/ = QModelIndex()) const { return createIndex(row, column); }
+ inline QModelIndex parent(const QModelIndex &) const { return QModelIndex(); }
+ inline int rowCount(const QModelIndex &/*parent*/ = QModelIndex()) const { return _messageList.count(); }
+ inline int columnCount(const QModelIndex &/*parent*/ = QModelIndex()) const { return 3; }
- virtual QVariant data(const QModelIndex &index, int role) const;
- virtual bool setData(const QModelIndex &index, const QVariant &value, int role);
+ virtual QVariant data(const QModelIndex &index, int role) const;
+ virtual bool setData(const QModelIndex &index, const QVariant &value, int role);
- //virtual Qt::ItemFlags flags(const QModelIndex &index) const;
+ //virtual Qt::ItemFlags flags(const QModelIndex &index) const;
- bool insertMessage(const Message &, bool fakeMsg = false);
- void insertMessages(const QList<Message> &);
+ bool insertMessage(const Message &, bool fakeMsg = false);
+ void insertMessages(const QList<Message> &);
- void clear();
+ void clear();
- protected:
- virtual MessageModelItem *createMessageModelItem(const Message &) = 0;
+protected:
+ virtual MessageModelItem *createMessageModelItem(const Message &) = 0;
- private:
- QList<MessageModelItem *> _messageList;
-
- int indexForId(MsgId);
+private:
+ QList<MessageModelItem *> _messageList;
+ int indexForId(MsgId);
};
class MessageModelItem {
-
- public:
-
- //! Creates a MessageModelItem from a Message object.
- /** This baseclass implementation takes care of all Message data *except* the stylable strings.
- * Subclasses need to provide Qt::DisplayRole at least, which should describe the plaintext
- * strings without formattings (e.g. for searching purposes).
- */
- MessageModelItem(const Message &);
- virtual ~MessageModelItem();
-
- virtual QVariant data(int column, int role) const;
- virtual bool setData(int column, const QVariant &value, int role) = 0;
-
- private:
- QDateTime _timestamp;
- MsgId _msgId;
- BufferId _bufferId;
- Message::Type _type;
- Message::Flags _flags;
+public:
+ //! Creates a MessageModelItem from a Message object.
+ /** This baseclass implementation takes care of all Message data *except* the stylable strings.
+ * Subclasses need to provide Qt::DisplayRole at least, which should describe the plaintext
+ * strings without formattings (e.g. for searching purposes).
+ */
+ MessageModelItem(const Message &);
+ inline virtual ~MessageModelItem() {}
+
+ virtual QVariant data(int column, int role) const;
+ virtual bool setData(int column, const QVariant &value, int role) = 0;
+
+ inline const QDateTime &timeStamp() const { return _timestamp; }
+ inline MsgId msgId() const { return _msgId; }
+ inline BufferId bufferId() const { return _bufferId; }
+ inline Message::Type msgType() const { return _type; }
+ inline Message::Flags msgFlags() const { return _flags; }
+
+private:
+ QDateTime _timestamp;
+ MsgId _msgId;
+ BufferId _bufferId;
+ Message::Type _type;
+ Message::Flags _flags;
};
#endif
_singleBufferScene(false),
_selectingItem(0),
_selectionStart(-1),
- _isSelecting(false)
+ _isSelecting(false),
+ _lastBacklogSize(0)
{
MessageFilter *filter = qobject_cast<MessageFilter*>(model);
if(filter) {
connect(this, SIGNAL(sceneRectChanged(const QRectF &)), this, SLOT(rectChanged(const QRectF &)));
- connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(rowsInserted(const QModelIndex &, int, int)));
- connect(model, SIGNAL(modelAboutToBeReset()), this, SLOT(modelReset()));
+ connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+ this, SLOT(rowsInserted(const QModelIndex &, int, int)));
+ connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
+ this, SLOT(rowsAboutToBeRemoved(const QModelIndex &, int, int)));
+
for(int i = 0; i < model->rowCount(); i++) {
ChatLine *line = new ChatLine(i, model);
_lines.append(line);
}
ChatScene::~ChatScene() {
-
}
void ChatScene::rowsInserted(const QModelIndex &index, int start, int end) {
// TODO bulk inserts, iterators
qreal h = 0;
qreal y = 0;
- if(_width && start > 0) y = _lines.value(start - 1)->y() + _lines.value(start - 1)->height();
+ if(_width && start > 0)
+ y = _lines.value(start - 1)->y() + _lines.value(start - 1)->height();
+
for(int i = start; i <= end; i++) {
ChatLine *line = new ChatLine(i, model());
_lines.insert(i, line);
if(h > 0) {
_height += h;
for(int i = end+1; i < _lines.count(); i++) {
- _lines.value(i)->moveBy(0, h);
+ _lines.at(i)->moveBy(0, h);
}
setSceneRect(QRectF(0, 0, _width, _height));
emit heightChanged(_height);
}
}
-void ChatScene::modelReset() {
- foreach(ChatLine *line, _lines) {
- removeItem(line);
- delete line;
+void ChatScene::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) {
+ Q_UNUSED(parent);
+
+ qreal h = 0; // total height of removed items;
+
+ // remove items from scene
+ QList<ChatLine *>::iterator lineIter = _lines.begin() + start;
+ int lineCount = start;
+ while(lineIter != _lines.end() && lineCount <= end) {
+ h += (*lineIter)->height();
+ delete *lineIter;
+ lineIter = _lines.erase(lineIter);
+ lineCount++;
+ }
+
+ // update rows of remaining chatlines
+ for(int i = start; i < _lines.count(); i++) {
+ _lines.at(i)->setRow(i);
+ }
+
+ // update selection
+ if(_selectionStart >= 0) {
+ int offset = end - start + 1;
+ if(_selectionStart >= start)
+ _selectionStart -= offset;
+ if(_selectionEnd >= start)
+ _selectionEnd -= offset;
+ if(_firstSelectionRow >= start)
+ _firstSelectionRow -= offset;
+ if(_lastSelectionRow >= start)
+ _lastSelectionRow -= offset;
+ }
+
+ // reposition remaining chatlines
+ if(h > 0) {
+ Q_ASSERT(_height >= h);
+ _height -= h;
+ for(int i = start; i < _lines.count(); i++) {
+ _lines.at(i)->moveBy(0, -h);
+ }
+ setSceneRect(QRectF(0, 0, _width, _height));
+ emit heightChanged(_height);
}
- _lines.clear();
- setSceneRect(QRectF(0, 0, _width, 0));
- emit heightChanged(0);
}
void ChatScene::setWidth(qreal w) {
protected slots:
void rowsInserted(const QModelIndex &, int, int);
- void modelReset();
+ void rowsAboutToBeRemoved(const QModelIndex &, int, int);
private slots:
void rectChanged(const QRectF &);