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