The nicklist is back! We now have a NickModel that can be attached to an IrcChannel
[quassel.git] / src / client / buffertreemodel.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-07 by The Quassel 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) any later version.                                   *
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
32 /*****************************************
33 *  Fancy Buffer Items
34 *****************************************/
35 BufferTreeItem::BufferTreeItem(Buffer *buffer, TreeItem *parent)
36   : TreeItem(parent),
37     buf(buffer),
38     activity(Buffer::NoActivity)
39 {
40   Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
41   if(buf->bufferType() == Buffer::QueryType)
42     flags |= Qt::ItemIsDropEnabled;
43   setFlags(flags);
44 }
45
46 uint BufferTreeItem::id() const {
47   return buf->bufferInfo().uid();
48 }
49
50 void BufferTreeItem::setActivity(const Buffer::ActivityLevel &level) {
51   activity = level;
52 }
53
54 QString BufferTreeItem::text(int column) const {
55   switch(column) {
56     case 0:
57       return buf->name();
58     case 1:
59       return buf->networkName();
60     default:
61       return QString();
62   }
63 }
64
65 QColor BufferTreeItem::foreground(int column) const {
66   Q_UNUSED(column)
67   // for the time beeing we ignore the column :)
68   if(activity & Buffer::Highlight) {
69     return QColor(Qt::red);
70   } else if(activity & Buffer::NewMessage) {
71     return QColor(Qt::darkYellow);
72   } else if(activity & Buffer::OtherActivity) {
73     return QColor(Qt::darkGreen);
74   } else {
75     if(buf->isActive())
76       return QColor(Qt::black);
77     else
78       return QColor(Qt::gray);
79   }
80 }
81
82
83 QVariant BufferTreeItem::data(int column, int role) const {
84   switch(role) {
85   case Qt::DisplayRole:
86     return text(column);
87   case Qt::ForegroundRole:
88     return foreground(column);
89   case BufferTreeModel::BufferTypeRole:
90     return int(buf->bufferType());
91   case BufferTreeModel::BufferActiveRole:
92     return buf->isActive();
93   case BufferTreeModel::BufferUidRole:
94     return buf->bufferInfo().uid();
95   case BufferTreeModel::NetworkIdRole:
96     return buf->bufferInfo().networkId();
97     
98   default:
99     return TreeItem::data(column, role);
100   }
101 }
102
103 /*****************************************
104 *  Network Items
105 *****************************************/
106 NetworkTreeItem::NetworkTreeItem(const uint &netid, const QString &network, TreeItem *parent)
107   : TreeItem(parent),
108     _networkId(netid),
109     net(network)
110 {
111   net = network;
112   itemData << net << "";
113   setFlags(Qt::ItemIsEnabled);
114 }
115
116 QVariant NetworkTreeItem::data(int column, int role) const {
117   switch(role) {
118   case BufferTreeModel::NetworkIdRole:
119     return _networkId;
120   default:
121     return TreeItem::data(column, role);
122   }
123 }
124
125 uint NetworkTreeItem::id() const {
126   return _networkId;
127 }
128
129 /*****************************************
130  * BufferTreeModel
131  *****************************************/
132 BufferTreeModel::BufferTreeModel(QObject *parent)
133   : TreeModel(BufferTreeModel::defaultHeader(), parent),
134     _selectionModelSynchronizer(new SelectionModelSynchronizer(this)),
135     _propertyMapper(new ModelPropertyMapper(this))
136 {
137   // initialize the Property Mapper
138   _propertyMapper->setModel(this);
139   delete _propertyMapper->selectionModel();
140   MappedSelectionModel *mappedSelectionModel = new MappedSelectionModel(this);
141   _propertyMapper->setSelectionModel(mappedSelectionModel);
142   synchronizeSelectionModel(mappedSelectionModel);
143   
144   connect(_selectionModelSynchronizer, SIGNAL(setCurrentIndex(QModelIndex, QItemSelectionModel::SelectionFlags)),
145           this, SLOT(setCurrentIndex(QModelIndex, QItemSelectionModel::SelectionFlags)));
146 }
147
148 QList<QVariant >BufferTreeModel::defaultHeader() {
149   QList<QVariant> data;
150   data << tr("Buffer") << tr("Network");
151   return data;
152 }
153
154 void BufferTreeModel::synchronizeSelectionModel(MappedSelectionModel *selectionModel) {
155   selectionModelSynchronizer()->addSelectionModel(selectionModel);
156 }
157
158 void BufferTreeModel::synchronizeView(QAbstractItemView *view) {
159   MappedSelectionModel *mappedSelectionModel = new MappedSelectionModel(view->model());
160   selectionModelSynchronizer()->addSelectionModel(mappedSelectionModel);
161   Q_ASSERT(mappedSelectionModel);
162   delete view->selectionModel();
163   view->setSelectionModel(mappedSelectionModel);
164 }
165
166 void BufferTreeModel::mapProperty(int column, int role, QObject *target, const QByteArray &property) {
167   propertyMapper()->addMapping(column, role, target, property);
168 }
169
170 bool BufferTreeModel::isBufferIndex(const QModelIndex &index) const {
171   // not so purdy...
172   return parent(index) != QModelIndex();
173 }
174
175 Buffer *BufferTreeModel::getBufferByIndex(const QModelIndex &index) const {
176   BufferTreeItem *item = static_cast<BufferTreeItem *>(index.internalPointer());
177   return item->buffer();
178 }
179
180 QModelIndex BufferTreeModel::getOrCreateNetworkItemIndex(Buffer *buffer) {
181   QString net = buffer->networkName();
182   uint netId = buffer->networkId();
183   TreeItem *networkItem;
184
185   if(!(networkItem = rootItem->childById(netId))) {
186     int nextRow = rootItem->childCount();
187     networkItem = new NetworkTreeItem(netId, net, rootItem);
188     
189     beginInsertRows(QModelIndex(), nextRow, nextRow);
190     rootItem->appendChild(networkItem);
191     endInsertRows();
192   }
193
194   Q_ASSERT(networkItem);
195   return index(networkItem->row(), 0);
196 }
197
198 QModelIndex BufferTreeModel::getOrCreateBufferItemIndex(Buffer *buffer) {
199   QModelIndex networkItemIndex = getOrCreateNetworkItemIndex(buffer);
200   NetworkTreeItem *networkItem = static_cast<NetworkTreeItem*>(networkItemIndex.internalPointer());
201   TreeItem *bufferItem;
202   
203   if(!(bufferItem = networkItem->childById(buffer->bufferInfo().uid()))) {
204     int nextRow = networkItem->childCount();
205     bufferItem = new BufferTreeItem(buffer, networkItem);
206     
207     beginInsertRows(networkItemIndex, nextRow, nextRow);
208     networkItem->appendChild(bufferItem);
209     endInsertRows();
210   }
211
212   Q_ASSERT(bufferItem);
213   return index(bufferItem->row(), 0, networkItemIndex);
214 }
215
216 QStringList BufferTreeModel::mimeTypes() const {
217   // mimetypes we accept for drops
218   QStringList types;
219   // comma separated list of colon separated pairs of networkid and bufferid
220   // example: 0:1,0:2,1:4
221   types << "application/Quassel/BufferItemList";
222   return types;
223 }
224
225 bool BufferTreeModel::mimeContainsBufferList(const QMimeData *mimeData) {
226   return mimeData->hasFormat("application/Quassel/BufferItemList");
227 }
228
229 QList< QPair<uint, uint> > BufferTreeModel::mimeDataToBufferList(const QMimeData *mimeData) {
230   QList< QPair<uint, uint> > bufferList;
231
232   if(!mimeContainsBufferList(mimeData))
233     return bufferList;
234
235   QStringList rawBufferList = QString::fromAscii(mimeData->data("application/Quassel/BufferItemList")).split(",");
236   uint networkId, bufferUid;
237   foreach(QString rawBuffer, rawBufferList) {
238     if(!rawBuffer.contains(":"))
239       continue;
240     networkId = rawBuffer.section(":", 0, 0).toUInt();
241     bufferUid = rawBuffer.section(":", 1, 1).toUInt();
242     bufferList.append(qMakePair(networkId, bufferUid));
243   }
244   return bufferList;
245 }
246
247
248 QMimeData *BufferTreeModel::mimeData(const QModelIndexList &indexes) const {
249   QMimeData *mimeData = new QMimeData();
250
251   QStringList bufferlist;
252   QString netid, uid, bufferid;
253   foreach(QModelIndex index, indexes) {
254     netid = QString::number(index.data(NetworkIdRole).toUInt());
255     uid = QString::number(index.data(BufferUidRole).toUInt());
256     bufferid = QString("%1:%2").arg(netid).arg(uid);
257     if(!bufferlist.contains(bufferid))
258       bufferlist << bufferid;
259   }
260
261   mimeData->setData("application/Quassel/BufferItemList", bufferlist.join(",").toAscii());
262
263   return mimeData;
264 }
265
266 bool BufferTreeModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) {
267   Q_UNUSED(action)
268   Q_UNUSED(row)
269   Q_UNUSED(column)
270
271   if(!mimeContainsBufferList(data))
272     return false;
273
274   // target must be a query
275   Buffer::Type targetType = (Buffer::Type)parent.data(BufferTreeModel::BufferTypeRole).toInt();
276   if(targetType != Buffer::QueryType)
277     return false;
278
279   QList< QPair<uint, uint> > bufferList = mimeDataToBufferList(data);
280
281   // exactly one buffer has to be dropped
282   if(bufferList.count() != 1)
283     return false;
284
285   uint netId = bufferList.first().first;
286   uint bufferId = bufferList.first().second;
287
288   // no self merges (would kill us)
289   if(bufferId == parent.data(BufferUidRole).toUInt())
290     return false; 
291   
292   Q_ASSERT(rootItem->childById(netId));
293   Q_ASSERT(rootItem->childById(netId)->childById(bufferId));
294
295   // source must be a query too
296   Buffer::Type sourceType = (Buffer::Type)rootItem->childById(netId)->childById(bufferId)->data(0, BufferTypeRole).toInt();
297   if(sourceType != Buffer::QueryType)
298     return false;
299     
300   // TODO: warn user about buffermerge!
301   qDebug() << "merging" << bufferId << parent.data(BufferUidRole).toInt();
302   removeRow(parent.row(), parent.parent());
303   
304   return true;
305 }
306
307 void BufferTreeModel::bufferUpdated(Buffer *buffer) {
308   QModelIndex itemindex = getOrCreateBufferItemIndex(buffer);
309   emit invalidateFilter();
310   emit dataChanged(itemindex, itemindex);
311 }
312
313 // This Slot indicates that the user has selected a different buffer in the gui
314 void BufferTreeModel::setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) {
315   Q_UNUSED(command)
316   if(isBufferIndex(index)) {
317     currentBuffer = getBufferByIndex(index);
318     bufferActivity(Buffer::NoActivity, currentBuffer);
319     emit bufferSelected(currentBuffer);
320     emit selectionChanged(index);
321   }
322 }
323
324 void BufferTreeModel::bufferActivity(Buffer::ActivityLevel level, Buffer *buffer) {
325   BufferTreeItem *bufferItem = static_cast<BufferTreeItem*>(getOrCreateBufferItemIndex(buffer).internalPointer());
326   if(buffer != currentBuffer)
327     bufferItem->setActivity(level);
328   else
329     bufferItem->setActivity(Buffer::NoActivity);
330   bufferUpdated(buffer);
331 }
332
333 void BufferTreeModel::selectBuffer(Buffer *buffer) {
334   QModelIndex index = getOrCreateBufferItemIndex(buffer);
335   // SUPER UGLY!
336   setCurrentIndex(index, 0);
337 }