We now have back a real BufferModel. It's basically a ProxyModel to
[quassel.git] / src / client / networkmodel.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-08 by the Quassel Project                          *
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 <QColor>  // FIXME Dependency on QtGui!
22
23 #include "networkmodel.h"
24
25 #include <QAbstractItemView>
26
27 #include "bufferinfo.h"
28 #include "client.h"
29 #include "signalproxy.h"
30 #include "networkinfo.h"
31 #include "ircchannel.h"
32 #include "ircuser.h"
33
34 /*****************************************
35 *  Fancy Buffer Items
36 *****************************************/
37 BufferItem::BufferItem(Buffer *buffer, AbstractTreeItem *parent)
38   : PropertyMapItem(QStringList() << "bufferName" << "topic" << "nickCount", parent),
39     buf(buffer),
40     activity(Buffer::NoActivity)
41 {
42   Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
43   if(buf->bufferType() == Buffer::QueryType)
44     flags |= Qt::ItemIsDropEnabled;
45   setFlags(flags);
46 }
47
48 quint64 BufferItem::id() const {
49   return buf->bufferInfo().uid();
50 }
51
52 void BufferItem::setActivity(const Buffer::ActivityLevel &level) {
53   activity = level;
54 }
55
56 QColor BufferItem::foreground(int column) const {
57   Q_UNUSED(column)
58   // for the time beeing we ignore the column :)
59   if(activity & Buffer::Highlight) {
60     return QColor(Qt::red);
61   } else if(activity & Buffer::NewMessage) {
62     return QColor(Qt::darkYellow);
63   } else if(activity & Buffer::OtherActivity) {
64     return QColor(Qt::darkGreen);
65   } else {
66     if(buf->isActive())
67       return QColor(Qt::black);
68     else
69       return QColor(Qt::gray);
70   }
71 }
72
73 QVariant BufferItem::data(int column, int role) const {
74   switch(role) {
75   case NetworkModel::ItemTypeRole:
76     return NetworkModel::BufferItemType;
77   case NetworkModel::BufferUidRole:
78     return buf->bufferInfo().uid();
79   case NetworkModel::NetworkIdRole:
80     return buf->bufferInfo().networkId();
81   case NetworkModel::BufferTypeRole:
82     return int(buf->bufferType());
83   case NetworkModel::BufferActiveRole:
84     return buf->isActive();
85   case Qt::ForegroundRole:
86     return foreground(column);
87   default:
88     return PropertyMapItem::data(column, role);
89   }
90 }
91
92 void BufferItem::attachIrcChannel(IrcChannel *ircChannel) {
93   if(!ircChannel)
94     return;
95   
96   _ircChannel = ircChannel;
97
98   connect(ircChannel, SIGNAL(topicSet(QString)),
99           this, SLOT(setTopic(QString)));
100   connect(ircChannel, SIGNAL(ircUserJoined(IrcUser *)),
101           this, SLOT(join(IrcUser *)));
102   connect(ircChannel, SIGNAL(ircUserParted(IrcUser *)),
103           this, SLOT(part(IrcUser *)));
104 }
105
106 QString BufferItem::bufferName() const {
107   return buf->name();
108 }
109
110 QString BufferItem::topic() const {
111   if(_ircChannel)
112     return _ircChannel->topic();
113   else
114     return QString();
115 }
116
117 int BufferItem::nickCount() const {
118   if(_ircChannel)
119     return _ircChannel->ircUsers().count();
120   else
121     return 0;
122 }
123
124 void BufferItem::setTopic(const QString &topic) {
125   Q_UNUSED(topic);
126   emit dataChanged(1);
127 }
128
129 void BufferItem::join(IrcUser *ircUser) {
130   emit newChild(new IrcUserItem(ircUser, this));
131   emit dataChanged(2);
132 }
133
134 void BufferItem::part(IrcUser *ircUser) {
135   Q_UNUSED(ircUser);
136   emit dataChanged(2);
137 }
138
139 /*****************************************
140 *  Network Items
141 *****************************************/
142 NetworkItem::NetworkItem(const uint &netid, const QString &network, AbstractTreeItem *parent)
143   : PropertyMapItem(QList<QString>() << "networkName" << "currentServer" << "nickCount", parent),
144     _networkId(netid),
145     _networkName(network)
146 {
147   setFlags(Qt::ItemIsEnabled);
148 }
149
150 QVariant NetworkItem::data(int column, int role) const {
151   switch(role) {
152   case NetworkModel::NetworkIdRole:
153     return _networkId;
154   case NetworkModel::ItemTypeRole:
155     return NetworkModel::NetworkItemType;
156   default:
157     return PropertyMapItem::data(column, role);
158   }
159 }
160
161 quint64 NetworkItem::id() const {
162   return _networkId;
163 }
164
165 QString NetworkItem::networkName() const {
166   if(_networkInfo)
167     return _networkInfo->networkName();
168   else
169     return _networkName;
170 }
171
172 QString NetworkItem::currentServer() const {
173   if(_networkInfo)
174     return _networkInfo->currentServer();
175   else
176     return QString();
177 }
178
179 int NetworkItem::nickCount() const {
180   BufferItem *bufferItem;
181   int count = 0;
182   for(int i = 0; i < childCount(); i++) {
183     bufferItem = qobject_cast<BufferItem *>(child(i));
184     if(!bufferItem)
185       continue;
186     count += bufferItem->nickCount();
187   }
188   return count;
189 }
190
191 void NetworkItem::attachNetworkInfo(NetworkInfo *networkInfo) {
192   if(!networkInfo)
193     return;
194   
195   _networkInfo = networkInfo;
196
197   connect(networkInfo, SIGNAL(networkNameSet(QString)),
198           this, SLOT(setNetworkName(QString)));
199   connect(networkInfo, SIGNAL(currentServerSet(QString)),
200           this, SLOT(setCurrentServer(QString)));
201   connect(networkInfo, SIGNAL(ircChannelAdded(QString)),
202           this, SLOT(attachIrcChannel(QString)));
203   // FIXME: connect this and that...
204 }
205
206 void NetworkItem::attachIrcChannel(const QString &channelName) {
207   IrcChannel *ircChannel = _networkInfo->ircChannel(channelName);
208   if(!ircChannel) {
209     qWarning() << "NetworkItem::attachIrcChannel(): unkown Channel" << channelName;
210     return;
211   }
212   
213   BufferItem *bufferItem;
214   for(int i = 0; i < childCount(); i++) {
215     bufferItem = qobject_cast<BufferItem *>(child(i));
216     if(bufferItem->bufferName() == ircChannel->name()) {
217       bufferItem->attachIrcChannel(ircChannel);
218       break;
219     }
220   }
221 }
222
223 void NetworkItem::setNetworkName(const QString &networkName) {
224   Q_UNUSED(networkName);
225   emit dataChanged(0);
226 }
227
228 void NetworkItem::setCurrentServer(const QString &serverName) {
229   Q_UNUSED(serverName);
230   emit dataChanged(1);
231 }
232
233 /*****************************************
234 *  Irc User Items
235 *****************************************/
236 IrcUserItem::IrcUserItem(IrcUser *ircUser, AbstractTreeItem *parent)
237   : PropertyMapItem(QStringList() << "nickName", parent),
238     _ircUser(ircUser)
239 {
240   connect(ircUser, SIGNAL(destroyed()),
241           this, SLOT(ircUserDestroyed()));
242   
243   connect(ircUser, SIGNAL(nickSet(QString)),
244           this, SLOT(setNick(QString)));
245 }
246
247 QString IrcUserItem::nickName() {
248   return _ircUser->nick();
249 }
250
251 void IrcUserItem::setNick(QString newNick) {
252   Q_UNUSED(newNick);
253   emit dataChanged(0);
254 }
255 void IrcUserItem::ircUserDestroyed() {
256   deleteLater();
257 }
258
259
260 /*****************************************
261  * NetworkModel
262  *****************************************/
263 NetworkModel::NetworkModel(QObject *parent)
264   : TreeModel(NetworkModel::defaultHeader(), parent)
265 {
266 }
267
268 QList<QVariant >NetworkModel::defaultHeader() {
269   QList<QVariant> data;
270   data << tr("Buffer") << tr("Topic") << tr("Nick Count");
271   return data;
272 }
273
274 bool NetworkModel::isBufferIndex(const QModelIndex &index) const {
275   return index.data(NetworkModel::ItemTypeRole) == NetworkModel::BufferItemType;
276 }
277
278 Buffer *NetworkModel::getBufferByIndex(const QModelIndex &index) const {
279   BufferItem *item = static_cast<BufferItem *>(index.internalPointer());
280   // FIXME get rid of this
281   Q_ASSERT(item->buffer() == Client::instance()->buffer(item->id()));
282   return item->buffer();
283 }
284
285
286 // experimental stuff :)
287 QModelIndex NetworkModel::networkIndex(uint networkId) {
288   return indexById(networkId);
289 }
290
291 NetworkItem *NetworkModel::network(uint networkId) {
292   return qobject_cast<NetworkItem *>(rootItem->childById(networkId));
293 }
294
295 NetworkItem *NetworkModel::newNetwork(uint networkId, const QString &networkName) {
296   NetworkItem *networkItem = network(networkId);
297
298   if(networkItem == 0) {
299     networkItem = new NetworkItem(networkId, networkName, rootItem);
300     appendChild(rootItem, networkItem);
301   }
302
303   Q_ASSERT(networkItem);
304   return networkItem;
305 }
306
307 QModelIndex NetworkModel::bufferIndex(BufferInfo bufferInfo) {
308   QModelIndex networkIdx = networkIndex(bufferInfo.networkId());
309   if(!networkIdx.isValid())
310     return QModelIndex();
311   else
312     return indexById(bufferInfo.uid(), networkIdx);
313 }
314
315 BufferItem *NetworkModel::buffer(BufferInfo bufferInfo) {
316   QModelIndex bufferIdx = bufferIndex(bufferInfo);
317   if(bufferIdx.isValid())
318     return static_cast<BufferItem *>(bufferIdx.internalPointer());
319   else
320     return 0;
321 }
322
323 BufferItem *NetworkModel::newBuffer(BufferInfo bufferInfo) {
324   BufferItem *bufferItem = buffer(bufferInfo);
325   if(bufferItem == 0) {
326     NetworkItem *networkItem = newNetwork(bufferInfo.networkId(), bufferInfo.network());
327
328     // FIXME: get rid of the buffer pointer
329     Buffer *buffer = Client::instance()->buffer(bufferInfo.uid());
330     bufferItem = new BufferItem(buffer, networkItem);
331     appendChild(networkItem, bufferItem);
332   }
333
334   Q_ASSERT(bufferItem);
335   return bufferItem;
336 }
337
338 QStringList NetworkModel::mimeTypes() const {
339   // mimetypes we accept for drops
340   QStringList types;
341   // comma separated list of colon separated pairs of networkid and bufferid
342   // example: 0:1,0:2,1:4
343   types << "application/Quassel/BufferItemList";
344   return types;
345 }
346
347 bool NetworkModel::mimeContainsBufferList(const QMimeData *mimeData) {
348   return mimeData->hasFormat("application/Quassel/BufferItemList");
349 }
350
351 QList< QPair<uint, uint> > NetworkModel::mimeDataToBufferList(const QMimeData *mimeData) {
352   QList< QPair<uint, uint> > bufferList;
353
354   if(!mimeContainsBufferList(mimeData))
355     return bufferList;
356
357   QStringList rawBufferList = QString::fromAscii(mimeData->data("application/Quassel/BufferItemList")).split(",");
358   uint networkId, bufferUid;
359   foreach(QString rawBuffer, rawBufferList) {
360     if(!rawBuffer.contains(":"))
361       continue;
362     networkId = rawBuffer.section(":", 0, 0).toUInt();
363     bufferUid = rawBuffer.section(":", 1, 1).toUInt();
364     bufferList.append(qMakePair(networkId, bufferUid));
365   }
366   return bufferList;
367 }
368
369
370 QMimeData *NetworkModel::mimeData(const QModelIndexList &indexes) const {
371   QMimeData *mimeData = new QMimeData();
372
373   QStringList bufferlist;
374   QString netid, uid, bufferid;
375   foreach(QModelIndex index, indexes) {
376     netid = QString::number(index.data(NetworkIdRole).toUInt());
377     uid = QString::number(index.data(BufferUidRole).toUInt());
378     bufferid = QString("%1:%2").arg(netid).arg(uid);
379     if(!bufferlist.contains(bufferid))
380       bufferlist << bufferid;
381   }
382
383   mimeData->setData("application/Quassel/BufferItemList", bufferlist.join(",").toAscii());
384
385   return mimeData;
386 }
387
388 bool NetworkModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) {
389   Q_UNUSED(action)
390   Q_UNUSED(row)
391   Q_UNUSED(column)
392
393   if(!mimeContainsBufferList(data))
394     return false;
395
396   // target must be a query
397   Buffer::Type targetType = (Buffer::Type)parent.data(NetworkModel::BufferTypeRole).toInt();
398   if(targetType != Buffer::QueryType)
399     return false;
400
401   QList< QPair<uint, uint> > bufferList = mimeDataToBufferList(data);
402
403   // exactly one buffer has to be dropped
404   if(bufferList.count() != 1)
405     return false;
406
407   uint netId = bufferList.first().first;
408   uint bufferId = bufferList.first().second;
409
410   // no self merges (would kill us)
411   if(bufferId == parent.data(BufferUidRole).toUInt())
412     return false; 
413   
414   Q_ASSERT(rootItem->childById(netId));
415   Q_ASSERT(rootItem->childById(netId)->childById(bufferId));
416
417   // source must be a query too
418   Buffer::Type sourceType = (Buffer::Type)rootItem->childById(netId)->childById(bufferId)->data(0, BufferTypeRole).toInt();
419   if(sourceType != Buffer::QueryType)
420     return false;
421     
422   // TODO: warn user about buffermerge!
423   qDebug() << "merging" << bufferId << parent.data(BufferUidRole).toInt();
424   removeRow(parent.row(), parent.parent());
425   
426   return true;
427 }
428
429 void NetworkModel::attachNetworkInfo(NetworkInfo *networkInfo) {
430   NetworkItem *networkItem = network(networkInfo->networkId());
431   if(!networkItem) {
432     qWarning() << "NetworkModel::attachNetworkInfo(): network is unknown!";
433     return;
434   }
435   networkItem->attachNetworkInfo(networkInfo);
436 }
437
438 void NetworkModel::bufferUpdated(Buffer *buffer) {
439   BufferItem *bufferItem = newBuffer(buffer->bufferInfo());
440   QModelIndex itemindex = indexByItem(bufferItem);
441   emit dataChanged(itemindex, itemindex);
442 }
443
444 void NetworkModel::bufferActivity(Buffer::ActivityLevel level, Buffer *buf) {
445   BufferItem *bufferItem = buffer(buf->bufferInfo());
446   if(!bufferItem) {
447     qWarning() << "NetworkModel::bufferActivity(): received Activity Info for uknown Buffer";
448     return;
449   }
450   bufferItem->setActivity(level);
451   bufferUpdated(buf);
452 }
453