1 /***************************************************************************
2 * Copyright (C) 2005-08 by the Quassel IRC Team *
3 * devel@quassel-irc.org *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) version 3. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 #include "messagemodel.h"
27 class ProcessBufferEvent : public QEvent {
29 inline ProcessBufferEvent() : QEvent(QEvent::User) {}
32 MessageModel::MessageModel(QObject *parent)
33 : QAbstractItemModel(parent)
35 QDateTime now = QDateTime::currentDateTime();
36 now.setTimeSpec(Qt::UTC);
37 _nextDayChange.setTimeSpec(Qt::UTC);
38 _nextDayChange.setTime_t(((now.toTime_t() / 86400) + 1) * 86400);
39 _nextDayChange.setTimeSpec(Qt::LocalTime);
40 qDebug() << _nextDayChange;
41 _dayChangeTimer.setInterval(QDateTime::currentDateTime().secsTo(_nextDayChange) * 1000);
42 _dayChangeTimer.start();
43 connect(&_dayChangeTimer, SIGNAL(timeout()), this, SLOT(changeOfDay()));
46 QVariant MessageModel::data(const QModelIndex &index, int role) const {
47 int row = index.row(); int column = index.column();
48 if(row < 0 || row >= _messageList.count() || column < 0)
51 if(role == ColumnTypeRole)
54 return _messageList[row]->data(index.column(), role);
57 bool MessageModel::setData(const QModelIndex &index, const QVariant &value, int role) {
58 int row = index.row();
59 if(row < 0 || row >= _messageList.count())
62 if(_messageList[row]->setData(index.column(), value, role)) {
63 emit dataChanged(index, index);
72 bool MessageModel::insertMessage(const Message &msg, bool fakeMsg) {
73 MsgId id = msg.msgId();
74 int idx = indexForId(id);
75 if(!fakeMsg && idx < _messageList.count()) { // check for duplicate
76 if(_messageList[idx]->msgId() == id)
80 MessageModelItem *item = createMessageModelItem(msg);
81 beginInsertRows(QModelIndex(), idx, idx);
82 _messageList.insert(idx, item);
87 void MessageModel::insertMessages(const QList<Message> &msglist) {
91 int processedMsgs = insertMessagesGracefully(msglist);
92 int remainingMsgs = msglist.count() - processedMsgs;
93 if(remainingMsgs > 0) {
94 if(msglist.first().msgId() < msglist.last().msgId()) {
96 _messageBuffer << msglist.mid(0, remainingMsgs);
98 _messageBuffer << msglist.mid(processedMsgs);
100 qSort(_messageBuffer);
101 QCoreApplication::postEvent(this, new ProcessBufferEvent());
105 void MessageModel::insertMessageGroup(const QList<Message> &msglist) {
106 int idx = indexForId(msglist.first().msgId());
108 int prevIdx = idx - 1;
109 if(_messageList[prevIdx]->msgType() == Message::DayChange
110 && _messageList[prevIdx]->timeStamp() > msglist.value(0).timestamp()) {
111 beginRemoveRows(QModelIndex(), prevIdx, prevIdx);
112 MessageModelItem *oldItem = _messageList.takeAt(prevIdx);
118 Message dayChangeMsg;
119 bool needsDayChangeMsg = false;
120 if(idx < _messageList.count() && _messageList[idx]->msgType() != Message::DayChange) {
121 QDateTime nextTs = _messageList[idx]->timeStamp();
122 QDateTime prevTs = msglist.last().timestamp();
123 nextTs.setTimeSpec(Qt::UTC);
124 prevTs.setTimeSpec(Qt::UTC);
125 uint nextDay = nextTs.toTime_t() / 86400;
126 uint prevDay = prevTs.toTime_t() / 86400;
127 if(nextDay != prevDay) {
128 nextTs.setTime_t(nextDay * 86400);
129 nextTs.setTimeSpec(Qt::LocalTime);
130 dayChangeMsg = Message::ChangeOfDay(nextTs);
131 dayChangeMsg.setMsgId(msglist.last().msgId());
132 needsDayChangeMsg = true;
136 int end = idx + msglist.count() - 1;
137 beginInsertRows(QModelIndex(), start, end);
138 foreach(Message msg, msglist) {
139 _messageList.insert(idx, createMessageModelItem(msg));
142 if(needsDayChangeMsg) {
143 _messageList.insert(idx, createMessageModelItem(dayChangeMsg));
148 int MessageModel::insertMessagesGracefully(const QList<Message> &msglist) {
149 bool inOrder = (msglist.first().msgId() < msglist.last().msgId());
150 // depending on the order we have to traverse from the front to the back or vice versa
152 QList<Message> grouplist;
156 bool fastForward = false;
157 QList<Message>::const_iterator iter;
159 iter = msglist.constEnd();
160 iter--; // this op is safe as we've allready passed an empty check
162 iter = msglist.constBegin();
165 int idx = indexForId((*iter).msgId());
166 if(idx >= 0 && !_messageList.isEmpty())
167 dupeId = _messageList[idx]->msgId();
169 // we always compare to the previous entry...
170 // if there isn't, we can fastforward to the top
172 id = _messageList[idx - 1]->msgId();
176 if((*iter).msgId() != dupeId)
185 while(iter != msglist.constBegin()) {
188 if(!fastForward && (*iter).msgId() < id)
191 if((*iter).msgId() != dupeId) {
192 if(!grouplist.isEmpty()) {
193 QDateTime nextTs = grouplist.value(0).timestamp();
194 QDateTime prevTs = (*iter).timestamp();
195 nextTs.setTimeSpec(Qt::UTC);
196 prevTs.setTimeSpec(Qt::UTC);
197 uint nextDay = nextTs.toTime_t() / 86400;
198 uint prevDay = prevTs.toTime_t() / 86400;
199 if(nextDay != prevDay) {
200 nextTs.setTime_t(nextDay * 86400);
201 nextTs.setTimeSpec(Qt::LocalTime);
202 Message dayChangeMsg = Message::ChangeOfDay(nextTs);
203 dayChangeMsg.setMsgId((*iter).msgId());
204 grouplist.prepend(dayChangeMsg);
208 grouplist.prepend(*iter);
214 while(iter != msglist.constEnd()) {
215 if(!fastForward && (*iter).msgId() < id)
218 if((*iter).msgId() != dupeId) {
219 if(!grouplist.isEmpty()) {
220 QDateTime nextTs = grouplist.value(0).timestamp();
221 QDateTime prevTs = (*iter).timestamp();
222 nextTs.setTimeSpec(Qt::UTC);
223 prevTs.setTimeSpec(Qt::UTC);
224 uint nextDay = nextTs.toTime_t() / 86400;
225 uint prevDay = prevTs.toTime_t() / 86400;
226 if(nextDay != prevDay) {
227 nextTs.setTime_t(nextDay * 86400);
228 nextTs.setTimeSpec(Qt::LocalTime);
229 Message dayChangeMsg = Message::ChangeOfDay(nextTs);
230 dayChangeMsg.setMsgId((*iter).msgId());
231 grouplist.prepend(dayChangeMsg);
235 grouplist.prepend(*iter);
244 insertMessageGroup(grouplist);
245 return grouplist.count() + dupeCount;
248 void MessageModel::customEvent(QEvent *event) {
249 if(event->type() != QEvent::User)
254 if(_messageBuffer.isEmpty())
257 int processedMsgs = insertMessagesGracefully(_messageBuffer);
258 int remainingMsgs = _messageBuffer.count() - processedMsgs;
260 QList<Message>::iterator removeStart = _messageBuffer.begin() + remainingMsgs;
261 QList<Message>::iterator removeEnd = _messageBuffer.end();
262 _messageBuffer.erase(removeStart, removeEnd);
264 if(!_messageBuffer.isEmpty())
265 QCoreApplication::postEvent(this, new ProcessBufferEvent());
268 void MessageModel::clear() {
269 beginRemoveRows(QModelIndex(), 0, rowCount() - 1);
270 qDeleteAll(_messageList);
271 _messageList.clear();
277 // returns index of msg with given Id or of the next message after that (i.e., the index where we'd insert this msg)
278 int MessageModel::indexForId(MsgId id) {
279 if(_messageList.isEmpty() || id <= _messageList.value(0)->msgId())
281 if(id > _messageList.last()->msgId())
282 return _messageList.count();
285 int start = 0; int end = _messageList.count()-1;
289 int pivot = (end + start) / 2;
290 if(id <= _messageList.value(pivot)->msgId()) end = pivot;
295 void MessageModel::changeOfDay() {
296 _dayChangeTimer.setInterval(86400000);
297 qDebug() << _nextDayChange;
298 if(!_messageList.isEmpty()) {
299 int idx = _messageList.count();
300 while(idx > 0 && _messageList[idx - 1]->timeStamp() > _nextDayChange) {
303 beginInsertRows(QModelIndex(), idx, idx);
304 Message dayChangeMsg = Message::ChangeOfDay(_nextDayChange);
305 dayChangeMsg.setMsgId(_messageList[idx - 1]->msgId());
306 _messageList.insert(idx, createMessageModelItem(dayChangeMsg));
309 _nextDayChange = _nextDayChange.addSecs(86400);
312 /**********************************************************************************/
314 MessageModelItem::MessageModelItem(const Message &msg) :
315 _timestamp(msg.timestamp()),
317 _bufferId(msg.bufferInfo().bufferId()),
323 QVariant MessageModelItem::data(int column, int role) const {
324 if(column < MessageModel::TimestampColumn || column > MessageModel::ContentsColumn)
328 case MessageModel::MsgIdRole: return QVariant::fromValue<MsgId>(_msgId);
329 case MessageModel::BufferIdRole: return QVariant::fromValue<BufferId>(_bufferId);
330 case MessageModel::TypeRole: return _type;
331 case MessageModel::FlagsRole: return (int)_flags;
332 case MessageModel::TimestampRole: return _timestamp;
333 default: return QVariant();
339 bool MessageModelItem::lessThan(const MessageModelItem *m1, const MessageModelItem *m2){
340 return (*m1) < (*m2);
343 bool MessageModelItem::operator<(const MessageModelItem &other) const {
344 return _msgId < other._msgId;
347 bool MessageModelItem::operator==(const MessageModelItem &other) const {
348 return _msgId == other._msgId;
351 bool MessageModelItem::operator>(const MessageModelItem &other) const {
352 return _msgId > other._msgId;