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"
26 #include <QTimerEvent>
30 #include <kplugininfo.h>
32 #include <kstandarddirs.h>
34 #include "datacontainer.h"
37 #include "scripting/dataenginescript.h"
39 #include "private/service_p.h"
44 DataEngine::DataEngine(QObject
*parent
, KService::Ptr service
)
46 d(new DataEnginePrivate(this, service
))
48 connect(d
->updateTimer
, SIGNAL(timeout()), this, SLOT(scheduleSourcesUpdated()));
51 DataEngine::DataEngine(QObject
*parent
, const QVariantList
&args
)
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! ";
64 QStringList
DataEngine::sources() const
67 return d
->script
->sources();
69 return d
->sources
.keys();
73 Service
*DataEngine::serviceForSource(const QString
&source
)
76 return d
->script
->serviceForSource(source
);
78 return new NullService(source
, this);
82 void DataEngine::connectSource(const QString
&source
, QObject
*visualization
,
84 Plasma::IntervalAlignment intervalAlignment
) const
86 //kDebug() << "connectSource" << source;
88 DataContainer
*s
= d
->requestSource(source
, &newSource
);
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);
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
126 DataContainer
*s
= d
->requestSource(source
, &newSource
);
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
)) {
137 DataEngine::Data data
= s
->data();
142 void DataEngine::init()
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
)
156 return d
->script
->sourceRequestEvent(name
);
162 bool DataEngine::updateSourceEvent(const QString
&source
)
165 return d
->script
->updateSourceEvent(source
);
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);
183 s
= d
->source(source
);
186 s
->setData(key
, value
);
189 emit
sourceAdded(source
);
195 void DataEngine::setData(const QString
&source
, const Data
&data
)
197 DataContainer
*s
= d
->source(source
, false);
201 s
= d
->source(source
);
204 Data::const_iterator it
= data
.constBegin();
205 while (it
!= data
.constEnd()) {
206 s
->setData(it
.key(), it
.value());
211 emit
sourceAdded(source
);
217 void DataEngine::removeAllData(const QString
&source
)
219 DataContainer
*s
= d
->source(source
, false);
226 void DataEngine::removeData(const QString
&source
, const QString
&key
)
228 DataContainer
*s
= d
->source(source
, false);
230 s
->setData(key
, QVariant());
235 void DataEngine::addSource(DataContainer
*source
)
237 if (d
->sources
.contains(source
->objectName())) {
238 kDebug() << "source named \"" << source
->objectName() << "\" already exists.";
242 QObject::connect(source
, SIGNAL(updateRequested(DataContainer
*)),
243 this, SLOT(internalUpdateSource(DataContainer
*)));
244 d
->sources
.insert(source
->objectName(), source
);
245 emit
sourceAdded(source
->objectName());
249 void DataEngine::setMaxSourceCount(uint limit
)
251 if (d
->limit
== limit
) {
260 d
->sourceQueue
.clear();
264 uint
DataEngine::maxSourceCount() const
269 void DataEngine::setMinimumPollingInterval(int minimumMs
)
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;
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
313 QQueue
<DataContainer
*>::iterator it
= d
->sourceQueue
.begin();
314 while (it
!= d
->sourceQueue
.end()) {
316 d
->sourceQueue
.erase(it
);
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()) {
334 emit
sourceRemoved(it
.key());
340 bool DataEngine::isValid() const
345 bool DataEngine::isEmpty() const
347 return d
->sources
.isEmpty();
350 void DataEngine::setValid(bool valid
)
355 DataEngine::SourceDict
DataEngine::containerDict() const
360 void DataEngine::timerEvent(QTimerEvent
*event
)
362 if (event
->timerId() != d
->updateTimerId
) {
369 // if the freq update is less than 0, don't bother
370 if (d
->minPollingInterval
< 0) {
371 //kDebug() << "uh oh.. no polling allowed!";
375 // minPollingInterval
376 if (d
->updateTimestamp
.elapsed() < d
->minPollingInterval
) {
377 //kDebug() << "hey now.. slow down!";
381 d
->updateTimestamp
.restart();
385 void DataEngine::updateAllSources()
387 QHashIterator
<QString
, Plasma::DataContainer
*> it(d
->sources
);
388 while (it
.hasNext()) {
390 //kDebug() << "updating" << it.key();
391 updateSourceEvent(it
.key());
394 scheduleSourcesUpdated();
397 void DataEngine::setIcon(const QString
&icon
)
402 QString
DataEngine::icon() const
407 QString
DataEngine::pluginName() const
409 if (!d
->dataEngineDescription
.isValid()) {
413 return d
->dataEngineDescription
.pluginName();
416 const Package
*DataEngine::package() const
421 void DataEngine::scheduleSourcesUpdated()
423 QHashIterator
<QString
, Plasma::DataContainer
*> it(d
->sources
);
424 while (it
.hasNext()) {
426 it
.value()->checkForUpdate();
430 QString
DataEngine::name() const
432 return d
->engineName
;
435 void DataEngine::setName(const QString
&name
)
437 d
->engineName
= name
;
441 // Private class implementations
442 DataEnginePrivate::DataEnginePrivate(DataEngine
*e
, KService::Ptr service
)
444 dataEngineDescription(service
),
445 refCount(-1), // first ref
447 minPollingInterval(-1),
453 updateTimer
= new QTimer(q
);
454 updateTimer
->setSingleShot(true);
455 updateTimestamp
.start();
458 engineName
= i18n("Unnamed");
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()) {
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
);
483 kDebug() << "Could not create a" << api
<< "ScriptEngine for the"
484 << dataEngineDescription
.name() << "DataEngine.";
492 DataEnginePrivate::~DataEnginePrivate()
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();
513 if (q
->updateSourceEvent(source
->objectName())) {
514 //kDebug() << "queuing an update";
517 kDebug() << "no update";
521 void DataEnginePrivate::ref()
526 void DataEnginePrivate::deref()
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();
542 QQueue
<DataContainer
*>::iterator it
= sourceQueue
.begin();
543 while (it
!= sourceQueue
.end()) {
545 sourceQueue
.erase(it
);
550 sourceQueue
.enqueue(s
);
555 if (!createWhenMissing
) {
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
*)));
570 sourceQueue
.enqueue(s
);
575 void DataEnginePrivate::connectSource(DataContainer
*s
, QObject
*visualization
,
576 uint pollingInterval
,
577 Plasma::IntervalAlignment align
,
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
);
587 pollingInterval
= pollingInterval
- (pollingInterval
% 50);
591 // we don't want to do an immediate call if we are simply
593 //kDebug() << "immediate call requested, we have:" << s->visualizationIsConnected(visualization);
594 immediateCall
= !s
->visualizationIsConnected(visualization
);
597 s
->connectVisualization(visualization
, pollingInterval
, align
);
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
)
612 //kDebug() << "requesting source " << sourceName;
613 DataContainer
*s
= source(sourceName
, false);
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);
623 // now we have a source; since it was created on demand, assume
624 // it should be removed when not used
628 QObject::connect(s
, SIGNAL(becameUnused(QString
)), q
, SLOT(removeSource(QString
)));
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()) {
651 updateTimer
->start(0);
656 #include "dataengine.moc"