31023f747cf4e49724181e620dddcc65a4ea70f9
[quassel.git] / gui / bufferviewwidget.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 "global.h"
22 #include "bufferviewwidget.h"
23
24 /*****************************************
25  *  Buffer Items stored in the Tree Model
26  *****************************************/
27 TreeItem::TreeItem(QList<QVariant> &data, TreeItem *parent) {
28   itemData = data;
29   parentItem = parent;
30   foreground = Qt::black;
31 }
32
33 TreeItem::TreeItem(TreeItem *parent) {
34   itemData = QList<QVariant>();
35   parentItem = parent;
36   foreground = Qt::black;
37 }
38
39 TreeItem::~TreeItem() {
40   qDeleteAll(childItems);
41 }
42
43 void TreeItem::appendChild(TreeItem *item) {
44   childItems.append(item);
45 }
46
47 void TreeItem::removeChild(int row) {
48   childItems.removeAt(row);
49 }
50
51 TreeItem *TreeItem::child(int row) {
52   return childItems.value(row);
53 }
54
55 int TreeItem::childCount() const {
56   return childItems.count();
57 }
58
59 int TreeItem::row() const {
60   if(parentItem)
61     return parentItem->childItems.indexOf(const_cast<TreeItem*>(this));
62   else
63     return 0;
64 }
65
66 TreeItem *TreeItem::parent() {
67   return parentItem;
68 }
69
70 int TreeItem::columnCount() const {
71   return itemData.count();
72 }
73
74 void TreeItem::setForeground(QColor newcolor) {
75   foreground = newcolor;
76 }
77
78 QVariant TreeItem::data(int column, int role) const {
79   switch(role) {
80     case Qt::DisplayRole:
81       if(column < itemData.count())
82         return itemData[column];
83       else
84         return QVariant();
85
86     case Qt::ForegroundRole:
87       return foreground;
88       
89     default:
90       return QVariant();
91       
92   }
93 }
94
95 /*****************************************
96 *  Fancy Buffer Items
97 *****************************************/
98 BufferTreeItem::BufferTreeItem(Buffer *buffer, TreeItem *parent) : TreeItem(parent) {
99   buf = buffer;
100   activity = Buffer::NoActivity;
101 }
102
103 void BufferTreeItem::setActivity(const Buffer::ActivityLevel &level) {
104   activity = level;
105 }
106
107 QString BufferTreeItem::text(int column) const {
108   switch(column) {
109     case 0:
110       return buf->displayName();
111     case 1:
112       return buf->networkName();
113     default:
114       return QString();
115   }
116 }
117
118 QColor BufferTreeItem::foreground(int column) const {
119   // for the time beeing we ignore the column :)
120   if(activity & Buffer::Highlight) {
121     return QColor(Qt::red);
122   } else if(activity & Buffer::NewMessage) {
123     return QColor(Qt::darkYellow);
124   } else if(activity & Buffer::OtherActivity) {
125     return QColor(Qt::darkGreen);
126   } else {
127     if(buf->isActive())
128       return QColor(Qt::black);
129     else
130       return QColor(Qt::gray);
131   }
132 }
133
134
135 QVariant BufferTreeItem::data(int column, int role) const {
136   switch(role) {
137     case Qt::DisplayRole:
138       return text(column);
139     case Qt::ForegroundRole:
140       return foreground(column);
141     case BufferTreeModel::BufferTypeRole:
142       return buf->bufferType();
143     case BufferTreeModel::BufferActiveRole:
144       return buf->isActive();
145     default:
146       return QVariant();
147   }
148 }
149
150 /*****************************************
151  * BufferTreeModel
152  *****************************************/
153 BufferTreeModel::BufferTreeModel(QObject *parent) : QAbstractItemModel(parent) {
154   QList<QVariant> rootData;
155   rootData << "Buffer" << "Network";
156   rootItem = new TreeItem(rootData, 0);
157   
158   connect(this, SIGNAL(fakeUserInput(BufferId, QString)), guiProxy, SLOT(gsUserInput(BufferId, QString)));
159 }
160
161 BufferTreeModel::~BufferTreeModel() {
162   delete rootItem;
163 }
164
165 QModelIndex BufferTreeModel::index(int row, int column, const QModelIndex &parent) const {
166   if(!hasIndex(row, column, parent))
167     return QModelIndex();
168   
169   TreeItem *parentItem;
170   
171   if(!parent.isValid())
172     parentItem = rootItem;
173   else
174     parentItem = static_cast<TreeItem*>(parent.internalPointer());
175   
176   TreeItem *childItem = parentItem->child(row);
177   if(childItem)
178     return createIndex(row, column, childItem);
179   else
180     return QModelIndex();
181 }
182
183 QModelIndex BufferTreeModel::parent(const QModelIndex &index) const {
184   if(!index.isValid())
185     return QModelIndex();
186   
187   TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer());
188   TreeItem *parentItem = childItem->parent();
189   
190   if(parentItem == rootItem)
191     return QModelIndex();
192   
193   return createIndex(parentItem->row(), 0, parentItem);
194 }
195
196 int BufferTreeModel::rowCount(const QModelIndex &parent) const {
197   TreeItem *parentItem;
198   if(parent.column() > 0)
199     return 0;
200   
201   if(!parent.isValid())
202     parentItem = rootItem;
203   else
204     parentItem = static_cast<TreeItem*>(parent.internalPointer());
205   
206   return parentItem->childCount();
207 }
208
209 int BufferTreeModel::columnCount(const QModelIndex &parent) const {
210   if(parent.isValid())
211     return static_cast<TreeItem*>(parent.internalPointer())->columnCount();
212   else
213     return rootItem->columnCount();
214 }
215
216 QVariant BufferTreeModel::data(const QModelIndex &index, int role) const {
217   if(!index.isValid())
218     return QVariant();
219
220   TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
221   return item->data(index.column(), role);
222 }
223
224 Qt::ItemFlags BufferTreeModel::flags(const QModelIndex &index) const {
225   if(!index.isValid())
226     return 0;
227
228   // I think this is pretty ugly..
229   if(isBufferIndex(index)) {
230     Buffer *buffer = getBufferByIndex(index);
231     if(buffer->bufferType() == Buffer::QueryBuffer)
232       return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
233     else
234       return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
235   } else {
236     return Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; 
237   }
238 }
239
240 QVariant BufferTreeModel::headerData(int section, Qt::Orientation orientation, int role) const {
241   if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
242     return rootItem->data(section, role);
243   else
244     return QVariant();
245 }
246
247 bool BufferTreeModel::removeRow(int row, const QModelIndex &parent) {
248   beginRemoveRows(parent, row, row);
249   TreeItem *item = static_cast<TreeItem*>(parent.internalPointer());
250   item->removeChild(row);
251   endRemoveRows();
252   return true;
253 }
254
255 bool BufferTreeModel::isBufferIndex(const QModelIndex &index) const {
256   return parent(index) != QModelIndex();
257 }
258
259 Buffer *BufferTreeModel::getBufferByIndex(const QModelIndex &index) const {
260   BufferTreeItem *item = static_cast<BufferTreeItem *>(index.internalPointer());
261   return item->buffer();
262 }
263
264 QModelIndex BufferTreeModel::getOrCreateNetworkItemIndex(Buffer *buffer) {
265   QString net = buffer->networkName();
266   
267   if(networkItem.contains(net)) {
268     return index(networkItem[net]->row(), 0);
269   } else {
270     QList<QVariant> data;
271     data << net << "";
272     
273     int nextRow = rootItem->childCount();
274     
275     beginInsertRows(QModelIndex(), nextRow, nextRow);
276     rootItem->appendChild(new TreeItem(data, rootItem));
277     endInsertRows();
278     
279     networkItem[net] = rootItem->child(nextRow);
280     return index(nextRow, 0);
281   }
282 }
283
284 QModelIndex BufferTreeModel::getOrCreateBufferItemIndex(Buffer *buffer) {
285   QModelIndex networkItemIndex = getOrCreateNetworkItemIndex(buffer);
286   
287   if(bufferItem.contains(buffer)) {
288     return index(bufferItem[buffer]->row(), 0, networkItemIndex);
289   } else {
290     // first we determine the parent of the new Item
291     TreeItem *networkItem = static_cast<TreeItem*>(networkItemIndex.internalPointer());
292
293     int nextRow = networkItem->childCount();
294     
295     beginInsertRows(networkItemIndex, nextRow, nextRow);
296     networkItem->appendChild(new BufferTreeItem(buffer, networkItem));
297     endInsertRows();
298                          
299     bufferItem[buffer] = static_cast<BufferTreeItem *>(networkItem->child(nextRow));
300     return index(nextRow, 0, networkItemIndex);
301   }
302 }
303
304 QStringList BufferTreeModel::mimeTypes() const {
305   QStringList types;
306   types << "application/Quassel/BufferItem/row"
307     << "application/Quassel/BufferItem/network";
308   return types;
309 }
310
311 QMimeData *BufferTreeModel::mimeData(const QModelIndexList &indexes) const {
312   QMimeData *mimeData = new QMimeData();
313
314   QModelIndex index = indexes.first();
315   
316   mimeData->setData("application/Quassel/BufferItem/row", QByteArray::number(index.row()));
317   mimeData->setData("application/Quassel/BufferItem/network", getBufferByIndex(index)->networkName().toUtf8());
318   return mimeData;
319 }
320
321 bool BufferTreeModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) {
322   int sourcerow = data->data("application/Quassel/BufferItem/row").toInt();
323   QString network = QString::fromUtf8(data->data("application/Quassel/BufferItem/network"));
324   
325   if(!networkItem.contains(network))
326     return false;
327
328   if(!isBufferIndex(parent)) // dropping at a network -> no merging needed
329     return false;
330
331   Buffer *sourceBuffer = static_cast<BufferTreeItem *>(networkItem[network]->child(sourcerow))->buffer();
332   Buffer *targetBuffer = getBufferByIndex(parent);
333   
334   qDebug() << "merging" << sourceBuffer->bufferName() << "with" << targetBuffer->bufferName();
335   bufferItem.remove(getBufferByIndex(parent));
336   removeRow(parent.row(), BufferTreeModel::parent(parent));
337   
338   return true;
339 }
340
341 void BufferTreeModel::bufferUpdated(Buffer *buffer) {
342   QModelIndex itemindex = getOrCreateBufferItemIndex(buffer);
343   emit invalidateFilter();
344   emit dataChanged(itemindex, itemindex);
345 }
346
347 // This Slot indicates that the user has selected a different buffer in the gui
348 void BufferTreeModel::changeCurrent(const QModelIndex &current, const QModelIndex &previous) {
349   if(isBufferIndex(current)) {
350     currentBuffer = getBufferByIndex(current);
351     bufferActivity(Buffer::NoActivity, currentBuffer);
352     emit bufferSelected(currentBuffer);
353   }
354 }
355
356 // we received a double click on a buffer, so we're going to join it
357 void BufferTreeModel::doubleClickReceived(const QModelIndex &clicked) {
358   if(isBufferIndex(clicked)) {
359     Buffer *buffer = getBufferByIndex(clicked);
360     if(!buffer->isStatusBuffer()) 
361       emit fakeUserInput(buffer->bufferId(), QString("/join " + buffer->bufferName()));
362   }
363     
364 }
365
366 void BufferTreeModel::bufferActivity(Buffer::ActivityLevel level, Buffer *buffer) {
367   if(bufferItem.contains(buffer) and buffer != currentBuffer)
368     bufferItem[buffer]->setActivity(level);
369   else
370     bufferItem[buffer]->setActivity(Buffer::NoActivity);
371   bufferUpdated(buffer);
372 }
373
374 void BufferTreeModel::selectBuffer(Buffer *buffer) {
375   QModelIndex index = getOrCreateBufferItemIndex(buffer);
376   emit updateSelection(index, QItemSelectionModel::ClearAndSelect);
377 }
378
379
380 /*****************************************
381  * This Widget Contains the BufferView
382  *****************************************/
383 BufferViewWidget::BufferViewWidget(QWidget *parent) : QWidget(parent) {
384   ui.setupUi(this);
385 }
386
387 QSize BufferViewWidget::sizeHint() const {
388   return QSize(150,100);
389 }
390
391
392 /*****************************************
393  * Dock and API for the BufferViews
394  *****************************************/
395 BufferViewDock::BufferViewDock(QAbstractItemModel *model, QString viewname, BufferViewFilter::Modes mode, QStringList nets, QWidget *parent) : QDockWidget(parent) {
396   setObjectName(QString("View-" + viewname)); // should be unique for mainwindow state!
397   setWindowTitle(viewname);
398
399   BufferViewWidget *viewWidget = new BufferViewWidget(this);
400   viewWidget->treeView()->setFilteredModel(model, mode, nets);
401   viewWidget->treeView()->init();
402   setWidget(viewWidget);
403 }