1 | /* |
2 | * Copyright 2006-2007 Aaron Seigo <aseigo@kde.org> |
3 | * |
4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU Library General Public License as |
6 | * published by the Free Software Foundation; either version 2, or |
7 | * (at your option) any later version. |
8 | * |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details |
13 | * |
14 | * You should have received a copy of the GNU Library General Public |
15 | * License along with this program; if not, write to the |
16 | * Free Software Foundation, Inc., |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18 | */ |
19 | #include "datacontainer.h" |
20 | #include "private/datacontainer_p.h" |
21 | #include "private/storage_p.h" |
22 | |
23 | #include <kdebug.h> |
24 | |
25 | #include "plasma.h" |
26 | |
27 | namespace Plasma |
28 | { |
29 | |
30 | DataContainer::DataContainer(QObject *parent) |
31 | : QObject(parent), |
32 | d(new DataContainerPrivate(this)) |
33 | { |
34 | } |
35 | |
36 | DataContainer::~DataContainer() |
37 | { |
38 | delete d; |
39 | } |
40 | |
41 | const DataEngine::Data DataContainer::data() const |
42 | { |
43 | return d->data; |
44 | } |
45 | |
46 | void DataContainer::setData(const QString &key, const QVariant &value) |
47 | { |
48 | if (!value.isValid()) { |
49 | d->data.remove(key); |
50 | } else { |
51 | d->data.insert(key, value); |
52 | } |
53 | |
54 | d->dirty = true; |
55 | d->updateTimer.start(); |
56 | |
57 | //check if storage is enabled and if storage is needed. |
58 | //If it is not set to be stored,then this is the first |
59 | //setData() since the last time it was stored. This |
60 | //gives us only one singleShot timer. |
61 | if (isStorageEnabled() || !needsToBeStored()) { |
62 | d->storageTimer.start(180000, this); |
63 | } |
64 | |
65 | setNeedsToBeStored(true); |
66 | } |
67 | |
68 | void DataContainer::removeAllData() |
69 | { |
70 | if (d->data.isEmpty()) { |
71 | // avoid an update if we don't have any data anyways |
72 | return; |
73 | } |
74 | |
75 | d->data.clear(); |
76 | d->dirty = true; |
77 | d->updateTimer.start(); |
78 | } |
79 | |
80 | bool DataContainer::visualizationIsConnected(QObject *visualization) const |
81 | { |
82 | return d->relayObjects.contains(visualization); |
83 | } |
84 | |
85 | void DataContainer::connectVisualization(QObject *visualization, uint pollingInterval, |
86 | Plasma::IntervalAlignment alignment) |
87 | { |
88 | //kDebug() << "connecting visualization" << visualization << "at interval of" |
89 | // << pollingInterval << "to" << objectName(); |
90 | QMap<QObject *, SignalRelay *>::iterator objIt = d->relayObjects.find(visualization); |
91 | bool connected = objIt != d->relayObjects.end(); |
92 | if (connected) { |
93 | // this visualization is already connected. just adjust the update |
94 | // frequency if necessary |
95 | SignalRelay *relay = objIt.value(); |
96 | if (relay) { |
97 | // connected to a relay |
98 | //kDebug() << " already connected, but to a relay"; |
99 | if (relay->m_interval == pollingInterval) { |
100 | //kDebug() << " already connected to a relay of the same interval of" |
101 | // << pollingInterval << ", nothing to do"; |
102 | return; |
103 | } |
104 | |
105 | if (relay->receiverCount() == 1) { |
106 | //kDebug() << " removing relay, as it is now unused"; |
107 | d->relays.remove(relay->m_interval); |
108 | delete relay; |
109 | } else { |
110 | disconnect(relay, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)), |
111 | visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data))); |
112 | //relay->isUnused(); |
113 | } |
114 | } else if (pollingInterval < 1) { |
115 | // the visualization was connected already, but not to a relay |
116 | // and it still doesn't want to connect to a relay, so we have |
117 | // nothing to do! |
118 | //kDebug() << " already connected, nothing to do"; |
119 | return; |
120 | } else { |
121 | disconnect(this, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)), |
122 | visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data))); |
123 | } |
124 | } else { |
125 | connect(visualization, SIGNAL(destroyed(QObject*)), |
126 | this, SLOT(disconnectVisualization(QObject*)));//, Qt::QueuedConnection); |
127 | } |
128 | |
129 | if (pollingInterval < 1) { |
130 | //kDebug() << " connecting directly"; |
131 | d->relayObjects[visualization] = 0; |
132 | connect(this, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)), |
133 | visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data))); |
134 | } else { |
135 | //kDebug() << " connecting to a relay"; |
136 | // we only want to do an imediate update if this is not the first object to connect to us |
137 | // if it is the first visualization, then the source will already have been populated |
138 | // engine's sourceRequested method |
139 | bool immediateUpdate = connected || d->relayObjects.count() > 1; |
140 | SignalRelay *relay = d->signalRelay(this, visualization, pollingInterval, |
141 | alignment, immediateUpdate); |
142 | connect(relay, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)), |
143 | visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data))); |
144 | } |
145 | } |
146 | |
147 | void DataContainer::setStorageEnabled(bool store) |
148 | { |
149 | QTime time = QTime::currentTime(); |
150 | qsrand((uint)time.msec()); |
151 | d->enableStorage = store; |
152 | if (store) { |
153 | QTimer::singleShot(qrand() % (2000 + 1) , this, SLOT(retrieve())); |
154 | } |
155 | } |
156 | |
157 | bool DataContainer::isStorageEnabled() const |
158 | { |
159 | return d->enableStorage; |
160 | } |
161 | |
162 | bool DataContainer::needsToBeStored() const |
163 | { |
164 | return !d->isStored; |
165 | } |
166 | |
167 | void DataContainer::setNeedsToBeStored(bool store) |
168 | { |
169 | d->isStored = !store; |
170 | } |
171 | |
172 | DataEngine* DataContainer::getDataEngine() |
173 | { |
174 | QObject *o = this; |
175 | DataEngine *de = NULL; |
176 | while (de == NULL) |
177 | { |
178 | o = dynamic_cast<QObject *> (o->parent()); |
179 | if (o == NULL) { |
180 | return NULL; |
181 | } |
182 | de = dynamic_cast<DataEngine *> (o); |
183 | } |
184 | return de; |
185 | } |
186 | |
187 | void DataContainerPrivate::store() |
188 | { |
189 | if (!q->needsToBeStored() || !q->isStorageEnabled()) { |
190 | return; |
191 | } |
192 | |
193 | DataEngine* de = q->getDataEngine(); |
194 | if (!de) { |
195 | return; |
196 | } |
197 | |
198 | q->setNeedsToBeStored(false); |
199 | |
200 | if (!storage) { |
201 | storage = new Storage(q); |
202 | } |
203 | |
204 | KConfigGroup op = storage->operationDescription("save" ); |
205 | op.writeEntry("group" , q->objectName()); |
206 | StorageJob *job = static_cast<StorageJob *>(storage->startOperationCall(op)); |
207 | job->setData(data); |
208 | storageCount++; |
209 | QObject::connect(job, SIGNAL(finished(KJob*)), q, SLOT(storeJobFinished(KJob*))); |
210 | } |
211 | |
212 | void DataContainerPrivate::storeJobFinished(KJob* ) |
213 | { |
214 | --storageCount; |
215 | if (storageCount < 1) { |
216 | storage->deleteLater(); |
217 | storage = 0; |
218 | } |
219 | } |
220 | |
221 | void DataContainerPrivate::retrieve() |
222 | { |
223 | DataEngine* de = q->getDataEngine(); |
224 | if (de == NULL) { |
225 | return; |
226 | } |
227 | |
228 | if (!storage) { |
229 | storage = new Storage(q); |
230 | } |
231 | |
232 | KConfigGroup retrieveGroup = storage->operationDescription("retrieve" ); |
233 | retrieveGroup.writeEntry("group" , q->objectName()); |
234 | ServiceJob* retrieveJob = storage->startOperationCall(retrieveGroup); |
235 | QObject::connect(retrieveJob, SIGNAL(result(KJob*)), q, |
236 | SLOT(populateFromStoredData(KJob*))); |
237 | } |
238 | |
239 | void DataContainerPrivate::populateFromStoredData(KJob *job) |
240 | { |
241 | if (job->error()) { |
242 | return; |
243 | } |
244 | |
245 | StorageJob *ret = dynamic_cast<StorageJob*>(job); |
246 | if (!ret) { |
247 | return; |
248 | } |
249 | |
250 | // Only fill the source with old stored |
251 | // data if it is not already populated with new data. |
252 | if (data.isEmpty() && !ret->data().isEmpty()) { |
253 | data = ret->data(); |
254 | dirty = true; |
255 | q->forceImmediateUpdate(); |
256 | } |
257 | |
258 | KConfigGroup expireGroup = storage->operationDescription("expire" ); |
259 | //expire things older than 4 days |
260 | expireGroup.writeEntry("age" , 345600); |
261 | storage->startOperationCall(expireGroup); |
262 | } |
263 | |
264 | void DataContainer::disconnectVisualization(QObject *visualization) |
265 | { |
266 | QMap<QObject *, SignalRelay *>::iterator objIt = d->relayObjects.find(visualization); |
267 | disconnect(visualization, SIGNAL(destroyed(QObject*)), |
268 | this, SLOT(disconnectVisualization(QObject*)));//, Qt::QueuedConnection); |
269 | |
270 | if (objIt == d->relayObjects.end() || !objIt.value()) { |
271 | // it is connected directly to the DataContainer itself |
272 | disconnect(this, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)), |
273 | visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data))); |
274 | } else { |
275 | SignalRelay *relay = objIt.value(); |
276 | |
277 | if (relay->receiverCount() == 1) { |
278 | d->relays.remove(relay->m_interval); |
279 | delete relay; |
280 | } else { |
281 | disconnect(relay, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)), |
282 | visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data))); |
283 | } |
284 | } |
285 | |
286 | d->relayObjects.erase(objIt); |
287 | checkUsage(); |
288 | } |
289 | |
290 | void DataContainer::checkForUpdate() |
291 | { |
292 | //kDebug() << objectName() << d->dirty; |
293 | if (d->dirty) { |
294 | emit dataUpdated(objectName(), d->data); |
295 | |
296 | foreach (SignalRelay *relay, d->relays) { |
297 | relay->checkQueueing(); |
298 | } |
299 | |
300 | d->dirty = false; |
301 | } |
302 | } |
303 | |
304 | void DataContainer::forceImmediateUpdate() |
305 | { |
306 | if (d->dirty) { |
307 | d->dirty = false; |
308 | emit dataUpdated(objectName(), d->data); |
309 | } |
310 | |
311 | foreach (SignalRelay *relay, d->relays) { |
312 | relay->forceImmediateUpdate(); |
313 | } |
314 | } |
315 | |
316 | uint DataContainer::timeSinceLastUpdate() const |
317 | { |
318 | return d->updateTimer.elapsed(); |
319 | } |
320 | |
321 | void DataContainer::setNeedsUpdate(bool update) |
322 | { |
323 | d->cached = update; |
324 | } |
325 | |
326 | void DataContainer::checkUsage() |
327 | { |
328 | if (!d->checkUsageTimer.isActive()) { |
329 | d->checkUsageTimer.start(10, this); |
330 | } |
331 | } |
332 | |
333 | void DataContainer::timerEvent(QTimerEvent * event) |
334 | { |
335 | if (event->timerId() == d->checkUsageTimer.timerId()) { |
336 | if (d->relays.count() < 1 && |
337 | receivers(SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data))) < 1) { |
338 | // DO NOT CALL ANYTHING AFTER THIS LINE AS IT MAY GET DELETED! |
339 | kDebug() << objectName() << "is unused" ; |
340 | emit becameUnused(objectName()); |
341 | } |
342 | d->checkUsageTimer.stop(); |
343 | } else if (event->timerId() == d->storageTimer.timerId()) { |
344 | d->store(); |
345 | d->storageTimer.stop(); |
346 | } |
347 | } |
348 | |
349 | } // Plasma namespace |
350 | |
351 | #include "datacontainer.moc" |
352 | |
353 | |