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> *
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. *
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. *
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 ***************************************************************************/
25 #include <QApplication>
27 #include <QStyleOptionGraphicsItem>
29 #include <QGraphicsSceneHoverEvent>
33 #include <KLocalizedString>
34 #include <KSharedConfig>
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
),
57 m_batteryFadeIn(true),
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);
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";
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
);
97 m_boxHoverAlpha
= 192;
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();
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
);
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
);
152 sizeHint
.setHeight(sizeHint
.width());
155 case Plasma::Horizontal
:
157 case Plasma::MediaCenter
:
158 if (m_numOfBattery
> 1 && m_showMultipleBatteries
) {
159 sizeHint
.setWidth(sizeHint
.height()*m_numOfBattery
);
161 sizeHint
.setWidth(sizeHint
.height());
167 //kDebug() << "SizeHintOut: " << sizeHint;
171 Qt::Orientations
Battery::expandingDirections() const
173 // no use of additional space in any direction
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
);
185 kDebug() << "Applet::Dunno what to do with " << source
;
190 void Battery::showConfigurationInterface()
193 m_dialog
= new KDialog
;
194 m_dialog
->setCaption(i18n("Configure Battery Monitor"));
196 QWidget
*widget
= new QWidget
;
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
);
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";
237 svgFile
= "widgets/battery";
239 showAcAdapter(false);
241 m_batteryStyle
= ui
.styleGroup
->selected();
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());
251 if (m_numOfBattery
> 1 && old_showMultipleBatteries
!= m_showMultipleBatteries
) {
252 kDebug() << "Show multiple battery changed: " << m_showMultipleBatteries
;
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
)
274 //showAcAdapter(false); // to test the animation without constant plugging
275 //showBattery(false); // to test the animation without constant plugging
277 Applet::hoverEnterEvent(event
);
280 void Battery::hoverLeaveEvent(QGraphicsSceneHoverEvent
*event
)
282 if (!m_showBatteryString
) {
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
);
295 void Battery::showLabel(bool show
)
297 if (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,
311 QRectF
Battery::scaleRectF(const qreal progress
, QRectF rect
) {
316 qreal w
= rect
.width()*progress
;
317 qreal h
= rect
.width()*progress
;
320 rect
.setX((rect
.width() - w
)/2);
321 rect
.setY((rect
.height() - h
)/2);
329 void Battery::showAcAdapter(bool show
)
331 if (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
);
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
) {
355 m_batteryFadeIn
= show
;
356 const int FadeInDuration
= 300;
358 if (m_batteryAnimId
!= -1) {
359 Plasma::Phase::self()->stopCustomAnimation(m_batteryAnimId
);
363 m_batteryAnimId
= Plasma::Phase::self()->customAnimation(40 / (1000 / FadeInDuration
), FadeInDuration
,
364 Plasma::Phase::EaseOutCurve
, this,
365 "batteryAnimationUpdate");
368 void Battery::animationUpdate(qreal progress
)
374 qreal new_alpha
= m_fadeIn
? progress
: 1 - progress
;
375 m_alpha
= qMin(new_alpha
, m_alpha
);
377 m_alpha
= m_fadeIn
? progress
: 1 - progress
;
382 void Battery::acAnimationUpdate(qreal progress
)
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;
396 void Battery::batteryAnimationUpdate(qreal progress
)
399 m_batteryAnimId
= -1;
401 m_batteryAlpha
= m_batteryFadeIn
? progress
: 1 - progress
;
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()));
420 m_font
.setPointSizeF(original_font_size
/1.5);
422 fm
= QFontMetrics(m_font
);
423 text_width
= (fm
.width(labelText
) * 1.2);
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);
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
),
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
);
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
) {
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
)
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"));
553 if (m_showMultipleBatteries
) {
554 // paint each battery with own charge level
556 int width
= contentsRect
.width()/m_numOfBattery
;
557 QHashIterator
<QString
, QHash
<QString
, QVariant
> > battery_data(m_batteries_data
);
558 while (battery_data
.hasNext()) {
560 QRect corect
= QRect(contentsRect
.left()+battery_num
*width
,
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("%");
574 batteryLabel
= I18N_NOOP("n/a");
576 paintLabel(p
, corect
, batteryLabel
);
581 // paint only one battery and show cumulative charge level
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()) {
588 battery_charge
+= battery_data
.value()[I18N_NOOP("Percent")].toInt();
589 if (battery_data
.value()[I18N_NOOP("Plugged in")].toBool()) {
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("%");
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"