Add accessor to check if a filter accepts a given BufferId
[quassel.git] / src / client / messagemodel.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-08 by the Quassel IRC Team                         *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
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.                                           *
9  *                                                                         *
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.                          *
14  *                                                                         *
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  ***************************************************************************/
20
21 #include "messagemodel.h"
22
23 #include "message.h"
24
25 #include <QEvent>
26
27 class ProcessBufferEvent : public QEvent {
28 public:
29   inline ProcessBufferEvent() : QEvent(QEvent::User) {}
30 };
31
32 MessageModel::MessageModel(QObject *parent)
33   : QAbstractItemModel(parent)
34 {
35 }
36
37 QVariant MessageModel::data(const QModelIndex &index, int role) const {
38   int row = index.row(); int column = index.column();
39   if(row < 0 || row >= _messageList.count() || column < 0)
40     return QVariant();
41
42   if(role == ColumnTypeRole)
43     return column;
44
45   return _messageList[row]->data(index.column(), role);
46 }
47
48 bool MessageModel::setData(const QModelIndex &index, const QVariant &value, int role) {
49   int row = index.row();
50   if(row < 0 || row >= _messageList.count())
51     return false;
52
53   if(_messageList[row]->setData(index.column(), value, role)) {
54     emit dataChanged(index, index);
55     return true;
56   }
57
58   return false;
59 }
60
61
62
63 bool MessageModel::insertMessage(const Message &msg, bool fakeMsg) {
64   MsgId id = msg.msgId();
65   int idx = indexForId(id);
66   if(!fakeMsg && idx < _messageList.count()) { // check for duplicate
67     if(_messageList[idx]->msgId() == id)
68       return false;
69   }
70
71   MessageModelItem *item = createMessageModelItem(msg);
72   beginInsertRows(QModelIndex(), idx, idx);
73   _messageList.insert(idx, item);
74   endInsertRows();
75   return true;
76 }
77
78 void MessageModel::insertMessages(const QList<Message> &msglist) {
79   if(msglist.isEmpty())
80     return;
81
82   if(_messageList.isEmpty()) {
83     insertMessageGroup(msglist);
84   } else {
85     int processedMsgs = insertMessagesGracefully(msglist);
86     int remainingMsgs = msglist.count() - processedMsgs;
87     if(remainingMsgs > 0) {
88       if(msglist.first().msgId() < msglist.last().msgId()) {
89         // in Order
90         _messageBuffer << msglist.mid(0, remainingMsgs);
91       } else {
92         _messageBuffer << msglist.mid(processedMsgs);
93       }
94       qSort(_messageBuffer);
95       QCoreApplication::postEvent(this, new ProcessBufferEvent());
96     }
97   }
98 }
99
100 void MessageModel::insertMessageGroup(const QList<Message> &msglist) {
101   int idx = indexForId(msglist.first().msgId());
102   beginInsertRows(QModelIndex(), idx, idx+msglist.count()-1);
103
104   foreach(Message msg, msglist) {
105     _messageList.insert(idx, createMessageModelItem(msg));
106     idx++;
107   }
108
109   endInsertRows();
110 }
111
112 int MessageModel::insertMessagesGracefully(const QList<Message> &msglist) {
113   bool inOrder = (msglist.first().msgId() < msglist.last().msgId());
114   // depending on the order we have to traverse from the front to the back or vice versa
115
116   QList<Message> grouplist;
117   MsgId id;
118   MsgId dupeId;
119   int dupeCount = 0;
120   bool fastForward = false;
121   QList<Message>::const_iterator iter;
122   if(inOrder) {
123     iter = msglist.constEnd();
124     iter--; // this op is safe as we've allready passed an empty check
125   } else {
126     iter = msglist.constBegin();
127   }
128
129   int idx = indexForId((*iter).msgId());
130   if(idx >= 0)
131     dupeId = _messageList[idx]->msgId();
132
133   // we always compare to the previous entry...
134   // if there isn't, we can fastforward to the top
135   if(idx - 1 >= 0)
136     id = _messageList[idx - 1]->msgId();
137   else
138     fastForward = true;
139
140   if((*iter).msgId() != dupeId)
141     grouplist << *iter;
142   else
143     dupeCount++;
144
145   if(!inOrder)
146     iter++;
147
148   if(inOrder) {
149     while(iter != msglist.constBegin()) {
150       iter--;
151
152       if(!fastForward && (*iter).msgId() < id)
153         break;
154
155       if((*iter).msgId() != dupeId)
156         grouplist.prepend(*iter);
157       else
158         dupeCount++;
159     }
160   } else {
161     while(iter != msglist.constEnd()) {
162       if(!fastForward && (*iter).msgId() < id)
163         break;
164
165       if((*iter).msgId() != dupeId)
166         grouplist.prepend(*iter);
167       else
168         dupeCount++;
169
170       iter++;
171     }
172   }
173
174   insertMessageGroup(grouplist);
175   return grouplist.count() + dupeCount;
176 }
177
178 void MessageModel::customEvent(QEvent *event) {
179   if(event->type() != QEvent::User)
180     return;
181
182   event->accept();
183
184   if(_messageBuffer.isEmpty())
185     return;
186
187   int processedMsgs = insertMessagesGracefully(_messageBuffer);
188   int remainingMsgs = _messageBuffer.count() - processedMsgs;
189
190   QList<Message>::iterator removeStart = _messageBuffer.begin() + remainingMsgs;
191   QList<Message>::iterator removeEnd = _messageBuffer.end();
192   _messageBuffer.erase(removeStart, removeEnd);
193
194   if(!_messageBuffer.isEmpty())
195     QCoreApplication::postEvent(this, new ProcessBufferEvent());
196 }
197
198 void MessageModel::clear() {
199   beginRemoveRows(QModelIndex(), 0, rowCount() - 1);
200   qDeleteAll(_messageList);
201   _messageList.clear();
202   endRemoveRows();
203 }
204
205
206
207 // returns index of msg with given Id or of the next message after that (i.e., the index where we'd insert this msg)
208 int MessageModel::indexForId(MsgId id) {
209   if(_messageList.isEmpty() || id <= _messageList.value(0)->data(0, MsgIdRole).value<MsgId>()) return 0;
210   if(id > _messageList.last()->data(0, MsgIdRole).value<MsgId>()) return _messageList.count();
211   // binary search
212   int start = 0; int end = _messageList.count()-1;
213   while(1) {
214     if(end - start == 1) return end;
215     int pivot = (end + start) / 2;
216     if(id <= _messageList.value(pivot)->data(0, MsgIdRole).value<MsgId>()) end = pivot;
217     else start = pivot;
218   }
219 }
220
221 /**********************************************************************************/
222
223 MessageModelItem::MessageModelItem(const Message &msg) :
224   _timestamp(msg.timestamp()),
225   _msgId(msg.msgId()),
226   _bufferId(msg.bufferInfo().bufferId()),
227   _type(msg.type()),
228   _flags(msg.flags())
229 {
230 }
231
232 QVariant MessageModelItem::data(int column, int role) const {
233   if(column < MessageModel::TimestampColumn || column > MessageModel::ContentsColumn)
234     return QVariant();
235
236   switch(role) {
237   case MessageModel::MsgIdRole: return QVariant::fromValue<MsgId>(_msgId);
238   case MessageModel::BufferIdRole: return QVariant::fromValue<BufferId>(_bufferId);
239   case MessageModel::TypeRole: return _type;
240   case MessageModel::FlagsRole: return (int)_flags;
241   case MessageModel::TimestampRole: return _timestamp;
242   default: return QVariant();
243   }
244 }
245
246
247 // Stuff for later
248 bool MessageModelItem::lessThan(const MessageModelItem *m1, const MessageModelItem *m2){
249   return (*m1) < (*m2);
250 }
251
252 bool MessageModelItem::operator<(const MessageModelItem &other) const {
253   return _msgId < other._msgId;
254 }
255
256 bool MessageModelItem::operator==(const MessageModelItem &other) const {
257   return _msgId == other._msgId;
258 }
259
260 bool MessageModelItem::operator>(const MessageModelItem &other) const {
261   return _msgId > other._msgId;
262 }