d3a1b52160f6384a36e275a8b0227b3116a6f320
[quassel.git] / src / uisupport / bufferviewfilter.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-08 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 "buffermodel.h"
28 #include "client.h"
29 #include "networkmodel.h"
30
31 #include "uisettings.h"
32
33 class CheckRemovalEvent : public QEvent {
34 public:
35   CheckRemovalEvent(const QModelIndex &source_index) : QEvent(QEvent::User), index(source_index) {};
36   QPersistentModelIndex index;
37 };
38
39 /*****************************************
40 * The Filter for the Tree View
41 *****************************************/
42 BufferViewFilter::BufferViewFilter(QAbstractItemModel *model, BufferViewConfig *config)
43   : QSortFilterProxyModel(model),
44     _config(0),
45     _sortOrder(Qt::AscendingOrder)
46 {
47   setConfig(config);
48   setSourceModel(model);
49
50   setDynamicSortFilter(true);
51
52   loadColors();
53
54   connect(this, SIGNAL(_dataChanged(const QModelIndex &, const QModelIndex &)),
55           this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex)));
56 }
57
58 void BufferViewFilter::loadColors() {
59   UiSettings s("QtUiStyle/Colors");
60   _FgColorInactiveActivity = s.value("inactiveActivityFG", QVariant(QColor(Qt::gray))).value<QColor>();
61   _FgColorNoActivity = s.value("noActivityFG", QVariant(QColor(Qt::black))).value<QColor>();
62   _FgColorHighlightActivity = s.value("highlightActivityFG", QVariant(QColor(Qt::magenta))).value<QColor>();
63   _FgColorNewMessageActivity = s.value("newMessageActivityFG", QVariant(QColor(Qt::green))).value<QColor>();
64   _FgColorOtherActivity = s.value("otherActivityFG", QVariant(QColor(Qt::darkGreen))).value<QColor>();
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     return;
80   }
81
82   if(config->isInitialized()) {
83     configInitialized();
84   } else {
85     connect(config, SIGNAL(initDone()), this, SLOT(configInitialized()));
86     invalidate();
87   }
88 }
89
90 void BufferViewFilter::configInitialized() {
91   if(!config())
92     return;
93
94   connect(config(), SIGNAL(bufferViewNameSet(const QString &)), this, SLOT(invalidate()));
95   connect(config(), SIGNAL(networkIdSet(const NetworkId &)), this, SLOT(invalidate()));
96   connect(config(), SIGNAL(addNewBuffersAutomaticallySet(bool)), this, SLOT(invalidate()));
97   connect(config(), SIGNAL(sortAlphabeticallySet(bool)), this, SLOT(invalidate()));
98   connect(config(), SIGNAL(hideInactiveBuffersSet(bool)), this, SLOT(invalidate()));
99   connect(config(), SIGNAL(allowedBufferTypesSet(int)), this, SLOT(invalidate()));
100   connect(config(), SIGNAL(minimumActivitySet(int)), this, SLOT(invalidate()));
101   connect(config(), SIGNAL(bufferListSet()), this, SLOT(invalidate()));
102   connect(config(), SIGNAL(bufferAdded(const BufferId &, int)), this, SLOT(invalidate()));
103   connect(config(), SIGNAL(bufferMoved(const BufferId &, int)), this, SLOT(invalidate()));
104   connect(config(), SIGNAL(bufferRemoved(const BufferId &)), this, SLOT(invalidate()));
105   connect(config(), SIGNAL(bufferPermanentlyRemoved(const BufferId &)), this, SLOT(invalidate()));
106
107   disconnect(config(), SIGNAL(initDone()), this, SLOT(configInitialized()));
108
109   invalidate();
110   emit configChanged();
111 }
112
113 Qt::ItemFlags BufferViewFilter::flags(const QModelIndex &index) const {
114   Qt::ItemFlags flags = mapToSource(index).flags();
115   if(_config && (index == QModelIndex() || index.parent() == QModelIndex()))
116     flags |= Qt::ItemIsDropEnabled;
117   return flags;
118 }
119
120 bool BufferViewFilter::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) {
121   if(!config() || !NetworkModel::mimeContainsBufferList(data))
122     return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent);
123
124   NetworkId droppedNetworkId;
125   if(parent.data(NetworkModel::ItemTypeRole) == NetworkModel::NetworkItemType)
126     droppedNetworkId = parent.data(NetworkModel::NetworkIdRole).value<NetworkId>();
127
128   QList< QPair<NetworkId, BufferId> > bufferList = NetworkModel::mimeDataToBufferList(data);
129   BufferId bufferId;
130   NetworkId networkId;
131   int pos;
132   for(int i = 0; i < bufferList.count(); i++) {
133     networkId = bufferList[i].first;
134     bufferId = bufferList[i].second;
135     if(droppedNetworkId == networkId) {
136       if(row < 0)
137         row = 0;
138
139       if(row < rowCount(parent)) {
140         BufferId beforeBufferId = parent.child(row, 0).data(NetworkModel::BufferIdRole).value<BufferId>();
141         pos = config()->bufferList().indexOf(beforeBufferId);
142         if(_sortOrder == Qt::DescendingOrder)
143           pos++;
144       } else {
145         if(_sortOrder == Qt::AscendingOrder)
146           pos = config()->bufferList().count();
147         else
148           pos = 0;
149       }
150
151       if(config()->bufferList().contains(bufferId)) {
152         if(config()->bufferList().indexOf(bufferId) < pos)
153           pos--;
154         config()->requestMoveBuffer(bufferId, pos);
155       } else {
156         config()->requestAddBuffer(bufferId, pos);
157       }
158
159     } else {
160       addBuffer(bufferId);
161     }
162   }
163   return true;
164 }
165
166 void BufferViewFilter::sort(int column, Qt::SortOrder order) {
167   _sortOrder = order;
168   QSortFilterProxyModel::sort(column, order);
169 }
170
171 void BufferViewFilter::addBuffer(const BufferId &bufferId) const {
172   if(!config() || config()->bufferList().contains(bufferId))
173     return;
174
175   int pos = config()->bufferList().count();
176   bool lt;
177   for(int i = 0; i < config()->bufferList().count(); i++) {
178     if(config() && config()->sortAlphabetically())
179       lt = bufferIdLessThan(bufferId, config()->bufferList()[i]);
180     else
181       lt = bufferId < config()->bufferList()[i];
182
183     if(lt) {
184       pos = i;
185       break;
186     }
187   }
188   config()->requestAddBuffer(bufferId, pos);
189 }
190
191 bool BufferViewFilter::filterAcceptBuffer(const QModelIndex &source_bufferIndex) const {
192   // no config -> "all buffers" -> accept everything
193   if(!config())
194     return true;
195
196   BufferId bufferId = sourceModel()->data(source_bufferIndex, NetworkModel::BufferIdRole).value<BufferId>();
197   Q_ASSERT(bufferId.isValid());
198
199   int activityLevel = source_bufferIndex.data(NetworkModel::BufferActivityRole).toInt();
200
201   if(!config()->bufferList().contains(bufferId)) {
202     // add the buffer if...
203     if(config()->isInitialized() && !config()->removedBuffers().contains(bufferId) // it hasn't been manually removed and either
204        && ((config()->addNewBuffersAutomatically() && !config()->temporarilyRemovedBuffers().contains(bufferId)) // is totally unknown to us (a new buffer)...
205            || (config()->temporarilyRemovedBuffers().contains(bufferId) && activityLevel > Buffer::OtherActivity))) { // or was just temporarily hidden and has a new message waiting for us.
206       addBuffer(bufferId);
207     }
208     // note: adding the buffer to the valid list does not temper with the following filters ("show only channels" and stuff)
209     return false;
210   }
211
212   if(config()->networkId().isValid() && config()->networkId() != sourceModel()->data(source_bufferIndex, NetworkModel::NetworkIdRole).value<NetworkId>())
213     return false;
214
215   if(!(config()->allowedBufferTypes() & (BufferInfo::Type)source_bufferIndex.data(NetworkModel::BufferTypeRole).toInt()))
216     return false;
217
218   if(config()->hideInactiveBuffers() && !source_bufferIndex.data(NetworkModel::ItemActiveRole).toBool())
219     return false;
220
221   if(config()->minimumActivity() > activityLevel) {
222     if(bufferId != Client::bufferModel()->standardSelectionModel()->currentIndex().data(NetworkModel::BufferIdRole).value<BufferId>())
223       return false;
224   }
225
226   return true;
227 }
228
229 bool BufferViewFilter::filterAcceptNetwork(const QModelIndex &source_index) const {
230   if(!config())
231     return true;
232
233   if(!config()->networkId().isValid()) {
234     return true;
235   } else {
236     return config()->networkId() == sourceModel()->data(source_index, NetworkModel::NetworkIdRole).value<NetworkId>();
237   }
238 }
239
240 bool BufferViewFilter::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const {
241   QModelIndex child = sourceModel()->index(source_row, 0, source_parent);
242
243   if(!child.isValid()) {
244     qWarning() << "filterAcceptsRow has been called with an invalid Child";
245     return false;
246   }
247
248   if(!source_parent.isValid())
249     return filterAcceptNetwork(child);
250   else
251     return filterAcceptBuffer(child);
252 }
253
254 bool BufferViewFilter::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const {
255   int itemType = sourceModel()->data(source_left, NetworkModel::ItemTypeRole).toInt();
256   switch(itemType) {
257   case NetworkModel::NetworkItemType:
258     return networkLessThan(source_left, source_right);
259   case NetworkModel::BufferItemType:
260     return bufferLessThan(source_left, source_right);
261   default:
262     return QSortFilterProxyModel::lessThan(source_left, source_right);
263   }
264 }
265
266 bool BufferViewFilter::bufferLessThan(const QModelIndex &source_left, const QModelIndex &source_right) const {
267   BufferId leftBufferId = sourceModel()->data(source_left, NetworkModel::BufferIdRole).value<BufferId>();
268   BufferId rightBufferId = sourceModel()->data(source_right, NetworkModel::BufferIdRole).value<BufferId>();
269   if(config()) {
270     return config()->bufferList().indexOf(leftBufferId) < config()->bufferList().indexOf(rightBufferId);
271   } else
272     return bufferIdLessThan(leftBufferId, rightBufferId);
273 }
274
275 bool BufferViewFilter::networkLessThan(const QModelIndex &source_left, const QModelIndex &source_right) const {
276   NetworkId leftNetworkId = sourceModel()->data(source_left, NetworkModel::NetworkIdRole).value<NetworkId>();
277   NetworkId rightNetworkId = sourceModel()->data(source_right, NetworkModel::NetworkIdRole).value<NetworkId>();
278
279   if(config() && config()->sortAlphabetically())
280     return QSortFilterProxyModel::lessThan(source_left, source_right);
281   else
282     return leftNetworkId < rightNetworkId;
283 }
284
285 QVariant BufferViewFilter::data(const QModelIndex &index, int role) const {
286   switch(role) {
287   case Qt::ForegroundRole:
288     return foreground(index);
289   case Qt::BackgroundRole:
290     if(index.data(NetworkModel::UserAwayRole).toBool()) {
291       QLinearGradient gradient(0, 0, 0, 18);
292       gradient.setColorAt(0.4, QApplication::palette().color(QPalette::Normal, QPalette::Base));
293       gradient.setColorAt(0.5, QApplication::palette().color(QPalette::Disabled, QPalette::Base));
294       gradient.setColorAt(0.6, QApplication::palette().color(QPalette::Normal, QPalette::Base));
295       return QBrush(gradient);
296     }
297     // else: fallthrough to default
298   default:
299     return QSortFilterProxyModel::data(index, role);
300   }
301 }
302
303 QVariant BufferViewFilter::foreground(const QModelIndex &index) const {
304   if(!index.data(NetworkModel::ItemActiveRole).toBool())
305     return _FgColorInactiveActivity;
306
307   Buffer::ActivityLevel activity = (Buffer::ActivityLevel)index.data(NetworkModel::BufferActivityRole).toInt();
308
309   if(activity & Buffer::Highlight)
310     return _FgColorHighlightActivity;
311   if(activity & Buffer::NewMessage)
312     return _FgColorNewMessageActivity;
313   if(activity & Buffer::OtherActivity)
314     return _FgColorOtherActivity;
315
316   return _FgColorNoActivity;
317 }
318
319 void BufferViewFilter::checkPreviousCurrentForRemoval(const QModelIndex &current, const QModelIndex &previous) {
320   Q_UNUSED(current);
321   if(previous.isValid())
322     QCoreApplication::postEvent(this, new CheckRemovalEvent(previous));
323 }
324
325 void BufferViewFilter::customEvent(QEvent *event) {
326   if(event->type() != QEvent::User)
327     return;
328
329   CheckRemovalEvent *removalEvent = static_cast<CheckRemovalEvent *>(event);
330   checkItemForRemoval(removalEvent->index);
331
332   event->accept();
333 }
334
335 void BufferViewFilter::checkItemsForRemoval(const QModelIndex &topLeft, const QModelIndex &bottomRight) {
336   QModelIndex source_topLeft = mapToSource(topLeft);
337   QModelIndex source_bottomRight = mapToSource(bottomRight);
338   emit _dataChanged(source_topLeft, source_bottomRight);
339 }
340
341 // ******************************
342 //  Helper
343 // ******************************
344 bool bufferIdLessThan(const BufferId &left, const BufferId &right) {
345   Q_CHECK_PTR(Client::networkModel());
346   if(!Client::networkModel())
347     return true;
348
349   QModelIndex leftIndex = Client::networkModel()->bufferIndex(left);
350   QModelIndex rightIndex = Client::networkModel()->bufferIndex(right);
351
352   int leftType = Client::networkModel()->data(leftIndex, NetworkModel::BufferTypeRole).toInt();
353   int rightType = Client::networkModel()->data(rightIndex, NetworkModel::BufferTypeRole).toInt();
354
355   if(leftType != rightType)
356     return leftType < rightType;
357   else
358     return QString::compare(Client::networkModel()->data(leftIndex, Qt::DisplayRole).toString(), Client::networkModel()->data(rightIndex, Qt::DisplayRole).toString(), Qt::CaseInsensitive) < 0;
359 }
360