Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / workspace / plasma / applets / battery / battery.cpp
blob9f3ecef04e29c57f526638a427f27a3aad59e77d
1 /***************************************************************************
2 * Copyright (C) 2005,2006,2007 by Siraj Razick <siraj@kdemail.net> *
3 * Copyright (C) 2007 by Riccardo Iaconelli <riccardo@kde.org> *
4 * Copyright (C) 2007 by Sebastian Kuegler <sebas@kde.org> *
5 * Copyright (C) 2007 by Luka Renko <lure@kubuntu.org> *
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the *
19 * Free Software Foundation, Inc., *
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
21 ***************************************************************************/
23 #include "battery.h"
25 #include <QApplication>
26 #include <QPainter>
27 #include <QStyleOptionGraphicsItem>
28 #include <QFont>
29 #include <QGraphicsSceneHoverEvent>
31 #include <KDebug>
32 #include <KIcon>
33 #include <KLocalizedString>
34 #include <KSharedConfig>
35 #include <KDialog>
36 #include <KColorScheme>
37 #include <KGlobalSettings>
39 #include <plasma/svg.h>
40 #include <plasma/theme.h>
41 #include <plasma/phase.h>
43 Battery::Battery(QObject *parent, const QVariantList &args)
44 : Plasma::Applet(parent, args),
45 m_batteryStyle(0),
46 m_smallPixelSize(22),
47 m_theme(0),
48 m_dialog(0),
49 m_animId(-1),
50 m_alpha(1),
51 m_fadeIn(true),
52 m_acAnimId(-1),
53 m_acAlpha(1),
54 m_acFadeIn(false),
55 m_batteryAnimId(-1),
56 m_batteryAlpha(1),
57 m_batteryFadeIn(true),
58 m_isHovered(false),
59 m_numOfBattery(0)
61 kDebug() << "Loading applet battery";
62 setAcceptsHoverEvents(true);
63 setHasConfigurationInterface(true);
64 // TODO: minimum size causes size on panel to be huge (do not use for now)
65 //setMinimumContentSize(m_smallPixelSize, m_smallPixelSize);
66 setContentSize(64, 64);
69 void Battery::init()
71 KConfigGroup cg = config();
72 m_showBatteryString = cg.readEntry("showBatteryString", false);
73 m_showMultipleBatteries = cg.readEntry("showMultipleBatteries", true);
74 m_drawBackground = cg.readEntry("drawBackground", true);
76 // TODO: set background on panel causes 0 height, so do not use it
77 if (formFactor() != Plasma::Vertical && formFactor() != Plasma::Horizontal) {
78 setDrawStandardBackground(m_drawBackground);
81 QString svgFile = QString();
82 if (cg.readEntry("style", 0) == 0) {
83 m_batteryStyle = OxygenBattery;
84 svgFile = "widgets/battery-oxygen";
85 } else {
86 m_batteryStyle = ClassicBattery;
87 svgFile = "widgets/battery";
89 m_theme = new Plasma::Svg(svgFile, this);
90 m_theme->setContentType(Plasma::Svg::SingleImage);
91 m_theme->resize(contentSize());
93 m_font = QApplication::font();
94 m_font.setWeight(QFont::Bold);
96 m_boxAlpha = 128;
97 m_boxHoverAlpha = 192;
99 readColors();
100 connect(Plasma::Theme::self(), SIGNAL(changed()), SLOT(readColors()));
102 const QStringList& battery_sources = dataEngine("powermanagement")->query(I18N_NOOP("Battery"))[I18N_NOOP("sources")].toStringList();
103 m_numOfBattery = battery_sources.size();
105 //connect sources
106 connectSources();
108 foreach (QString battery_source, battery_sources) {
109 dataUpdated(battery_source, dataEngine("powermanagement")->query(battery_source));
111 dataUpdated(I18N_NOOP("AC Adapter"), dataEngine("powermanagement")->query(I18N_NOOP("AC Adapter")));
114 void Battery::constraintsUpdated(Plasma::Constraints constraints)
116 if (constraints & Plasma::FormFactorConstraint) {
117 if (formFactor() == Plasma::Vertical) {
118 kDebug() << "Vertical FormFactor";
119 // TODO: set background(true) on panel causes 0 height, so do not use it
120 setDrawStandardBackground(false);
121 } else if (formFactor() == Plasma::Horizontal) {
122 kDebug() << "Horizontal FormFactor";
123 // TODO: set background(true) on panel causes 0 height, so do not use it
124 setDrawStandardBackground(false);
125 } else if (formFactor() == Plasma::Planar) {
126 kDebug() << "Planar FormFactor";
127 setDrawStandardBackground(m_drawBackground);
128 } else if (formFactor() == Plasma::MediaCenter) {
129 kDebug() << "MediaCenter FormFactor";
130 setDrawStandardBackground(m_drawBackground);
131 } else {
132 kDebug() << "Other FormFactor" << formFactor();
133 setDrawStandardBackground(m_drawBackground);
137 if (constraints & Plasma::SizeConstraint && m_theme) {
138 m_theme->resize(contentSize().toSize());
142 QSizeF Battery::contentSizeHint() const
144 QSizeF sizeHint = contentSize();
145 //kDebug() << "SizeHintIn: " << sizeHint;
146 switch (formFactor()) {
147 case Plasma::Vertical:
148 if (m_numOfBattery > 1 && m_showMultipleBatteries) {
149 sizeHint.setHeight(sizeHint.width()*m_numOfBattery);
151 else {
152 sizeHint.setHeight(sizeHint.width());
154 break;
155 case Plasma::Horizontal:
156 case Plasma::Planar:
157 case Plasma::MediaCenter:
158 if (m_numOfBattery > 1 && m_showMultipleBatteries) {
159 sizeHint.setWidth(sizeHint.height()*m_numOfBattery);
160 } else {
161 sizeHint.setWidth(sizeHint.height());
163 break;
164 default:
165 break;
167 //kDebug() << "SizeHintOut: " << sizeHint;
168 return sizeHint;
171 Qt::Orientations Battery::expandingDirections() const
173 // no use of additional space in any direction
174 return 0;
177 void Battery::dataUpdated(const QString& source, const Plasma::DataEngine::Data &data)
179 if (source.startsWith(I18N_NOOP("Battery"))) {
180 m_batteries_data[source] = data;
181 } else if (source == I18N_NOOP("AC Adapter")) {
182 m_acadapter_plugged = data[I18N_NOOP("Plugged in")].toBool();
183 showAcAdapter(m_acadapter_plugged);
184 } else {
185 kDebug() << "Applet::Dunno what to do with " << source;
187 update();
190 void Battery::showConfigurationInterface()
192 if (m_dialog == 0) {
193 m_dialog = new KDialog;
194 m_dialog->setCaption(i18n("Configure Battery Monitor"));
196 QWidget *widget = new QWidget;
197 ui.setupUi(widget);
198 m_dialog->setMainWidget(widget);
199 m_dialog->setButtons( KDialog::Ok | KDialog::Cancel | KDialog::Apply );
201 connect( m_dialog, SIGNAL(applyClicked()), this, SLOT(configAccepted()) );
202 connect( m_dialog, SIGNAL(okClicked()), this, SLOT(configAccepted()) );
205 ui.styleGroup->setSelected(m_batteryStyle);
207 ui.showBatteryStringCheckBox->setChecked(m_showBatteryString ? Qt::Checked : Qt::Unchecked);
208 ui.showMultipleBatteriesCheckBox->setChecked(m_showMultipleBatteries ? Qt::Checked : Qt::Unchecked);
209 ui.drawBackgroundCheckBox->setChecked(m_drawBackground ? Qt::Checked : Qt::Unchecked);
210 m_dialog->show();
213 void Battery::configAccepted()
215 KConfigGroup cg = config();
216 m_showBatteryString = ui.showBatteryStringCheckBox->checkState() == Qt::Checked;
217 showLabel(m_showBatteryString);
218 cg.writeEntry("showBatteryString", m_showBatteryString);
220 bool old_showMultipleBatteries = m_showMultipleBatteries;
221 m_showMultipleBatteries = ui.showMultipleBatteriesCheckBox->checkState() == Qt::Checked;
222 cg.writeEntry("showMultipleBatteries", m_showMultipleBatteries);
224 m_drawBackground = ui.drawBackgroundCheckBox->checkState() == Qt::Checked;
225 cg.writeEntry("drawBackground", m_drawBackground);
227 // TODO: set background on panel causes 0 height, so do not use it
228 if (formFactor() != Plasma::Vertical && formFactor() != Plasma::Horizontal) {
229 setDrawStandardBackground(m_drawBackground);
232 if (ui.styleGroup->selected() != m_batteryStyle) {
233 QString svgFile = QString();
234 if (ui.styleGroup->selected() == OxygenBattery) {
235 svgFile = "widgets/battery-oxygen";
236 } else {
237 svgFile = "widgets/battery";
239 showAcAdapter(false);
240 showBattery(false);
241 m_batteryStyle = ui.styleGroup->selected();
242 delete m_theme;
243 m_theme = new Plasma::Svg(svgFile, this);
244 kDebug() << "Changing theme to " << svgFile;
245 cg.writeEntry("style", m_batteryStyle);
246 m_theme->resize(contentSize());
247 showAcAdapter(true);
248 showBattery(true);
251 if (m_numOfBattery > 1 && old_showMultipleBatteries != m_showMultipleBatteries) {
252 kDebug() << "Show multiple battery changed: " << m_showMultipleBatteries;
253 updateGeometry();
256 //reconnect sources
257 disconnectSources();
258 connectSources();
260 update();
261 emit configNeedsSaving();
264 void Battery::readColors()
266 m_textColor = Plasma::Theme::self()->textColor();
267 m_boxColor = Plasma::Theme::self()->backgroundColor();
268 m_boxColor.setAlpha(m_boxAlpha);
271 void Battery::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
273 showLabel(true);
274 //showAcAdapter(false); // to test the animation without constant plugging
275 //showBattery(false); // to test the animation without constant plugging
276 m_isHovered = true;
277 Applet::hoverEnterEvent(event);
280 void Battery::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
282 if (!m_showBatteryString) {
283 showLabel(false);
285 //showAcAdapter(true); // to test the animation without constant plugging
286 //showBattery(true); // to test the animation without constant plugging
287 //m_isHovered = false;
288 Applet::hoverLeaveEvent(event);
291 Battery::~Battery()
295 void Battery::showLabel(bool show)
297 if (m_fadeIn == show) {
298 return;
300 m_fadeIn = show;
301 const int FadeInDuration = 150;
303 if (m_animId != -1) {
304 Plasma::Phase::self()->stopCustomAnimation(m_animId);
306 m_animId = Plasma::Phase::self()->customAnimation(40 / (1000 / FadeInDuration), FadeInDuration,
307 Plasma::Phase::EaseOutCurve, this,
308 "animationUpdate");
311 QRectF Battery::scaleRectF(const qreal progress, QRectF rect) {
312 if (progress == 1) {
313 return rect;
315 // Scale
316 qreal w = rect.width()*progress;
317 qreal h = rect.width()*progress;
319 // Position centered
320 rect.setX((rect.width() - w)/2);
321 rect.setY((rect.height() - h)/2);
323 rect.setWidth(w);
324 rect.setHeight(h);
326 return rect;
329 void Battery::showAcAdapter(bool show)
331 if (m_acFadeIn == show) {
332 return;
334 m_acFadeIn = show;
335 const int FadeInDuration = 300;
336 // As long as the animation is running, we fake it's still plugged in so it gets
337 // painted in paintInterface()
338 m_acadapter_plugged = true;
340 if (m_acAnimId != -1) {
341 Plasma::Phase::self()->stopCustomAnimation(m_acAnimId);
344 //m_fadeIn = false;
345 m_acAnimId = Plasma::Phase::self()->customAnimation(40 / (1000 / FadeInDuration), FadeInDuration,
346 Plasma::Phase::EaseOutCurve, this,
347 "acAnimationUpdate");
350 void Battery::showBattery(bool show)
352 if (m_batteryFadeIn == show) {
353 return;
355 m_batteryFadeIn = show;
356 const int FadeInDuration = 300;
358 if (m_batteryAnimId != -1) {
359 Plasma::Phase::self()->stopCustomAnimation(m_batteryAnimId);
362 //m_fadeIn = false;
363 m_batteryAnimId = Plasma::Phase::self()->customAnimation(40 / (1000 / FadeInDuration), FadeInDuration,
364 Plasma::Phase::EaseOutCurve, this,
365 "batteryAnimationUpdate");
368 void Battery::animationUpdate(qreal progress)
370 if (progress == 1) {
371 m_animId = -1;
373 if (!m_fadeIn) {
374 qreal new_alpha = m_fadeIn ? progress : 1 - progress;
375 m_alpha = qMin(new_alpha, m_alpha);
376 } else {
377 m_alpha = m_fadeIn ? progress : 1 - progress;
379 update();
382 void Battery::acAnimationUpdate(qreal progress)
384 if (progress == 1) {
385 m_acAnimId = -1;
387 m_acAlpha = m_acFadeIn ? progress : 1 - progress;
388 // During the fadeout animation, we had set it to true (and lie)
389 // now the animation has ended, we _really_ set it to not show the adapter
390 if (!m_acFadeIn && (progress == 1)) {
391 m_acadapter_plugged = false;
393 update();
396 void Battery::batteryAnimationUpdate(qreal progress)
398 if (progress == 1) {
399 m_batteryAnimId = -1;
401 m_batteryAlpha = m_batteryFadeIn ? progress : 1 - progress;
402 update();
405 void Battery::paintLabel(QPainter *p, const QRect &contentsRect, const QString& labelText)
407 // Store font size, we want to restore it shortly
408 int original_font_size = m_font.pointSize();
410 // Fonts smaller than smallestReadableFont don't make sense.
411 m_font.setPointSize(qMax(KGlobalSettings::smallestReadableFont().pointSize(), m_font.pointSize()));
412 QFontMetrics fm(m_font);
413 qreal text_width = fm.width(labelText);
415 // Longer texts get smaller fonts
416 if (labelText.length() > 4) {
417 if (original_font_size/1.5 < KGlobalSettings::smallestReadableFont().pointSize()) {
418 m_font.setPointSize((KGlobalSettings::smallestReadableFont().pointSize()));
419 } else {
420 m_font.setPointSizeF(original_font_size/1.5);
422 fm = QFontMetrics(m_font);
423 text_width = (fm.width(labelText) * 1.2);
424 } else {
425 // Smaller texts get a wider box
426 text_width = (text_width * 1.4);
428 if (formFactor() == Plasma::Horizontal ||
429 formFactor() == Plasma::Vertical) {
430 m_font = KGlobalSettings::smallestReadableFont();
431 m_font.setWeight(QFont::Bold);
432 fm = QFontMetrics(m_font);
433 text_width = (fm.width(labelText)+8);
435 p->setFont(m_font);
437 // Let's find a good position for painting the background
438 QRectF text_rect = QRectF(qMax(0.0, contentsRect.left() + (contentsRect.width() - text_width) / 2),
439 contentsRect.top() + ((contentsRect.height() - (int)fm.height()) / 2 * 0.9),
440 qMin(contentSize().width(), text_width),
441 fm.height() * 1.2 );
443 // Poor man's highlighting
444 m_boxColor.setAlphaF(m_alpha);
445 p->setPen(m_boxColor);
446 m_boxColor.setAlphaF(m_alpha*0.5);
447 p->setBrush(m_boxColor);
449 // Find sensible proportions for the rounded corners
450 float round_prop = text_rect.width() / text_rect.height();
452 // Tweak the rounding edge a bit with the proportions of the textbox
453 qreal round_radius = 35.0;
454 p->drawRoundedRect(text_rect, round_radius / round_prop, round_radius, Qt::RelativeSize);
456 m_textColor.setAlphaF(m_alpha);
457 p->setPen(m_textColor);
458 p->drawText(text_rect, Qt::AlignCenter, labelText);
460 // Reset font and box
461 m_font.setPointSize(original_font_size);
462 m_boxColor.setAlpha(m_boxAlpha);
465 void Battery::paintBattery(QPainter *p, const QRect &contentsRect, const int batteryPercent, const bool plugState)
467 QString fill_element = QString();
468 if (plugState && m_theme->elementExists("Battery")) {
469 m_theme->paint(p, scaleRectF(m_batteryAlpha, contentsRect), "Battery");
471 if (m_batteryStyle == OxygenBattery) {
472 if (batteryPercent > 95) {
473 fill_element = "Fill100";
474 } else if (batteryPercent > 80) {
475 fill_element = "Fill80";
476 } else if (batteryPercent > 50) {
477 fill_element = "Fill60";
478 } else if (batteryPercent > 20) {
479 fill_element = "Fill40";
480 } else if (batteryPercent > 10) {
481 fill_element = "Fill20";
482 } // Don't show a fillbar below 11% charged
483 } else { // OxyenStyle
484 if (batteryPercent > 95) {
485 fill_element = "Fill100";
486 } else if (batteryPercent > 90) {
487 fill_element = "Fill90";
488 } else if (batteryPercent > 80) {
489 fill_element = "Fill80";
490 } else if (batteryPercent > 70) {
491 fill_element = "Fill70";
492 } else if (batteryPercent > 55) {
493 fill_element = "Fill60";
494 } else if (batteryPercent > 40) {
495 fill_element = "Fill50";
496 } else if (batteryPercent > 30) {
497 fill_element = "Fill40";
498 } else if (batteryPercent > 20) {
499 fill_element = "Fill30";
500 } else if (batteryPercent > 10) {
501 fill_element = "Fill20";
502 } else if (batteryPercent >= 5) {
503 fill_element = "Fill10";
504 } // Lower than 5%? Show no fillbar.
507 //kDebug() << "plugState:" << plugState;
509 // Now let's find out which fillstate to show
510 if (plugState && !fill_element.isEmpty()) {
511 if (m_theme->elementExists(fill_element)) {
512 m_theme->paint(p, scaleRectF(m_batteryAlpha, contentsRect), fill_element);
513 } else {
514 kDebug() << fill_element << " does not exist in svg";
518 if (m_acadapter_plugged) {
519 //QRectF ac_rect = QRectF(contentsRect.topLeft(), QSizeF(contentsRect.width()*m_acAlpha, contentsRect.height()*m_acAlpha));
520 m_theme->paint(p, scaleRectF(m_acAlpha, contentsRect), "AcAdapter");
523 // For small FormFactors, we're drawing a shadow
524 if (formFactor() == Plasma::Vertical ||
525 formFactor() == Plasma::Horizontal) {
526 if (plugState) {
527 m_theme->paint(p, contentsRect, "Shadow");
530 if (plugState && m_theme->elementExists("Overlay")) {
531 m_theme->paint(p, scaleRectF(m_batteryAlpha, contentsRect), "Overlay");
535 void Battery::paintInterface(QPainter *p, const QStyleOptionGraphicsItem *option, const QRect &contentsRect)
537 Q_UNUSED( option );
539 p->setRenderHint(QPainter::SmoothPixmapTransform);
540 p->setRenderHint(QPainter::Antialiasing);
542 if (m_numOfBattery == 0) {
543 QRectF ac_contentsRect(contentsRect.topLeft(), QSizeF(contentsRect.width() * m_acAlpha, contentsRect.height() * m_acAlpha));
544 m_theme->paint(p, ac_contentsRect, "AcAdapter");
545 if (formFactor() == Plasma::Planar ||
546 formFactor() == Plasma::MediaCenter) {
547 // Show that there's no battery
548 paintLabel(p, contentsRect, I18N_NOOP("n/a"));
550 return;
553 if (m_showMultipleBatteries) {
554 // paint each battery with own charge level
555 int battery_num = 0;
556 int width = contentsRect.width()/m_numOfBattery;
557 QHashIterator<QString, QHash<QString, QVariant > > battery_data(m_batteries_data);
558 while (battery_data.hasNext()) {
559 battery_data.next();
560 QRect corect = QRect(contentsRect.left()+battery_num*width,
561 contentsRect.top(),
562 width, contentSize().toSize().height());
564 // paint battery with appropriate charge level
565 paintBattery(p, corect, battery_data.value()[I18N_NOOP("Percent")].toInt(), battery_data.value()[I18N_NOOP("Plugged in")].toBool());
567 if (m_showBatteryString || m_isHovered) {
568 // Show the charge percentage with a box on top of the battery
569 QString batteryLabel;
570 if (battery_data.value()[I18N_NOOP("Plugged in")].toBool()) {
571 batteryLabel = battery_data.value()[I18N_NOOP("Percent")].toString();
572 batteryLabel.append("%");
573 } else {
574 batteryLabel = I18N_NOOP("n/a");
576 paintLabel(p, corect, batteryLabel);
578 ++battery_num;
580 } else {
581 // paint only one battery and show cumulative charge level
582 int battery_num = 0;
583 int battery_charge = 0;
584 bool has_battery = false;
585 QHashIterator<QString, QHash<QString, QVariant > > battery_data(m_batteries_data);
586 while (battery_data.hasNext()) {
587 battery_data.next();
588 battery_charge += battery_data.value()[I18N_NOOP("Percent")].toInt();
589 if (battery_data.value()[I18N_NOOP("Plugged in")].toBool()) {
590 has_battery = true;
591 ++battery_num;
594 if (battery_num > 0) {
595 battery_charge = battery_charge / battery_num;
597 // paint battery with appropriate charge level
598 paintBattery(p, contentsRect, battery_charge, has_battery);
599 if (m_showBatteryString || m_isHovered) {
600 // Show the charge percentage with a box on top of the battery
601 QString batteryLabel;
602 if (battery_data.value()[I18N_NOOP("Plugged in")].toBool()) {
603 batteryLabel = battery_data.value()[I18N_NOOP("Percent")].toString();
604 batteryLabel.append("%");
605 } else {
606 batteryLabel = I18N_NOOP("n/a");
608 paintLabel(p, contentsRect, batteryLabel);
613 void Battery::connectSources() {
614 const QStringList& battery_sources = dataEngine("powermanagement")->query(I18N_NOOP("Battery"))[I18N_NOOP("sources")].toStringList();
616 foreach (QString battery_source, battery_sources) {
617 dataEngine("powermanagement")->connectSource(battery_source, this);
620 dataEngine("powermanagement")->connectSource(I18N_NOOP("AC Adapter"), this);
623 void Battery::disconnectSources()
625 const QStringList& battery_sources = dataEngine("powermanagement")->query(I18N_NOOP("Battery"))[I18N_NOOP("sources")].toStringList();
627 foreach (QString battery_source ,battery_sources) {
628 dataEngine("powermanagement")->disconnectSource(battery_source, this);
631 dataEngine("powermanagement")->disconnectSource(I18N_NOOP("AC Adapter"), this);
634 #include "battery.moc"