Goodbye SyncRelay, you will live on in our hearts...
[quassel.git] / src / uisupport / bufferviewfilter.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-09 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 "bufferviewfilter.h"
22
23 #include <QApplication>
24 #include <QPalette>
25 #include <QBrush>
26
27 #include "bufferinfo.h"
28 #include "buffermodel.h"
29 #include "buffersettings.h"
30 #include "client.h"
31 #include "clientbufferviewconfig.h"
32 #include "graphicalui.h"
33 #include "iconloader.h"
34 #include "networkmodel.h"
35 #include "uistyle.h"
36
37 class CheckRemovalEvent : public QEvent {
38 public:
39   CheckRemovalEvent(const QModelIndex &source_index) : QEvent(QEvent::User), index(source_index) {};
40   QPersistentModelIndex index;
41 };
42
43 /*****************************************
44 * The Filter for the Tree View
45 *****************************************/
46 BufferViewFilter::BufferViewFilter(QAbstractItemModel *model, BufferViewConfig *config)
47   : QSortFilterProxyModel(model),
48     _config(0),
49     _sortOrder(Qt::AscendingOrder),
50     _editMode(false),
51     _enableEditMode(tr("Show / Hide buffers"), this)
52 {
53   setConfig(config);
54   setSourceModel(model);
55
56   setDynamicSortFilter(true);
57
58   connect(this, SIGNAL(_dataChanged(const QModelIndex &, const QModelIndex &)),
59           this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex)));
60
61   _enableEditMode.setCheckable(true);
62   _enableEditMode.setChecked(_editMode);
63   connect(&_enableEditMode, SIGNAL(toggled(bool)), this, SLOT(enableEditMode(bool)));
64
65 }
66
67 void BufferViewFilter::setConfig(BufferViewConfig *config) {
68   if(_config == config)
69     return;
70
71   if(_config) {
72     disconnect(_config, 0, this, 0);
73   }
74
75   _config = config;
76
77   if(!config) {
78     invalidate();
79     setObjectName("");
80     return;
81   }
82
83   if(config->isInitialized()) {
84     configInitialized();
85   } else {
86     // we use a queued connection here since manipulating the connection list of a sending object
87     // doesn't seem to be such a good idea while executing a connected slots.
88     connect(config, SIGNAL(initDone()), this, SLOT(configInitialized()), Qt::QueuedConnection);
89     invalidate();
90   }
91 }
92
93 void BufferViewFilter::configInitialized() {
94   if(!config())
95     return;
96
97   connect(config(), SIGNAL(bufferViewNameSet(const QString &)), this, SLOT(invalidate()));
98   connect(config(), SIGNAL(networkIdSet(const NetworkId &)), this, SLOT(invalidate()));
99   connect(config(), SIGNAL(addNewBuffersAutomaticallySet(bool)), this, SLOT(invalidate()));
100   connect(config(), SIGNAL(sortAlphabeticallySet(bool)), this, SLOT(invalidate()));
101   connect(config(), SIGNAL(hideInactiveBuffersSet(bool)), this, SLOT(invalidate()));
102   connect(config(), SIGNAL(allowedBufferTypesSet(int)), this, SLOT(invalidate()));
103   connect(config(), SIGNAL(minimumActivitySet(int)), this, SLOT(invalidate()));
104   connect(config(), SIGNAL(bufferListSet()), this, SLOT(invalidate()));
105   connect(config(), SIGNAL(bufferAdded(const BufferId &, int)), this, SLOT(invalidate()));
106   connect(config(), SIGNAL(bufferMoved(const BufferId &, int)), this, SLOT(invalidate()));
107   connect(config(), SIGNAL(bufferRemoved(const BufferId &)), this, SLOT(invalidate()));
108   connect(config(), SIGNAL(bufferPermanentlyRemoved(const BufferId &)), this, SLOT(invalidate()));
109
110   disconnect(config(), SIGNAL(initDone()), this, SLOT(configInitialized()));
111
112   setObjectName(config()->bufferViewName());
113
114   invalidate();
115   emit configChanged();
116 }
117
118 QList<QAction *> BufferViewFilter::actions(const QModelIndex &index) {
119   Q_UNUSED(index)
120   QList<QAction *> actionList;
121   actionList << &_enableEditMode;
122   return actionList;
123 }
124
125 void BufferViewFilter::enableEditMode(bool enable) {
126   if(_editMode == enable) {
127     return;
128   }
129   _editMode = enable;
130
131   if(!config())
132     return;
133
134   if(enable == false) {
135     addBuffers(QList<BufferId>::fromSet(_toAdd));
136     QSet<BufferId>::const_iterator iter;
137     for(iter = _toTempRemove.constBegin(); iter != _toTempRemove.constEnd(); iter++) {
138       if(config()->temporarilyRemovedBuffers().contains(*iter))
139          continue;
140       config()->requestRemoveBuffer(*iter);
141     }
142     for(iter = _toRemove.constBegin(); iter != _toRemove.constEnd(); iter++) {
143       if(config()->removedBuffers().contains(*iter))
144          continue;
145       config()->requestRemoveBufferPermanently(*iter);
146     }
147   }
148   _toAdd.clear();
149   _toTempRemove.clear();
150   _toRemove.clear();
151
152   invalidate();
153 }
154
155
156 Qt::ItemFlags BufferViewFilter::flags(const QModelIndex &index) const {
157   QModelIndex source_index = mapToSource(index);
158   Qt::ItemFlags flags = sourceModel()->flags(source_index);
159   if(config()) {
160     NetworkModel::ItemType itemType = (NetworkModel::ItemType)sourceModel()->data(source_index, NetworkModel::ItemTypeRole).toInt();
161     BufferInfo::Type bufferType = (BufferInfo::Type)sourceModel()->data(source_index, NetworkModel::BufferTypeRole).toInt();
162     if(source_index == QModelIndex() || itemType == NetworkModel::NetworkItemType) {
163       flags |= Qt::ItemIsDropEnabled;
164     } else if(_editMode) {
165       flags |= Qt::ItemIsUserCheckable | Qt::ItemIsTristate;
166     }
167
168     // prohibit dragging of most items. and most drop places
169     // only query to query is allowed for merging
170     if(bufferType != BufferInfo::QueryBuffer) {
171       ClientBufferViewConfig *clientConf = qobject_cast<ClientBufferViewConfig *>(config());
172       if(clientConf && clientConf->isLocked()) {
173         flags &= ~(Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled);
174       }
175     }
176   }
177   return flags;
178 }
179
180 bool BufferViewFilter::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) {
181   if(!config() || !NetworkModel::mimeContainsBufferList(data))
182     return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent);
183
184   NetworkId droppedNetworkId;
185   QModelIndex source_parent = mapToSource(parent);
186   if(sourceModel()->data(source_parent, NetworkModel::ItemTypeRole) == NetworkModel::NetworkItemType)
187     droppedNetworkId = sourceModel()->data(source_parent, NetworkModel::NetworkIdRole).value<NetworkId>();
188
189   QList< QPair<NetworkId, BufferId> > bufferList = NetworkModel::mimeDataToBufferList(data);
190   BufferId bufferId;
191   NetworkId networkId;
192   int pos;
193   for(int i = 0; i < bufferList.count(); i++) {
194     networkId = bufferList[i].first;
195     bufferId = bufferList[i].second;
196     if(droppedNetworkId == networkId) {
197       if(row < 0)
198         row = 0;
199
200       if(row < rowCount(parent)) {
201         QModelIndex source_child = mapToSource(index(row, 0, parent));
202         BufferId beforeBufferId = sourceModel()->data(source_child, NetworkModel::BufferIdRole).value<BufferId>();
203         pos = config()->bufferList().indexOf(beforeBufferId);
204         if(_sortOrder == Qt::DescendingOrder)
205           pos++;
206       } else {
207         if(_sortOrder == Qt::AscendingOrder)
208           pos = config()->bufferList().count();
209         else
210           pos = 0;
211       }
212
213       if(config()->bufferList().contains(bufferId) && !config()->sortAlphabetically()) {
214         if(config()->bufferList().indexOf(bufferId) < pos)
215           pos--;
216         ClientBufferViewConfig *clientConf = qobject_cast<ClientBufferViewConfig *>(config());
217         if(!clientConf || !clientConf->isLocked())
218           config()->requestMoveBuffer(bufferId, pos);
219       } else {
220         config()->requestAddBuffer(bufferId, pos);
221       }
222
223     } else {
224       addBuffer(bufferId);
225     }
226   }
227   return true;
228 }
229
230 void BufferViewFilter::sort(int column, Qt::SortOrder order) {
231   _sortOrder = order;
232   QSortFilterProxyModel::sort(column, order);
233 }
234
235 void BufferViewFilter::addBuffer(const BufferId &bufferId) const {
236   if(!config() || config()->bufferList().contains(bufferId))
237     return;
238
239   int pos = config()->bufferList().count();
240   bool lt;
241   for(int i = 0; i < config()->bufferList().count(); i++) {
242     if(config() && config()->sortAlphabetically())
243       lt = bufferIdLessThan(bufferId, config()->bufferList()[i]);
244     else
245       lt = bufferId < config()->bufferList()[i];
246
247     if(lt) {
248       pos = i;
249       break;
250     }
251   }
252   config()->requestAddBuffer(bufferId, pos);
253 }
254
255 void BufferViewFilter::addBuffers(const QList<BufferId> &bufferIds) const {
256   if(!config())
257     return;
258
259   QList<BufferId> bufferList = config()->bufferList();
260   foreach(BufferId bufferId, bufferIds) {
261     if(bufferList.contains(bufferId))
262       continue;
263
264     int pos = bufferList.count();
265     bool lt;
266     for(int i = 0; i < bufferList.count(); i++) {
267       if(config() && config()->sortAlphabetically())
268         lt = bufferIdLessThan(bufferId, bufferList[i]);
269       else
270         lt = bufferId < config()->bufferList()[i];
271
272       if(lt) {
273         pos = i;
274         bufferList.insert(pos, bufferId);
275         break;
276       }
277     }
278     config()->requestAddBuffer(bufferId, pos);
279   }
280 }
281
282 bool BufferViewFilter::filterAcceptBuffer(const QModelIndex &source_bufferIndex) const {
283   // no config -> "all buffers" -> accept everything
284   if(!config())
285     return true;
286
287   BufferId bufferId = sourceModel()->data(source_bufferIndex, NetworkModel::BufferIdRole).value<BufferId>();
288   Q_ASSERT(bufferId.isValid());
289
290   int activityLevel = sourceModel()->data(source_bufferIndex, NetworkModel::BufferActivityRole).toInt();
291
292   if(!config()->bufferList().contains(bufferId) && !_editMode) {
293     // add the buffer if...
294     if(config()->isInitialized()
295        && !config()->removedBuffers().contains(bufferId) // it hasn't been manually removed and either
296        && ((config()->addNewBuffersAutomatically() && !config()->temporarilyRemovedBuffers().contains(bufferId)) // is totally unknown to us (a new buffer)...
297            || (config()->temporarilyRemovedBuffers().contains(bufferId) && activityLevel > BufferInfo::OtherActivity))) { // or was just temporarily hidden and has a new message waiting for us.
298       addBuffer(bufferId);
299     }
300     // note: adding the buffer to the valid list does not temper with the following filters ("show only channels" and stuff)
301     return false;
302   }
303
304   if(config()->networkId().isValid() && config()->networkId() != sourceModel()->data(source_bufferIndex, NetworkModel::NetworkIdRole).value<NetworkId>())
305     return false;
306
307   int allowedBufferTypes = config()->allowedBufferTypes();
308   if(!config()->networkId().isValid())
309     allowedBufferTypes &= ~BufferInfo::StatusBuffer;
310   if(!(allowedBufferTypes & sourceModel()->data(source_bufferIndex, NetworkModel::BufferTypeRole).toInt()))
311     return false;
312
313   // the following dynamic filters may not trigger if the buffer is currently selected.
314   QModelIndex currentIndex = Client::bufferModel()->standardSelectionModel()->currentIndex();
315   if(bufferId == Client::bufferModel()->data(currentIndex, NetworkModel::BufferIdRole).value<BufferId>())
316     return true;
317
318   if(config()->hideInactiveBuffers() && !sourceModel()->data(source_bufferIndex, NetworkModel::ItemActiveRole).toBool() && activityLevel <= BufferInfo::OtherActivity)
319     return false;
320
321   if(config()->minimumActivity() > activityLevel)
322     return false;
323
324   return true;
325 }
326
327 bool BufferViewFilter::filterAcceptNetwork(const QModelIndex &source_index) const {
328   if(!config())
329     return true;
330
331   if(!config()->networkId().isValid()) {
332     return true;
333   } else {
334     return config()->networkId() == sourceModel()->data(source_index, NetworkModel::NetworkIdRole).value<NetworkId>();
335   }
336 }
337
338 bool BufferViewFilter::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const {
339   QModelIndex child = sourceModel()->index(source_row, 0, source_parent);
340
341   if(!child.isValid()) {
342     qWarning() << "filterAcceptsRow has been called with an invalid Child";
343     return false;
344   }
345
346   NetworkModel::ItemType childType = (NetworkModel::ItemType)sourceModel()->data(child, NetworkModel::ItemTypeRole).toInt();
347   switch(childType) {
348   case NetworkModel::NetworkItemType:
349     return filterAcceptNetwork(child);
350   case NetworkModel::BufferItemType:
351     return filterAcceptBuffer(child);
352   default:
353     return false;
354   }
355 }
356
357 bool BufferViewFilter::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const {
358   int leftItemType = sourceModel()->data(source_left, NetworkModel::ItemTypeRole).toInt();
359   int rightItemType = sourceModel()->data(source_right, NetworkModel::ItemTypeRole).toInt();
360   int itemType = leftItemType & rightItemType;
361   switch(itemType) {
362   case NetworkModel::NetworkItemType:
363     return networkLessThan(source_left, source_right);
364   case NetworkModel::BufferItemType:
365     return bufferLessThan(source_left, source_right);
366   default:
367     return QSortFilterProxyModel::lessThan(source_left, source_right);
368   }
369 }
370
371 bool BufferViewFilter::bufferLessThan(const QModelIndex &source_left, const QModelIndex &source_right) const {
372   BufferId leftBufferId = sourceModel()->data(source_left, NetworkModel::BufferIdRole).value<BufferId>();
373   BufferId rightBufferId = sourceModel()->data(source_right, NetworkModel::BufferIdRole).value<BufferId>();
374   if(config()) {
375     int leftPos = config()->bufferList().indexOf(leftBufferId);
376     int rightPos = config()->bufferList().indexOf(rightBufferId);
377     if(leftPos == -1 && rightPos == -1)
378       return QSortFilterProxyModel::lessThan(source_left, source_right);
379     if(leftPos == -1 || rightPos == -1)
380       return !(leftPos < rightPos);
381     return leftPos < rightPos;
382   } else
383     return bufferIdLessThan(leftBufferId, rightBufferId);
384 }
385
386 bool BufferViewFilter::networkLessThan(const QModelIndex &source_left, const QModelIndex &source_right) const {
387   NetworkId leftNetworkId = sourceModel()->data(source_left, NetworkModel::NetworkIdRole).value<NetworkId>();
388   NetworkId rightNetworkId = sourceModel()->data(source_right, NetworkModel::NetworkIdRole).value<NetworkId>();
389
390   return QSortFilterProxyModel::lessThan(source_left, source_right);
391 }
392
393 QVariant BufferViewFilter::data(const QModelIndex &index, int role) const {
394   switch(role) {
395   case Qt::FontRole:
396   case Qt::ForegroundRole:
397   case Qt::BackgroundRole:
398   case Qt::DecorationRole:
399     if((config() && config()->disableDecoration()))
400       return QVariant();
401     return GraphicalUi::uiStyle()->bufferViewItemData(mapToSource(index), role);
402   case Qt::CheckStateRole:
403     return checkedState(index);
404   default:
405     return QSortFilterProxyModel::data(index, role);
406   }
407 }
408
409 QVariant BufferViewFilter::checkedState(const QModelIndex &index) const {
410   if(!_editMode || !config())
411     return QVariant();
412
413   QModelIndex source_index = mapToSource(index);
414   if(source_index == QModelIndex() || sourceModel()->data(source_index, NetworkModel::ItemTypeRole) == NetworkModel::NetworkItemType)
415     return QVariant();
416
417   BufferId bufferId = sourceModel()->data(source_index, NetworkModel::BufferIdRole).value<BufferId>();
418   if(_toAdd.contains(bufferId))
419     return Qt::Checked;
420
421   if(_toTempRemove.contains(bufferId))
422     return Qt::PartiallyChecked;
423
424   if(_toRemove.contains(bufferId))
425     return Qt::Unchecked;
426
427   if(config()->bufferList().contains(bufferId))
428     return Qt::Checked;
429
430   if(config()->temporarilyRemovedBuffers().contains(bufferId))
431     return Qt::PartiallyChecked;
432
433   return Qt::Unchecked;
434 }
435
436 bool BufferViewFilter::setData(const QModelIndex &index, const QVariant &value, int role) {
437   switch(role) {
438   case Qt::CheckStateRole:
439     return setCheckedState(index, Qt::CheckState(value.toInt()));
440   default:
441     return QSortFilterProxyModel::setData(index, value, role);
442   }
443 }
444
445 bool BufferViewFilter::setCheckedState(const QModelIndex &index, Qt::CheckState state) {
446   QModelIndex source_index = mapToSource(index);
447   BufferId bufferId = sourceModel()->data(source_index, NetworkModel::BufferIdRole).value<BufferId>();
448   if(!bufferId.isValid())
449     return false;
450
451   switch(state) {
452   case Qt::Unchecked:
453     _toAdd.remove(bufferId);
454     _toTempRemove.remove(bufferId);
455     _toRemove << bufferId;
456     break;
457   case Qt::PartiallyChecked:
458     _toAdd.remove(bufferId);
459     _toTempRemove << bufferId;
460     _toRemove.remove(bufferId);
461     break;
462   case Qt::Checked:
463     _toAdd << bufferId;
464     _toTempRemove.remove(bufferId);
465     _toRemove.remove(bufferId);
466     break;
467   default:
468     return false;
469   }
470   emit dataChanged(index, index);
471   return true;
472 }
473
474 void BufferViewFilter::checkPreviousCurrentForRemoval(const QModelIndex &current, const QModelIndex &previous) {
475   Q_UNUSED(current);
476   if(previous.isValid())
477     QCoreApplication::postEvent(this, new CheckRemovalEvent(previous));
478 }
479
480 void BufferViewFilter::customEvent(QEvent *event) {
481   if(event->type() != QEvent::User)
482     return;
483
484   CheckRemovalEvent *removalEvent = static_cast<CheckRemovalEvent *>(event);
485   checkItemForRemoval(removalEvent->index);
486
487   event->accept();
488 }
489
490 void BufferViewFilter::checkItemsForRemoval(const QModelIndex &topLeft, const QModelIndex &bottomRight) {
491   QModelIndex source_topLeft = mapToSource(topLeft);
492   QModelIndex source_bottomRight = mapToSource(bottomRight);
493   emit _dataChanged(source_topLeft, source_bottomRight);
494 }
495
496 bool BufferViewFilter::bufferIdLessThan(const BufferId &left, const BufferId &right) {
497   Q_CHECK_PTR(Client::networkModel());
498   if(!Client::networkModel())
499     return true;
500
501   QModelIndex leftIndex = Client::networkModel()->bufferIndex(left);
502   QModelIndex rightIndex = Client::networkModel()->bufferIndex(right);
503
504   int leftType = Client::networkModel()->data(leftIndex, NetworkModel::BufferTypeRole).toInt();
505   int rightType = Client::networkModel()->data(rightIndex, NetworkModel::BufferTypeRole).toInt();
506
507   if(leftType != rightType)
508     return leftType < rightType;
509   else
510     return QString::compare(Client::networkModel()->data(leftIndex, Qt::DisplayRole).toString(), Client::networkModel()->data(rightIndex, Qt::DisplayRole).toString(), Qt::CaseInsensitive) < 0;
511 }
512