uisupport: Provide helpers for dealing with widget changes
[quassel.git] / src / client / bufferviewoverlay.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 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 "bufferviewoverlay.h"
22
23 #include <QEvent>
24
25 #include "bufferviewconfig.h"
26 #include "client.h"
27 #include "clientbacklogmanager.h"
28 #include "clientbufferviewmanager.h"
29 #include "networkmodel.h"
30
31 const int BufferViewOverlay::_updateEventId = QEvent::registerEventType();
32
33 BufferViewOverlay::BufferViewOverlay(QObject *parent)
34     : QObject(parent)
35 {
36 }
37
38
39 void BufferViewOverlay::reset()
40 {
41     _aboutToUpdate = false;
42
43     _bufferViewIds.clear();
44     _uninitializedViewCount = 0;
45
46     _networkIds.clear();
47     _allowedBufferTypes = 0;
48     _minimumActivity = 0;
49
50     _buffers.clear();
51     _removedBuffers.clear();
52     _tempRemovedBuffers.clear();
53 }
54
55
56 void BufferViewOverlay::save()
57 {
58     CoreAccountSettings().setBufferViewOverlay(_bufferViewIds);
59 }
60
61
62 void BufferViewOverlay::restore()
63 {
64     QSet<int> currentIds = _bufferViewIds;
65     reset();
66     currentIds += CoreAccountSettings().bufferViewOverlay();
67
68     QSet<int>::const_iterator iter;
69     for (iter = currentIds.constBegin(); iter != currentIds.constEnd(); ++iter) {
70         addView(*iter);
71     }
72 }
73
74
75 void BufferViewOverlay::addView(int viewId)
76 {
77     if (_bufferViewIds.contains(viewId))
78         return;
79
80     BufferViewConfig *config = Client::bufferViewManager()->bufferViewConfig(viewId);
81     if (!config) {
82         qDebug() << "BufferViewOverlay::addView(): no such buffer view:" << viewId;
83         return;
84     }
85
86     _bufferViewIds << viewId;
87     bool wasInitialized = isInitialized();
88     _uninitializedViewCount++;
89
90     if (config->isInitialized()) {
91         viewInitialized(config);
92
93         if (wasInitialized) {
94             BufferIdList buffers;
95             if (config->networkId().isValid()) {
96                 foreach(BufferId bufferId, config->bufferList()) {
97                     if (Client::networkModel()->networkId(bufferId) == config->networkId())
98                         buffers << bufferId;
99                 }
100                 foreach(BufferId bufferId, config->temporarilyRemovedBuffers().toList()) {
101                     if (Client::networkModel()->networkId(bufferId) == config->networkId())
102                         buffers << bufferId;
103                 }
104             }
105             else {
106                 buffers = BufferIdList::fromSet(config->bufferList().toSet() + config->temporarilyRemovedBuffers());
107             }
108             Client::backlogManager()->checkForBacklog(buffers);
109         }
110     }
111     else {
112         // we use a queued connection here since manipulating the connection list of a sending object
113         // doesn't seem to be such a good idea while executing a connected slots.
114         connect(config, &BufferViewConfig::initDone, this, selectOverload<>(&BufferViewOverlay::viewInitialized), Qt::QueuedConnection);
115     }
116     save();
117 }
118
119
120 void BufferViewOverlay::removeView(int viewId)
121 {
122     if (!_bufferViewIds.contains(viewId))
123         return;
124
125     _bufferViewIds.remove(viewId);
126     BufferViewConfig *config = Client::bufferViewManager()->bufferViewConfig(viewId);
127     if (config)
128         disconnect(config, nullptr, this, nullptr);
129
130     // update initialized State:
131     bool wasInitialized = isInitialized();
132     _uninitializedViewCount = 0;
133     QSet<int>::iterator viewIter = _bufferViewIds.begin();
134     while (viewIter != _bufferViewIds.end()) {
135         config = Client::bufferViewManager()->bufferViewConfig(*viewIter);
136         if (!config) {
137             viewIter = _bufferViewIds.erase(viewIter);
138         }
139         else {
140             if (!config->isInitialized())
141                 _uninitializedViewCount++;
142             ++viewIter;
143         }
144     }
145
146     update();
147     if (!wasInitialized && isInitialized())
148         emit initDone();
149     save();
150 }
151
152
153 void BufferViewOverlay::viewInitialized(BufferViewConfig *config)
154 {
155     if (!config) {
156         qWarning() << "BufferViewOverlay::viewInitialized() received invalid view!";
157         return;
158     }
159     connect(config, &BufferViewConfig::configChanged, this, &BufferViewOverlay::update);
160
161     // check if the view was removed in the meantime...
162     if (_bufferViewIds.contains(config->bufferViewId()))
163         update();
164
165     _uninitializedViewCount--;
166     if (isInitialized())
167         emit initDone();
168 }
169
170
171 void BufferViewOverlay::viewInitialized()
172 {
173     auto *config = qobject_cast<BufferViewConfig *>(sender());
174     Q_ASSERT(config);
175
176     viewInitialized(config);
177 }
178
179
180 void BufferViewOverlay::update()
181 {
182     if (_aboutToUpdate) {
183         return;
184     }
185     _aboutToUpdate = true;
186     QCoreApplication::postEvent(this, new QEvent((QEvent::Type)_updateEventId));
187 }
188
189
190 void BufferViewOverlay::updateHelper()
191 {
192     if (!_aboutToUpdate)
193         return;
194
195     bool changed = false;
196
197     int allowedBufferTypes = 0;
198     int minimumActivity = -1;
199     QSet<NetworkId> networkIds;
200     QSet<BufferId> buffers;
201     QSet<BufferId> removedBuffers;
202     QSet<BufferId> tempRemovedBuffers;
203
204     if (Client::bufferViewManager()) {
205         BufferViewConfig *config = nullptr;
206         QSet<int>::const_iterator viewIter;
207         for (viewIter = _bufferViewIds.constBegin(); viewIter != _bufferViewIds.constEnd(); ++viewIter) {
208             config = Client::bufferViewManager()->bufferViewConfig(*viewIter);
209             if (!config)
210                 continue;
211
212             allowedBufferTypes |= config->allowedBufferTypes();
213             if (minimumActivity == -1 || config->minimumActivity() < minimumActivity)
214                 minimumActivity = config->minimumActivity();
215
216             networkIds << config->networkId();
217
218             // we have to apply several filters before we can add a buffer to a category (visible, removed, ...)
219             buffers += filterBuffersByConfig(config->bufferList(), config);
220             tempRemovedBuffers += filterBuffersByConfig(config->temporarilyRemovedBuffers().toList(), config);
221             removedBuffers += config->removedBuffers();
222         }
223
224         // prune the sets from overlap
225         QSet<BufferId> availableBuffers = Client::networkModel()->allBufferIds().toSet();
226
227         buffers.intersect(availableBuffers);
228
229         tempRemovedBuffers.intersect(availableBuffers);
230         tempRemovedBuffers.subtract(buffers);
231
232         removedBuffers.intersect(availableBuffers);
233         removedBuffers.subtract(tempRemovedBuffers);
234         removedBuffers.subtract(buffers);
235     }
236
237     changed |= (allowedBufferTypes != _allowedBufferTypes);
238     changed |= (minimumActivity != _minimumActivity);
239     changed |= (networkIds != _networkIds);
240     changed |= (buffers != _buffers);
241     changed |= (removedBuffers != _removedBuffers);
242     changed |= (tempRemovedBuffers != _tempRemovedBuffers);
243
244     _allowedBufferTypes = allowedBufferTypes;
245     _minimumActivity = minimumActivity;
246     _networkIds = networkIds;
247     _buffers = buffers;
248     _removedBuffers = removedBuffers;
249     _tempRemovedBuffers = tempRemovedBuffers;
250
251     _aboutToUpdate = false;
252
253     if (changed)
254         emit hasChanged();
255 }
256
257
258 QSet<BufferId> BufferViewOverlay::filterBuffersByConfig(const QList<BufferId> &buffers, const BufferViewConfig *config)
259 {
260     Q_ASSERT(config);
261
262     QSet<BufferId> bufferIds;
263     BufferInfo bufferInfo;
264     foreach(BufferId bufferId, buffers) {
265         bufferInfo = Client::networkModel()->bufferInfo(bufferId);
266         if (!(bufferInfo.type() & config->allowedBufferTypes()))
267             continue;
268         if (config->networkId().isValid() && bufferInfo.networkId() != config->networkId())
269             continue;
270         bufferIds << bufferId;
271     }
272
273     return bufferIds;
274 }
275
276
277 void BufferViewOverlay::customEvent(QEvent *event)
278 {
279     if (event->type() == _updateEventId) {
280         updateHelper();
281     }
282 }
283
284
285 bool BufferViewOverlay::allNetworks()
286 {
287     updateHelper();
288     return _networkIds.contains(NetworkId());
289 }
290
291
292 const QSet<NetworkId> &BufferViewOverlay::networkIds()
293 {
294     updateHelper();
295     return _networkIds;
296 }
297
298
299 const QSet<BufferId> &BufferViewOverlay::bufferIds()
300 {
301     updateHelper();
302     return _buffers;
303 }
304
305
306 const QSet<BufferId> &BufferViewOverlay::removedBufferIds()
307 {
308     updateHelper();
309     return _removedBuffers;
310 }
311
312
313 const QSet<BufferId> &BufferViewOverlay::tempRemovedBufferIds()
314 {
315     updateHelper();
316     return _tempRemovedBuffers;
317 }
318
319
320 int BufferViewOverlay::allowedBufferTypes()
321 {
322     updateHelper();
323     return _allowedBufferTypes;
324 }
325
326
327 int BufferViewOverlay::minimumActivity()
328 {
329     updateHelper();
330     return _minimumActivity;
331 }