e0039c8fa4afa66903adf40a0d2f8365934f3415
[quassel.git] / src / client / buffertreemodel.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-07 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 <QColor>  // FIXME Dependency on QtGui!
22
23 #include "buffertreemodel.h"
24
25 #include "mappedselectionmodel.h"
26 #include <QAbstractItemView>
27
28 #include "bufferinfo.h"
29 #include "client.h"
30 #include "signalproxy.h"
31 #include "networkinfo.h"
32 #include "ircchannel.h"
33 #include "ircuser.h"
34
35 /*****************************************
36 *  Fancy Buffer Items
37 *****************************************/
38 BufferTreeItem::BufferTreeItem(Buffer *buffer, AbstractTreeItem *parent)
39   : PropertyMapItem(QStringList() << "bufferName" << "topic" << "nickCount", parent),
40     buf(buffer),
41     activity(Buffer::NoActivity)
42 {
43   Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
44   if(buf->bufferType() == Buffer::QueryType)
45     flags |= Qt::ItemIsDropEnabled;
46   setFlags(flags);
47 }
48
49 quint64 BufferTreeItem::id() const {
50   return buf->bufferInfo().uid();
51 }
52
53 void BufferTreeItem::setActivity(const Buffer::ActivityLevel &level) {
54   activity = level;
55 }
56
57 QColor BufferTreeItem::foreground(int column) const {
58   Q_UNUSED(column)
59   // for the time beeing we ignore the column :)
60   if(activity & Buffer::Highlight) {
61     return QColor(Qt::red);
62   } else if(activity & Buffer::NewMessage) {
63     return QColor(Qt::darkYellow);
64   } else if(activity & Buffer::OtherActivity) {
65     return QColor(Qt::darkGreen);
66   } else {
67     if(buf->isActive())
68       return QColor(Qt::black);
69     else
70       return QColor(Qt::gray);
71   }
72 }
73
74 QVariant BufferTreeItem::data(int column, int role) const {
75   switch(role) {
76   case BufferTreeModel::BufferUidRole:
77     return buf->bufferInfo().uid();
78   case BufferTreeModel::NetworkIdRole:
79     return buf->bufferInfo().networkId();
80   case BufferTreeModel::BufferTypeRole:
81     return int(buf->bufferType());
82   case BufferTreeModel::BufferActiveRole:
83     return buf->isActive();
84   case Qt::ForegroundRole:
85     return foreground(column);
86   default:
87     return PropertyMapItem::data(column, role);
88   }
89 }
90
91 void BufferTreeItem::attachIrcChannel(IrcChannel *ircChannel) {
92   if(!ircChannel)
93     return;
94   
95   _ircChannel = ircChannel;
96
97   connect(ircChannel, SIGNAL(topicSet(QString)),
98           this, SLOT(setTopic(QString)));
99   connect(ircChannel, SIGNAL(ircUserJoined(IrcUser *)),
100           this, SLOT(join(IrcUser *)));
101   connect(ircChannel, SIGNAL(ircUserParted(IrcUser *)),
102           this, SLOT(part(IrcUser *)));
103 }
104
105 QString BufferTreeItem::bufferName() const {
106   return buf->name();
107 }
108
109 QString BufferTreeItem::topic() const {
110   if(_ircChannel)
111     return _ircChannel->topic();
112   else
113     return QString();
114 }
115
116 int BufferTreeItem::nickCount() const {
117   if(_ircChannel)
118     return _ircChannel->ircUsers().count();
119   else
120     return 0;
121 }
122
123 void BufferTreeItem::setTopic(const QString &topic) {
124   Q_UNUSED(topic);
125   emit dataChanged(1);
126 }
127
128 void BufferTreeItem::join(IrcUser *ircUser) {
129   Q_UNUSED(ircUser);
130   emit dataChanged(2);
131 }
132
133 void BufferTreeItem::part(IrcUser *ircUser) {
134   Q_UNUSED(ircUser);
135   emit dataChanged(2);
136 }
137
138 /*****************************************
139 *  Network Items
140 *****************************************/
141 NetworkTreeItem::NetworkTreeItem(const uint &netid, const QString &network, AbstractTreeItem *parent)
142   : PropertyMapItem(QList<QString>() << "networkName" << "currentServer" << "nickCount", parent),
143     _networkId(netid),
144     _networkName(network)
145 {
146   setFlags(Qt::ItemIsEnabled);
147 }
148
149 QVariant NetworkTreeItem::data(int column, int role) const {
150   switch(role) {
151   case BufferTreeModel::NetworkIdRole:
152     return _networkId;
153   case BufferTreeModel::ItemTypeRole:
154     return BufferTreeModel::NetworkItem;
155   default:
156     return PropertyMapItem::data(column, role);
157   }
158 }
159
160 quint64 NetworkTreeItem::id() const {
161   return _networkId;
162 }
163
164 QString NetworkTreeItem::networkName() const {
165   if(_networkInfo)
166     return _networkInfo->networkName();
167   else
168     return _networkName;
169 }
170
171 QString NetworkTreeItem::currentServer() const {
172   if(_networkInfo)
173     return _networkInfo->currentServer();
174   else
175     return QString();
176 }
177
178 int NetworkTreeItem::nickCount() const {
179   BufferTreeItem *bufferItem;
180   int count = 0;
181   for(int i = 0; i < childCount(); i++) {
182     bufferItem = qobject_cast<BufferTreeItem *>(child(i));
183     if(!bufferItem)
184       continue;
185     count += bufferItem->nickCount();
186   }
187   return count;
188 }
189
190 void NetworkTreeItem::attachNetworkInfo(NetworkInfo *networkInfo) {
191   if(!networkInfo)
192     return;
193   
194   _networkInfo = networkInfo;
195
196   connect(networkInfo, SIGNAL(networkNameSet(QString)),
197           this, SLOT(setNetworkName(QString)));
198   connect(networkInfo, SIGNAL(currentServerSet(QString)),
199           this, SLOT(setCurrentServer(QString)));
200   connect(networkInfo, SIGNAL(ircChannelAdded(QString)),
201           this, SLOT(attachIrcChannel(QString)));
202   // FIXME: connect this and that...
203 }
204
205 void NetworkTreeItem::attachIrcChannel(const QString &channelName) {
206   IrcChannel *ircChannel = _networkInfo->ircChannel(channelName);
207   if(!ircChannel) {
208     qWarning() << "NetworkTreeItem::attachIrcChannel(): unkown Channel" << channelName;
209     return;
210   }
211   
212   BufferTreeItem *bufferItem;
213   for(int i = 0; i < childCount(); i++) {
214     bufferItem = qobject_cast<BufferTreeItem *>(child(i));
215     if(bufferItem->bufferName() == ircChannel->name()) {
216       bufferItem->attachIrcChannel(ircChannel);
217       break;
218     }
219   }
220 }
221
222 void NetworkTreeItem::setNetworkName(const QString &networkName) {
223   Q_UNUSED(networkName);
224   emit dataChanged(0);
225 }
226
227 void NetworkTreeItem::setCurrentServer(const QString &serverName) {
228   Q_UNUSED(serverName);
229   emit dataChanged(1);
230 }
231
232 /*****************************************
233  * BufferTreeModel
234  *****************************************/
235 BufferTreeModel::BufferTreeModel(QObject *parent)
236   : TreeModel(BufferTreeModel::defaultHeader(), parent),
237     _selectionModelSynchronizer(new SelectionModelSynchronizer(this)),
238     _propertyMapper(new ModelPropertyMapper(this))
239 {
240   // initialize the Property Mapper
241   _propertyMapper->setModel(this);
242   delete _propertyMapper->selectionModel();
243   MappedSelectionModel *mappedSelectionModel = new MappedSelectionModel(this);
244   _propertyMapper->setSelectionModel(mappedSelectionModel);
245   synchronizeSelectionModel(mappedSelectionModel);
246   
247   connect(_selectionModelSynchronizer, SIGNAL(setCurrentIndex(QModelIndex, QItemSelectionModel::SelectionFlags)),
248           this, SLOT(setCurrentIndex(QModelIndex, QItemSelectionModel::SelectionFlags)));
249 }
250
251 QList<QVariant >BufferTreeModel::defaultHeader() {
252   QList<QVariant> data;
253   data << tr("Buffer") << tr("Topic") << tr("Nick Count");
254   return data;
255 }
256
257 void BufferTreeModel::synchronizeSelectionModel(MappedSelectionModel *selectionModel) {
258   selectionModelSynchronizer()->addSelectionModel(selectionModel);
259 }
260
261 void BufferTreeModel::synchronizeView(QAbstractItemView *view) {
262   MappedSelectionModel *mappedSelectionModel = new MappedSelectionModel(view->model());
263   selectionModelSynchronizer()->addSelectionModel(mappedSelectionModel);
264   Q_ASSERT(mappedSelectionModel);
265   delete view->selectionModel();
266   view->setSelectionModel(mappedSelectionModel);
267 }
268
269 void BufferTreeModel::mapProperty(int column, int role, QObject *target, const QByteArray &property) {
270   propertyMapper()->addMapping(column, role, target, property);
271 }
272
273 bool BufferTreeModel::isBufferIndex(const QModelIndex &index) const {
274   // not so purdy...
275   return parent(index) != QModelIndex();
276 }
277
278 Buffer *BufferTreeModel::getBufferByIndex(const QModelIndex &index) const {
279   BufferTreeItem *item = static_cast<BufferTreeItem *>(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 BufferTreeModel::networkIndex(uint networkId) {
288   return indexById(networkId);
289 }
290
291 NetworkTreeItem *BufferTreeModel::network(uint networkId) {
292   return qobject_cast<NetworkTreeItem *>(rootItem->childById(networkId));
293 }
294
295 NetworkTreeItem *BufferTreeModel::newNetwork(uint networkId, const QString &networkName) {
296   NetworkTreeItem *networkItem = network(networkId);
297
298   if(networkItem == 0) {
299     networkItem = new NetworkTreeItem(networkId, networkName, rootItem);
300     appendChild(rootItem, networkItem);
301   }
302
303   Q_ASSERT(networkItem);
304   return networkItem;
305 }
306
307 QModelIndex BufferTreeModel::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 BufferTreeItem *BufferTreeModel::buffer(BufferInfo bufferInfo) {
316   QModelIndex bufferIdx = bufferIndex(bufferInfo);
317   if(bufferIdx.isValid())
318     return static_cast<BufferTreeItem *>(bufferIdx.internalPointer());
319   else
320     return 0;
321 }
322
323 BufferTreeItem *BufferTreeModel::newBuffer(BufferInfo bufferInfo) {
324   BufferTreeItem *bufferItem = buffer(bufferInfo);
325   if(bufferItem == 0) {
326     NetworkTreeItem *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 BufferTreeItem(buffer, networkItem);
331     appendChild(networkItem, bufferItem);
332   }
333
334   Q_ASSERT(bufferItem);
335   return bufferItem;
336 }
337
338 QStringList BufferTreeModel::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 BufferTreeModel::mimeContainsBufferList(const QMimeData *mimeData) {
348   return mimeData->hasFormat("application/Quassel/BufferItemList");
349 }
350
351 QList< QPair<uint, uint> > BufferTreeModel::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 *BufferTreeModel::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 BufferTreeModel::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(BufferTreeModel::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 BufferTreeModel::attachNetworkInfo(NetworkInfo *networkInfo) {
430   NetworkTreeItem *networkItem = network(networkInfo->networkId());
431   if(!networkItem) {
432     qWarning() << "BufferTreeModel::attachNetworkInfo(): network is unknown!";
433     return;
434   }
435   networkItem->attachNetworkInfo(networkInfo);
436 }
437
438 void BufferTreeModel::bufferUpdated(Buffer *buffer) {
439   BufferTreeItem *bufferItem = newBuffer(buffer->bufferInfo());
440   QModelIndex itemindex = indexByItem(bufferItem);
441   emit dataChanged(itemindex, itemindex);
442 }
443
444 // This Slot indicates that the user has selected a different buffer in the gui
445 void BufferTreeModel::setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) {
446   Q_UNUSED(command)
447   Buffer *newCurrentBuffer;
448   if(isBufferIndex(index) && currentBuffer != (newCurrentBuffer = getBufferByIndex(index))) {
449     currentBuffer = newCurrentBuffer;
450     bufferActivity(Buffer::NoActivity, currentBuffer);
451     emit bufferSelected(currentBuffer);
452     emit selectionChanged(index);
453   }
454 }
455
456 void BufferTreeModel::bufferActivity(Buffer::ActivityLevel level, Buffer *buf) {
457   BufferTreeItem *bufferItem = buffer(buf->bufferInfo());
458   if(!bufferItem) {
459     qWarning() << "BufferTreeModel::bufferActivity(): received Activity Info for uknown Buffer";
460     return;
461   }
462   
463   if(buf != currentBuffer)
464     bufferItem->setActivity(level);
465   else
466     bufferItem->setActivity(Buffer::NoActivity);
467   bufferUpdated(buf);
468 }
469
470 void BufferTreeModel::selectBuffer(Buffer *buffer) {
471   QModelIndex index = bufferIndex(buffer->bufferInfo());
472   if(!index.isValid()) {
473     qWarning() << "BufferTreeModel::selectBuffer(): unknown Buffer has been selected.";
474     return;
475   }
476   // SUPER UGLY!
477   setCurrentIndex(index, 0);
478 }