some more win32'fication to fix non-ascii filename handling
[kdelibs.git] / plasma / dataengine.cpp
blob1691a7e8cd7b57320b930c9935240d129d614437
1 /*
2 * Copyright 2006-2007 Aaron Seigo <aseigo@kde.org>
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.
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
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.
20 #include "dataengine.h"
21 #include "private/dataengine_p.h"
23 #include <QQueue>
24 #include <QTimer>
25 #include <QTime>
26 #include <QTimerEvent>
27 #include <QVariant>
29 #include <kdebug.h>
30 #include <kplugininfo.h>
31 #include <kservice.h>
32 #include <kstandarddirs.h>
34 #include "datacontainer.h"
35 #include "package.h"
36 #include "service.h"
37 #include "scripting/dataenginescript.h"
39 #include "private/service_p.h"
41 namespace Plasma
44 DataEngine::DataEngine(QObject *parent, KService::Ptr service)
45 : QObject(parent),
46 d(new DataEnginePrivate(this, service))
48 connect(d->updateTimer, SIGNAL(timeout()), this, SLOT(scheduleSourcesUpdated()));
51 DataEngine::DataEngine(QObject *parent, const QVariantList &args)
52 : QObject(parent),
53 d(new DataEnginePrivate(this, KService::serviceByStorageId(args.count() > 0 ? args[0].toString() : QString())))
55 connect(d->updateTimer, SIGNAL(timeout()), this, SLOT(scheduleSourcesUpdated()));
58 DataEngine::~DataEngine()
60 //kDebug() << objectName() << ": bye bye birdy! ";
61 delete d;
64 QStringList DataEngine::sources() const
66 if (d->script) {
67 return d->script->sources();
68 } else {
69 return d->sources.keys();
73 Service *DataEngine::serviceForSource(const QString &source)
75 if (d->script) {
76 return d->script->serviceForSource(source);
77 } else {
78 return new NullService(source, this);
82 void DataEngine::connectSource(const QString &source, QObject *visualization,
83 uint pollingInterval,
84 Plasma::IntervalAlignment intervalAlignment) const
86 //kDebug() << "connectSource" << source;
87 bool newSource;
88 DataContainer *s = d->requestSource(source, &newSource);
90 if (s) {
91 // we suppress the immediate invocation of dataUpdated here if the
92 // source was prexisting and they don't request delayed updates
93 // (we want to do an immediate update in that case so they don't
94 // have to wait for the first time out)
95 d->connectSource(s, visualization, pollingInterval, intervalAlignment,
96 !newSource || pollingInterval > 0);
97 //kDebug() << " ==> source connected";
101 void DataEngine::connectAllSources(QObject *visualization, uint pollingInterval,
102 Plasma::IntervalAlignment intervalAlignment) const
104 foreach (DataContainer *s, d->sources) {
105 d->connectSource(s, visualization, pollingInterval, intervalAlignment);
109 void DataEngine::disconnectSource(const QString &source, QObject *visualization) const
111 DataContainer *s = d->source(source, false);
113 if (s) {
114 s->disconnectVisualization(visualization);
118 DataContainer *DataEngine::containerForSource(const QString &source)
120 return d->source(source, false);
123 DataEngine::Data DataEngine::query(const QString &source) const
125 bool newSource;
126 DataContainer *s = d->requestSource(source, &newSource);
128 if (!s) {
129 return DataEngine::Data();
130 } else if (!newSource && d->minPollingInterval >= 0 &&
131 s->timeSinceLastUpdate() >= uint(d->minPollingInterval)) {
132 if (const_cast<DataEngine*>(this)->updateSourceEvent(source)) {
133 d->queueUpdate();
137 DataEngine::Data data = s->data();
138 s->checkUsage();
139 return data;
142 void DataEngine::init()
144 if (d->script) {
145 d->script->init();
146 } else {
147 // kDebug() << "called";
148 // default implementation does nothing. this is for engines that have to
149 // start things in motion external to themselves before they can work
153 bool DataEngine::sourceRequestEvent(const QString &name)
155 if (d->script) {
156 return d->script->sourceRequestEvent(name);
157 } else {
158 return false;
162 bool DataEngine::updateSourceEvent(const QString &source)
164 if (d->script) {
165 return d->script->updateSourceEvent(source);
166 } else {
167 //kDebug() << source;
168 return false; //TODO: should this be true to trigger, even needless, updates on every tick?
172 void DataEngine::setData(const QString &source, const QVariant &value)
174 setData(source, source, value);
177 void DataEngine::setData(const QString &source, const QString &key, const QVariant &value)
179 DataContainer *s = d->source(source, false);
180 bool isNew = !s;
182 if (isNew) {
183 s = d->source(source);
186 s->setData(key, value);
188 if (isNew) {
189 emit sourceAdded(source);
192 d->queueUpdate();
195 void DataEngine::setData(const QString &source, const Data &data)
197 DataContainer *s = d->source(source, false);
198 bool isNew = !s;
200 if (isNew) {
201 s = d->source(source);
204 Data::const_iterator it = data.constBegin();
205 while (it != data.constEnd()) {
206 s->setData(it.key(), it.value());
207 ++it;
210 if (isNew) {
211 emit sourceAdded(source);
214 d->queueUpdate();
217 void DataEngine::removeAllData(const QString &source)
219 DataContainer *s = d->source(source, false);
220 if (s) {
221 s->removeAllData();
222 d->queueUpdate();
226 void DataEngine::removeData(const QString &source, const QString &key)
228 DataContainer *s = d->source(source, false);
229 if (s) {
230 s->setData(key, QVariant());
231 d->queueUpdate();
235 void DataEngine::addSource(DataContainer *source)
237 if (d->sources.contains(source->objectName())) {
238 kDebug() << "source named \"" << source->objectName() << "\" already exists.";
239 return;
242 QObject::connect(source, SIGNAL(updateRequested(DataContainer*)),
243 this, SLOT(internalUpdateSource(DataContainer*)));
244 d->sources.insert(source->objectName(), source);
245 emit sourceAdded(source->objectName());
246 d->queueUpdate();
249 void DataEngine::setMaxSourceCount(uint limit)
251 if (d->limit == limit) {
252 return;
255 d->limit = limit;
257 if (d->limit > 0) {
258 d->trimQueue();
259 } else {
260 d->sourceQueue.clear();
264 uint DataEngine::maxSourceCount() const
266 return d->limit;
269 void DataEngine::setMinimumPollingInterval(int minimumMs)
271 if (minimumMs < 0) {
272 minimumMs = 0;
275 d->minPollingInterval = minimumMs;
278 int DataEngine::minimumPollingInterval() const
280 return d->minPollingInterval;
283 void DataEngine::setPollingInterval(uint frequency)
285 killTimer(d->updateTimerId);
286 d->updateTimerId = 0;
288 if (frequency > 0) {
289 d->updateTimerId = startTimer(frequency);
294 NOTE: This is not implemented to prevent having to store the value internally.
295 When there is a good use case for needing access to this value, we can
296 add another member to the Private class and add this method.
298 void DataEngine::pollingInterval()
300 return d->pollingInterval;
304 void DataEngine::removeSource(const QString &source)
306 //kDebug() << "removing source " << source;
307 SourceDict::iterator it = d->sources.find(source);
308 if (it != d->sources.end()) {
309 DataContainer *s = it.value();
311 // remove it from the limit queue if we're keeping one
312 if (d->limit > 0) {
313 QQueue<DataContainer*>::iterator it = d->sourceQueue.begin();
314 while (it != d->sourceQueue.end()) {
315 if (*it == s) {
316 d->sourceQueue.erase(it);
317 break;
319 ++it;
323 s->deleteLater();
324 d->sources.erase(it);
325 emit sourceRemoved(source);
329 void DataEngine::removeAllSources()
331 QMutableHashIterator<QString, Plasma::DataContainer*> it(d->sources);
332 while (it.hasNext()) {
333 it.next();
334 emit sourceRemoved(it.key());
335 delete it.value();
336 it.remove();
340 bool DataEngine::isValid() const
342 return d->valid;
345 bool DataEngine::isEmpty() const
347 return d->sources.isEmpty();
350 void DataEngine::setValid(bool valid)
352 d->valid = valid;
355 DataEngine::SourceDict DataEngine::containerDict() const
357 return d->sources;
360 void DataEngine::timerEvent(QTimerEvent *event)
362 if (event->timerId() != d->updateTimerId) {
363 kDebug() << "bzzzt";
364 return;
367 event->accept();
369 // if the freq update is less than 0, don't bother
370 if (d->minPollingInterval < 0) {
371 //kDebug() << "uh oh.. no polling allowed!";
372 return;
375 // minPollingInterval
376 if (d->updateTimestamp.elapsed() < d->minPollingInterval) {
377 //kDebug() << "hey now.. slow down!";
378 return;
381 d->updateTimestamp.restart();
382 updateAllSources();
385 void DataEngine::updateAllSources()
387 QHashIterator<QString, Plasma::DataContainer*> it(d->sources);
388 while (it.hasNext()) {
389 it.next();
390 //kDebug() << "updating" << it.key();
391 updateSourceEvent(it.key());
394 scheduleSourcesUpdated();
397 void DataEngine::setIcon(const QString &icon)
399 d->icon = icon;
402 QString DataEngine::icon() const
404 return d->icon;
407 QString DataEngine::pluginName() const
409 if (!d->dataEngineDescription.isValid()) {
410 return QString();
413 return d->dataEngineDescription.pluginName();
416 const Package *DataEngine::package() const
418 return d->package;
421 void DataEngine::scheduleSourcesUpdated()
423 QHashIterator<QString, Plasma::DataContainer*> it(d->sources);
424 while (it.hasNext()) {
425 it.next();
426 it.value()->checkForUpdate();
430 QString DataEngine::name() const
432 return d->engineName;
435 void DataEngine::setName(const QString &name)
437 d->engineName = name;
438 setObjectName(name);
441 // Private class implementations
442 DataEnginePrivate::DataEnginePrivate(DataEngine *e, KService::Ptr service)
443 : q(e),
444 dataEngineDescription(service),
445 refCount(-1), // first ref
446 updateTimerId(0),
447 minPollingInterval(-1),
448 limit(0),
449 valid(true),
450 script(0),
451 package(0)
453 updateTimer = new QTimer(q);
454 updateTimer->setSingleShot(true);
455 updateTimestamp.start();
457 if (!service) {
458 engineName = i18n("Unnamed");
459 return;
462 engineName = service->name();
463 if (engineName.isEmpty()) {
464 engineName = i18n("Unnamed");
466 e->setObjectName(engineName);
467 icon = service->icon();
469 if (dataEngineDescription.isValid()) {
470 QString api = dataEngineDescription.property("X-Plasma-API").toString();
472 if (!api.isEmpty()) {
473 const QString path =
474 KStandardDirs::locate("data",
475 "plasma/dataengines/" + dataEngineDescription.pluginName() + '/');
476 PackageStructure::Ptr structure =
477 Plasma::packageStructure(api, Plasma::DataEngineComponent);
478 structure->setPath(path);
479 package = new Package(path, structure);
481 script = Plasma::loadScriptEngine(api, q);
482 if (!script) {
483 kDebug() << "Could not create a" << api << "ScriptEngine for the"
484 << dataEngineDescription.name() << "DataEngine.";
485 delete package;
486 package = 0;
492 DataEnginePrivate::~DataEnginePrivate()
494 delete script;
495 script = 0;
496 delete package;
497 package = 0;
500 void DataEnginePrivate::internalUpdateSource(DataContainer *source)
502 if (minPollingInterval > 0 &&
503 source->timeSinceLastUpdate() < (uint)minPollingInterval) {
504 // skip updating this source; it's been too soon
505 //kDebug() << "internal update source is delaying" << source->timeSinceLastUpdate() << minPollingInterval;
506 //but fake an update so that the signalrelay that triggered this gets the data from the
507 //recent update. this way we don't have to worry about queuing - the relay will send a
508 //signal immediately and everyone else is undisturbed.
509 source->setNeedsUpdate();
510 return;
513 if (q->updateSourceEvent(source->objectName())) {
514 //kDebug() << "queuing an update";
515 queueUpdate();
516 }/* else {
517 kDebug() << "no update";
521 void DataEnginePrivate::ref()
523 --refCount;
526 void DataEnginePrivate::deref()
528 ++refCount;
531 bool DataEnginePrivate::isUsed() const
533 return refCount != 0;
536 DataContainer *DataEnginePrivate::source(const QString &sourceName, bool createWhenMissing)
538 DataEngine::SourceDict::const_iterator it = sources.constFind(sourceName);
539 if (it != sources.constEnd()) {
540 DataContainer *s = it.value();
541 if (limit > 0) {
542 QQueue<DataContainer*>::iterator it = sourceQueue.begin();
543 while (it != sourceQueue.end()) {
544 if (*it == s) {
545 sourceQueue.erase(it);
546 break;
548 ++it;
550 sourceQueue.enqueue(s);
552 return it.value();
555 if (!createWhenMissing) {
556 return 0;
559 /*kDebug() << "DataEngine " << q->objectName()
560 << ": could not find DataContainer " << sourceName
561 << ", creating" << endl;*/
562 DataContainer *s = new DataContainer(q);
563 s->setObjectName(sourceName);
564 sources.insert(sourceName, s);
565 QObject::connect(s, SIGNAL(updateRequested(DataContainer*)),
566 q, SLOT(internalUpdateSource(DataContainer*)));
568 if (limit > 0) {
569 trimQueue();
570 sourceQueue.enqueue(s);
572 return s;
575 void DataEnginePrivate::connectSource(DataContainer *s, QObject *visualization,
576 uint pollingInterval,
577 Plasma::IntervalAlignment align,
578 bool immediateCall)
580 //kDebug() << "connect source called" << s->objectName() << "with interval" << pollingInterval;
581 if (pollingInterval > 0) {
582 // never more frequently than allowed, never more than 20 times per second
583 uint min = qMax(50, minPollingInterval); // for qMax below
584 pollingInterval = qMax(min, pollingInterval);
586 // align on the 50ms
587 pollingInterval = pollingInterval - (pollingInterval % 50);
590 if (immediateCall) {
591 // we don't want to do an immediate call if we are simply
592 // reconnecting
593 //kDebug() << "immediate call requested, we have:" << s->visualizationIsConnected(visualization);
594 immediateCall = !s->visualizationIsConnected(visualization);
597 s->connectVisualization(visualization, pollingInterval, align);
599 if (immediateCall) {
600 QMetaObject::invokeMethod(visualization, "dataUpdated",
601 Q_ARG(QString, s->objectName()),
602 Q_ARG(Plasma::DataEngine::Data, s->data()));
606 DataContainer *DataEnginePrivate::requestSource(const QString &sourceName, bool *newSource)
608 if (newSource) {
609 *newSource = false;
612 //kDebug() << "requesting source " << sourceName;
613 DataContainer *s = source(sourceName, false);
615 if (!s) {
616 // we didn't find a data source, so give the engine an opportunity to make one
617 /*kDebug() << "DataEngine " << q->objectName()
618 << ": could not find DataContainer " << sourceName
619 << " will create on request" << endl;*/
620 if (q->sourceRequestEvent(sourceName)) {
621 s = source(sourceName, false);
622 if (s) {
623 // now we have a source; since it was created on demand, assume
624 // it should be removed when not used
625 if (newSource) {
626 *newSource = true;
628 QObject::connect(s, SIGNAL(becameUnused(QString)), q, SLOT(removeSource(QString)));
633 return s;
636 void DataEnginePrivate::trimQueue()
638 uint queueCount = sourceQueue.count();
639 while (queueCount >= limit && !sourceQueue.isEmpty()) {
640 DataContainer *punted = sourceQueue.dequeue();
641 q->removeSource(punted->objectName());
642 queueCount = sourceQueue.count();
646 void DataEnginePrivate::queueUpdate()
648 if (updateTimer->isActive()) {
649 return;
651 updateTimer->start(0);
656 #include "dataengine.moc"