Plasma
datacontainer.cpp
Go to the documentation of this file.
00001 /* 00002 * Copyright 2006-2007 Aaron Seigo <aseigo@kde.org> 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU Library General Public License as 00006 * published by the Free Software Foundation; either version 2, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details 00013 * 00014 * You should have received a copy of the GNU Library General Public 00015 * License along with this program; if not, write to the 00016 * Free Software Foundation, Inc., 00017 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00018 */ 00019 #include "datacontainer.h" 00020 #include "private/datacontainer_p.h" 00021 #include "private/storage_p.h" 00022 00023 #include <kdebug.h> 00024 00025 #include "plasma.h" 00026 00027 namespace Plasma 00028 { 00029 00030 DataContainer::DataContainer(QObject *parent) 00031 : QObject(parent), 00032 d(new DataContainerPrivate(this)) 00033 { 00034 } 00035 00036 DataContainer::~DataContainer() 00037 { 00038 delete d; 00039 } 00040 00041 const DataEngine::Data DataContainer::data() const 00042 { 00043 return d->data; 00044 } 00045 00046 void DataContainer::setData(const QString &key, const QVariant &value) 00047 { 00048 if (!value.isValid()) { 00049 d->data.remove(key); 00050 } else { 00051 d->data.insert(key, value); 00052 } 00053 00054 d->dirty = true; 00055 d->updateTs.start(); 00056 00057 //check if storage is enabled and if storage is needed. 00058 //If it is not set to be stored,then this is the first 00059 //setData() since the last time it was stored. This 00060 //gives us only one singleShot timer. 00061 if (isStorageEnabled() || !needsToBeStored()) { 00062 QTimer::singleShot(180000, this, SLOT(store())); 00063 } 00064 00065 setNeedsToBeStored(true); 00066 } 00067 00068 void DataContainer::removeAllData() 00069 { 00070 if (d->data.isEmpty()) { 00071 // avoid an update if we don't have any data anyways 00072 return; 00073 } 00074 00075 d->data.clear(); 00076 d->dirty = true; 00077 d->updateTs.start(); 00078 } 00079 00080 bool DataContainer::visualizationIsConnected(QObject *visualization) const 00081 { 00082 return d->relayObjects.contains(visualization); 00083 } 00084 00085 void DataContainer::connectVisualization(QObject *visualization, uint pollingInterval, 00086 Plasma::IntervalAlignment alignment) 00087 { 00088 //kDebug() << "connecting visualization" << visualization << "at interval of" 00089 // << pollingInterval << "to" << objectName(); 00090 QMap<QObject *, SignalRelay *>::iterator objIt = d->relayObjects.find(visualization); 00091 bool connected = objIt != d->relayObjects.end(); 00092 if (connected) { 00093 // this visualization is already connected. just adjust the update 00094 // frequency if necessary 00095 SignalRelay *relay = objIt.value(); 00096 if (relay) { 00097 // connected to a relay 00098 //kDebug() << " already connected, but to a relay"; 00099 if (relay->m_interval == pollingInterval) { 00100 //kDebug() << " already connected to a relay of the same interval of" 00101 // << pollingInterval << ", nothing to do"; 00102 return; 00103 } 00104 00105 if (relay->receiverCount() == 1) { 00106 //kDebug() << " removing relay, as it is now unused"; 00107 d->relays.remove(relay->m_interval); 00108 delete relay; 00109 } else { 00110 disconnect(relay, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)), 00111 visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data))); 00112 //relay->isUnused(); 00113 } 00114 } else if (pollingInterval < 1) { 00115 // the visualization was connected already, but not to a relay 00116 // and it still doesn't want to connect to a relay, so we have 00117 // nothing to do! 00118 //kDebug() << " already connected, nothing to do"; 00119 return; 00120 } else { 00121 disconnect(this, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)), 00122 visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data))); 00123 } 00124 } else { 00125 connect(visualization, SIGNAL(destroyed(QObject*)), 00126 this, SLOT(disconnectVisualization(QObject*)));//, Qt::QueuedConnection); 00127 } 00128 00129 if (pollingInterval < 1) { 00130 //kDebug() << " connecting directly"; 00131 d->relayObjects[visualization] = 0; 00132 connect(this, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)), 00133 visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data))); 00134 } else { 00135 //kDebug() << " connecting to a relay"; 00136 // we only want to do an imediate update if this is not the first object to connect to us 00137 // if it is the first visualization, then the source will already have been populated 00138 // engine's sourceRequested method 00139 bool immediateUpdate = connected || d->relayObjects.count() > 1; 00140 SignalRelay *relay = d->signalRelay(this, visualization, pollingInterval, 00141 alignment, immediateUpdate); 00142 connect(relay, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)), 00143 visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data))); 00144 } 00145 } 00146 00147 void DataContainer::setStorageEnabled(bool store) 00148 { 00149 QTime time = QTime::currentTime(); 00150 qsrand((uint)time.msec()); 00151 d->enableStorage = store; 00152 if (store) { 00153 QTimer::singleShot(qrand() % (2000 + 1) , this, SLOT(retrieve())); 00154 } 00155 } 00156 00157 bool DataContainer::isStorageEnabled() const 00158 { 00159 return d->enableStorage; 00160 } 00161 00162 bool DataContainer::needsToBeStored() const 00163 { 00164 return !d->isStored; 00165 } 00166 00167 void DataContainer::setNeedsToBeStored(bool store) 00168 { 00169 d->isStored = !store; 00170 } 00171 00172 DataEngine* DataContainer::getDataEngine() 00173 { 00174 QObject *o = NULL; 00175 DataEngine *de = NULL; 00176 o = this; 00177 while (de == NULL) 00178 { 00179 o = dynamic_cast<QObject *> (o->parent()); 00180 if (o == NULL) { 00181 return NULL; 00182 } 00183 de = dynamic_cast<DataEngine *> (o); 00184 } 00185 return de; 00186 } 00187 00188 void DataContainerPrivate::store() 00189 { 00190 if (!q->needsToBeStored() || !q->isStorageEnabled()){ 00191 return; 00192 } 00193 00194 DataEngine* de = q->getDataEngine(); 00195 if (de == NULL) { 00196 return; 00197 } 00198 00199 q->setNeedsToBeStored(false); 00200 00201 if (storage == NULL) { 00202 storage = new Storage(q); 00203 } 00204 00205 KConfigGroup op = storage->operationDescription("save"); 00206 op.writeEntry("group", q->objectName()); 00207 DataEngine::Data dataToStore = q->data(); 00208 DataEngine::Data::const_iterator it = dataToStore.constBegin(); 00209 while (it != dataToStore.constEnd() && dataToStore.constEnd() == q->data().constEnd()) { 00210 QVariant v = it.value(); 00211 if ((it.value().type() == QVariant::String) || (it.value().type() == QVariant::Int)) { 00212 op.writeEntry("key", it.key()); 00213 op.writeEntry("data", it.value()); 00214 } else { 00215 QByteArray b; 00216 QDataStream ds(&b, QIODevice::WriteOnly); 00217 ds << it.value(); 00218 op.writeEntry("key", QString(QLatin1String("base64-") + it.key())); 00219 op.writeEntry("data", b.toBase64()); 00220 } 00221 ++it; 00222 if (storage == NULL) { 00223 storage = new Storage(q); 00224 } 00225 ServiceJob* job = storage->startOperationCall(op); 00226 storageCount++; 00227 QObject::connect(job, SIGNAL(finished(KJob*)), q, SLOT(storeJobFinished(KJob*))); 00228 } 00229 } 00230 00231 void DataContainerPrivate::storeJobFinished(KJob* ) 00232 { 00233 --storageCount; 00234 if (storageCount < 1) { 00235 storage->deleteLater(); 00236 storage = 0; 00237 } 00238 } 00239 00240 void DataContainerPrivate::retrieve() 00241 { 00242 DataEngine* de = q->getDataEngine(); 00243 if (de == NULL) { 00244 return; 00245 } 00246 if (!storage) { 00247 storage = new Storage(q); 00248 } 00249 00250 KConfigGroup retrieveGroup = storage->operationDescription("retrieve"); 00251 retrieveGroup.writeEntry("group", q->objectName()); 00252 ServiceJob* retrieveJob = storage->startOperationCall(retrieveGroup); 00253 QObject::connect(retrieveJob, SIGNAL(result(KJob*)), q, 00254 SLOT(populateFromStoredData(KJob*))); 00255 } 00256 00257 void DataContainerPrivate::populateFromStoredData(KJob *job) 00258 { 00259 if (job->error()) { 00260 return; 00261 } 00262 00263 DataEngine::Data dataToInsert; 00264 ServiceJob* ret = dynamic_cast<ServiceJob*>(job); 00265 const QHash<QString, QVariant> h = ret->result().toHash(); 00266 QHash<QString, QVariant>::const_iterator it = h.begin(); 00267 QHash<QString, QVariant>::const_iterator itEnd = h.end(); 00268 for ( ; it != itEnd; ++it) { 00269 QString key = it.key(); 00270 if (key.startsWith("base64-")) { 00271 QByteArray b = QByteArray::fromBase64(it.value().toString().toAscii()); 00272 QDataStream ds(&b, QIODevice::ReadOnly); 00273 QVariant v(ds); 00274 key.remove(0, 7); 00275 dataToInsert.insert(key, v); 00276 } else { 00277 dataToInsert.insert(key, it.value()); 00278 } 00279 } 00280 00281 KConfigGroup expireGroup = storage->operationDescription("expire"); 00282 //expire things older than 4 days 00283 expireGroup.writeEntry("age", 345600); 00284 storage->startOperationCall(expireGroup); 00285 00286 if (!(data.isEmpty())) { 00287 //Do not fill the source with old stored 00288 //data if it is already populated with new data. 00289 return; 00290 } 00291 00292 data = dataToInsert; 00293 dirty = true; 00294 q->checkForUpdate(); 00295 } 00296 00297 void DataContainer::disconnectVisualization(QObject *visualization) 00298 { 00299 QMap<QObject *, SignalRelay *>::iterator objIt = d->relayObjects.find(visualization); 00300 disconnect(visualization, SIGNAL(destroyed(QObject*)), 00301 this, SLOT(disconnectVisualization(QObject*)));//, Qt::QueuedConnection); 00302 00303 if (objIt == d->relayObjects.end() || !objIt.value()) { 00304 // it is connected directly to the DataContainer itself 00305 disconnect(this, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)), 00306 visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data))); 00307 } else { 00308 SignalRelay *relay = objIt.value(); 00309 00310 if (relay->receiverCount() == 1) { 00311 d->relays.remove(relay->m_interval); 00312 delete relay; 00313 } else { 00314 disconnect(relay, SIGNAL(dataUpdated(QString,Plasma::DataEngine::Data)), 00315 visualization, SLOT(dataUpdated(QString,Plasma::DataEngine::Data))); 00316 } 00317 } 00318 00319 d->relayObjects.erase(objIt); 00320 checkUsage(); 00321 } 00322 00323 void DataContainer::checkForUpdate() 00324 { 00325 //kDebug() << objectName() << d->dirty; 00326 if (d->dirty) { 00327 emit dataUpdated(objectName(), d->data); 00328 00329 foreach (SignalRelay *relay, d->relays) { 00330 relay->checkQueueing(); 00331 } 00332 00333 d->dirty = false; 00334 } 00335 } 00336 00337 void DataContainer::forceImmediateUpdate() 00338 { 00339 if (d->dirty) { 00340 d->dirty = false; 00341 emit dataUpdated(objectName(), d->data); 00342 } 00343 00344 foreach (SignalRelay *relay, d->relays) { 00345 relay->forceImmediateUpdate(); 00346 } 00347 } 00348 00349 uint DataContainer::timeSinceLastUpdate() const 00350 { 00351 //FIXME: we still assume it's been <24h 00352 //and ignore possible daylight savings changes 00353 return d->updateTs.elapsed(); 00354 } 00355 00356 void DataContainer::setNeedsUpdate(bool update) 00357 { 00358 d->cached = update; 00359 } 00360 00361 void DataContainer::checkUsage() 00362 { 00363 if (d->relays.count() < 1 && 00364 receivers(SIGNAL(dataUpdated(QString, Plasma::DataEngine::Data))) < 1) { 00365 // DO NOT CALL ANYTHING AFTER THIS LINE AS IT MAY GET DELETED! 00366 kDebug() << objectName() << "is unused"; 00367 emit becameUnused(objectName()); 00368 } 00369 } 00370 00371 } // Plasma namespace 00372 00373 #include "datacontainer.moc" 00374
KDE 4.6 API Reference