From 756158351d58ce25857f52589f5f8c42236a1c27 Mon Sep 17 00:00:00 2001 From: Marcus Eggenberger Date: Mon, 18 Aug 2008 20:37:06 +0200 Subject: [PATCH] ChatScene now properly react on aboutToRemoveRows(), which should improve stability for the chatmonitor after disconnects. Also hopefully fixing backlog replay which was borked in some channels. --- src/client/messagemodel.cpp | 70 +++++++++++------------ src/client/messagemodel.h | 110 ++++++++++++++++++------------------ src/qtui/chatscene.cpp | 66 +++++++++++++++++----- src/qtui/chatscene.h | 2 +- 4 files changed, 145 insertions(+), 103 deletions(-) diff --git a/src/client/messagemodel.cpp b/src/client/messagemodel.cpp index c6605de2..940dda20 100644 --- a/src/client/messagemodel.cpp +++ b/src/client/messagemodel.cpp @@ -22,31 +22,32 @@ #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; } @@ -54,10 +55,10 @@ bool MessageModel::insertMessage(const Message &msg, bool fakeMsg) { MsgId id = msg.msgId(); int idx = indexForId(id); if(!fakeMsg && idx < _messageList.count()) { // check for duplicate - if(_messageList.value(idx)->data(0, MsgIdRole).value() == id) { + if(_messageList[idx]->msgId() == id) return false; - } } + MessageModelItem *item = createMessageModelItem(msg); beginInsertRows(QModelIndex(), idx, idx); _messageList.insert(idx, item); @@ -68,14 +69,15 @@ bool MessageModel::insertMessage(const Message &msg, bool fakeMsg) { void MessageModel::insertMessages(const QList &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) @@ -94,28 +96,26 @@ int MessageModel::indexForId(MsgId id) { /**********************************************************************************/ -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); - case MessageModel::BufferIdRole: return QVariant::fromValue(_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); + case MessageModel::BufferIdRole: return QVariant::fromValue(_bufferId); + case MessageModel::TypeRole: return _type; + case MessageModel::FlagsRole: return (int)_flags; + case MessageModel::TimestampRole: return _timestamp; + default: return QVariant(); } } diff --git a/src/client/messagemodel.h b/src/client/messagemodel.h index c381f0f1..cc61c4f6 100644 --- a/src/client/messagemodel.h +++ b/src/client/messagemodel.h @@ -33,73 +33,75 @@ struct MsgId; 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 &); + bool insertMessage(const Message &, bool fakeMsg = false); + void insertMessages(const QList &); - void clear(); + void clear(); - protected: - virtual MessageModelItem *createMessageModelItem(const Message &) = 0; +protected: + virtual MessageModelItem *createMessageModelItem(const Message &) = 0; - private: - QList _messageList; - - int indexForId(MsgId); +private: + QList _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 diff --git a/src/qtui/chatscene.cpp b/src/qtui/chatscene.cpp index 67ce63c6..05d26c5e 100644 --- a/src/qtui/chatscene.cpp +++ b/src/qtui/chatscene.cpp @@ -45,7 +45,8 @@ ChatScene::ChatScene(QAbstractItemModel *model, const QString &idString, QObject _singleBufferScene(false), _selectingItem(0), _selectionStart(-1), - _isSelecting(false) + _isSelecting(false), + _lastBacklogSize(0) { MessageFilter *filter = qobject_cast(model); if(filter) { @@ -54,8 +55,11 @@ ChatScene::ChatScene(QAbstractItemModel *model, const QString &idString, QObject 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); @@ -86,7 +90,6 @@ ChatScene::ChatScene(QAbstractItemModel *model, const QString &idString, QObject } ChatScene::~ChatScene() { - } void ChatScene::rowsInserted(const QModelIndex &index, int start, int end) { @@ -96,7 +99,9 @@ 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); @@ -123,21 +128,56 @@ void ChatScene::rowsInserted(const QModelIndex &index, int start, int end) { 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::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) { diff --git a/src/qtui/chatscene.h b/src/qtui/chatscene.h index 1fee5c29..b454e1d6 100644 --- a/src/qtui/chatscene.h +++ b/src/qtui/chatscene.h @@ -70,7 +70,7 @@ class ChatScene : public QGraphicsScene { protected slots: void rowsInserted(const QModelIndex &, int, int); - void modelReset(); + void rowsAboutToBeRemoved(const QModelIndex &, int, int); private slots: void rectChanged(const QRectF &); -- 2.20.1