16bf535e5e
CCBUG:199899 svn path=/trunk/KDE/kdelibs/; revision=1002146
552 lines
15 KiB
C++
552 lines
15 KiB
C++
/*
|
|
* Copyright (C) 2007 Petri Damsten <damu@iki.fi>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Library General Public License as
|
|
* published by the Free Software Foundation; either version 2, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this program; if not, write to the
|
|
* Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "meter.h"
|
|
|
|
#include <cmath>
|
|
|
|
#include <QPainter>
|
|
#include <QTimeLine>
|
|
|
|
#include <kdebug.h>
|
|
#include <kglobalsettings.h>
|
|
|
|
#include "plasma/animator.h"
|
|
#include "plasma/framesvg.h"
|
|
#include "plasma/theme.h"
|
|
|
|
namespace Plasma {
|
|
|
|
class MeterPrivate
|
|
{
|
|
public:
|
|
MeterPrivate(Meter *m)
|
|
: minimum(0),
|
|
maximum(100),
|
|
value(0),
|
|
targetValue(0),
|
|
meterType(Meter::AnalogMeter),
|
|
image(0),
|
|
minrotate(0),
|
|
maxrotate(360),
|
|
meter(m),
|
|
movementId(0)
|
|
{
|
|
}
|
|
|
|
void progressChanged(qreal progress)
|
|
{
|
|
bool over = qFuzzyCompare(progress, qreal(1.0));
|
|
|
|
if (value == targetValue) {
|
|
if (!over && movementId) {
|
|
Animator::self()->stopCustomAnimation(movementId);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (over) {
|
|
value = targetValue;
|
|
//kDebug() << "done";
|
|
movementId = 0;
|
|
} else {
|
|
int frame = progress * 10;
|
|
int delta = targetValue - value;
|
|
value += (delta / qreal(10 - frame));
|
|
//kDebug() << frame << value << targetValue;
|
|
}
|
|
|
|
meter->update();
|
|
}
|
|
|
|
void paint(QPainter *p, const QString &elementID)
|
|
{
|
|
if (image->hasElement(elementID)) {
|
|
QRectF elementRect = image->elementRect(elementID);
|
|
image->paint(p, elementRect, elementID);
|
|
}
|
|
}
|
|
|
|
void text(QPainter *p, int index)
|
|
{
|
|
QString elementID = QString("label%1").arg(index);
|
|
QString text = labels[index];
|
|
|
|
if (image->hasElement(elementID)) {
|
|
QRectF elementRect = image->elementRect(elementID);
|
|
Qt::Alignment align = Qt::AlignCenter;
|
|
|
|
if (colors.count() > index) {
|
|
p->setPen(QPen(colors[index]));
|
|
}
|
|
if (fonts.count() > index) {
|
|
p->setFont(fonts[index]);
|
|
}
|
|
if (alignments.count() > index) {
|
|
align = alignments[index];
|
|
}
|
|
if (elementRect.width() > elementRect.height()) {
|
|
p->drawText(elementRect, align, text);
|
|
} else {
|
|
p->save();
|
|
QPointF rotateCenter(
|
|
elementRect.left() + elementRect.width() / 2,
|
|
elementRect.top() + elementRect.height() / 2);
|
|
p->translate(rotateCenter);
|
|
p->rotate(-90);
|
|
p->translate(elementRect.height() / -2,
|
|
elementRect.width() / -2);
|
|
QRectF r(0, 0, elementRect.height(), elementRect.width());
|
|
p->drawText(r, align, text);
|
|
p->restore();
|
|
}
|
|
}
|
|
}
|
|
|
|
QRectF barRect()
|
|
{
|
|
QRectF elementRect;
|
|
|
|
if (labels.count() > 0) {
|
|
elementRect = image->elementRect("background");
|
|
} else {
|
|
elementRect = QRectF(QPoint(0,0), meter->size());
|
|
}
|
|
|
|
if (image->hasElement("hint-bar-stretch") || !image->hasElement("bar-active-center")) {
|
|
return elementRect;
|
|
}
|
|
|
|
QSize imageSize = image->size();
|
|
image->resize();
|
|
QSize tileSize = image->elementSize("bar-active-center");
|
|
image->resize(imageSize);
|
|
|
|
if (elementRect.width() > elementRect.height()) {
|
|
qreal ratio = qMax(1, tileSize.height() / tileSize.width());
|
|
int numTiles = qMax(qreal(1.0), qreal(elementRect.width())/(qreal(elementRect.height())/ratio));
|
|
tileSize = QSize(elementRect.width()/numTiles, elementRect.height());
|
|
|
|
QPoint center = elementRect.center().toPoint();
|
|
elementRect.setWidth(tileSize.width()*numTiles);
|
|
elementRect.moveCenter(center);
|
|
} else {
|
|
qreal ratio = qMax(1, tileSize.width() / tileSize.height());
|
|
int numTiles = qMax(qreal(1.0), qreal(elementRect.height())/(qreal(elementRect.width())/ratio));
|
|
tileSize = QSize(elementRect.width(), elementRect.height()/numTiles);
|
|
|
|
QPoint center = elementRect.center().toPoint();
|
|
elementRect.setHeight(tileSize.height()*numTiles);
|
|
elementRect.moveCenter(center);
|
|
}
|
|
|
|
return elementRect;
|
|
}
|
|
|
|
void paintBackground(QPainter *p)
|
|
{
|
|
//be retrocompatible with themes for kde <= 4.1
|
|
if (image->hasElement("background-center")) {
|
|
QRectF elementRect = barRect();
|
|
if (elementRect.isEmpty()) {
|
|
return; // nothing to be done
|
|
}
|
|
|
|
QSize imageSize = image->size();
|
|
image->resize();
|
|
|
|
image->setElementPrefix("background");
|
|
image->resizeFrame(elementRect.size());
|
|
image->paintFrame(p, elementRect.topLeft());
|
|
image->resize(imageSize);
|
|
|
|
paintBar(p, "bar-inactive");
|
|
} else {
|
|
paint(p, "background");
|
|
}
|
|
}
|
|
|
|
void paintBar(QPainter *p, const QString &prefix)
|
|
{
|
|
QRectF elementRect = barRect();
|
|
|
|
image->setUsingRenderingCache(false);
|
|
if (image->hasElement("hint-bar-stretch")) {
|
|
image->resizeFrame(elementRect.size());
|
|
image->paintFrame(p);
|
|
} else {
|
|
QSize imageSize = image->size();
|
|
image->resize();
|
|
QSize tileSize = image->elementSize("bar-active-center");
|
|
|
|
if (elementRect.width() > elementRect.height()) {
|
|
qreal ratio = tileSize.height() / tileSize.width();
|
|
int numTiles = elementRect.width()/(elementRect.height()/ratio);
|
|
tileSize = QSize(elementRect.width()/numTiles, elementRect.height());
|
|
} else {
|
|
qreal ratio = tileSize.width() / tileSize.height();
|
|
int numTiles = elementRect.height()/(elementRect.width()/ratio);
|
|
tileSize = QSize(elementRect.width(), elementRect.height()/numTiles);
|
|
}
|
|
|
|
image->setElementPrefix(prefix);
|
|
image->resizeFrame(tileSize);
|
|
p->drawTiledPixmap(elementRect, image->framePixmap());
|
|
image->resize(imageSize);
|
|
}
|
|
image->setUsingRenderingCache(true);
|
|
}
|
|
|
|
void paintForeground(QPainter *p)
|
|
{
|
|
for (int i = 0; i < labels.count(); ++i) {
|
|
text(p, i);
|
|
}
|
|
|
|
paint(p, "foreground");
|
|
}
|
|
|
|
void setSizePolicyAndPreferredSize()
|
|
{
|
|
switch (meterType) {
|
|
case Meter::BarMeterHorizontal:
|
|
meter->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
|
|
break;
|
|
case Meter::BarMeterVertical:
|
|
meter->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
|
|
break;
|
|
case Meter::AnalogMeter:
|
|
default:
|
|
meter->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
|
break;
|
|
}
|
|
|
|
if (image) {
|
|
//set a sane preferredSize. We can't just use the svg's native size, since that way
|
|
//letters get cut off if the user uses a font larger then usual. Check how many rows of
|
|
//labels we have, add 1 (the progress bar), and multiply by the font height to get a
|
|
//somewhat sane size height. This is not perfect but work well enough for 4.2. I suggest
|
|
//we look into alternatives for 4.3.
|
|
uint i = 0;
|
|
uint rows = 0;
|
|
qreal prevY = -1;
|
|
QString labelName = "label0";
|
|
while (image->hasElement(labelName)) {
|
|
if (image->elementRect(labelName).y() > prevY) {
|
|
prevY = image->elementRect(labelName).y();
|
|
rows++;
|
|
}
|
|
i++;
|
|
labelName = QString("label%0").arg(i);
|
|
}
|
|
|
|
Plasma::Theme *theme = Plasma::Theme::defaultTheme();
|
|
QFont font = theme->font(Plasma::Theme::DefaultFont);
|
|
QFontMetrics fm(font);
|
|
|
|
meter->setPreferredHeight((rows + 1) * fm.height());
|
|
} else {
|
|
meter->setPreferredSize(QSizeF(30, 30));
|
|
}
|
|
}
|
|
|
|
int minimum;
|
|
int maximum;
|
|
int value;
|
|
int targetValue;
|
|
QStringList labels;
|
|
QList<Qt::Alignment> alignments;
|
|
QList<QColor> colors;
|
|
QList<QFont> fonts;
|
|
QString svg;
|
|
Meter::MeterType meterType;
|
|
Plasma::FrameSvg *image;
|
|
int minrotate;
|
|
int maxrotate;
|
|
Meter *meter;
|
|
int movementId;
|
|
};
|
|
|
|
Meter::Meter(QGraphicsItem *parent) :
|
|
QGraphicsWidget(parent),
|
|
d(new MeterPrivate(this))
|
|
{
|
|
d->setSizePolicyAndPreferredSize();
|
|
}
|
|
|
|
Meter::~Meter()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
void Meter::setMaximum(int maximum)
|
|
{
|
|
d->maximum = maximum;
|
|
}
|
|
|
|
int Meter::maximum() const
|
|
{
|
|
return d->maximum;
|
|
}
|
|
|
|
void Meter::setMinimum(int minimum)
|
|
{
|
|
d->minimum = minimum;
|
|
}
|
|
|
|
int Meter::minimum() const
|
|
{
|
|
return d->minimum;
|
|
}
|
|
|
|
void Meter::setValue(int value)
|
|
{
|
|
if (value == d->targetValue) {
|
|
return;
|
|
}
|
|
|
|
d->targetValue = qBound(d->minimum, value, d->maximum);
|
|
int delta = abs(d->value - d->targetValue);
|
|
|
|
if (d->movementId) {
|
|
Animator::self()->stopCustomAnimation(d->movementId);
|
|
d->movementId = 0;
|
|
}
|
|
|
|
//kDebug() << d->targetValue << d->value << delta;
|
|
if (!(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) ||
|
|
delta / qreal(d->maximum) < 0.1) {
|
|
d->value = value;
|
|
update();
|
|
} else {
|
|
d->movementId = Animator::self()->customAnimation(10, 100, Animator::EaseOutCurve,
|
|
this, "progressChanged");
|
|
}
|
|
}
|
|
|
|
int Meter::value() const
|
|
{
|
|
return d->value;
|
|
}
|
|
|
|
void Meter::setLabel(int index, const QString &text)
|
|
{
|
|
while (d->labels.count() <= index) {
|
|
d->labels << QString();
|
|
}
|
|
d->labels[index] = text;
|
|
}
|
|
|
|
QString Meter::label(int index) const
|
|
{
|
|
return d->labels[index];
|
|
}
|
|
|
|
void Meter::setLabelColor(int index, const QColor &color)
|
|
{
|
|
while (d->colors.count() <= index) {
|
|
d->colors << color;
|
|
}
|
|
d->colors[index] = color;
|
|
}
|
|
|
|
QColor Meter::labelColor(int index) const
|
|
{
|
|
return d->colors[index];
|
|
}
|
|
|
|
void Meter::setLabelFont(int index, const QFont &font)
|
|
{
|
|
while (d->fonts.count() <= index) {
|
|
d->fonts << font;
|
|
}
|
|
d->fonts[index] = font;
|
|
}
|
|
|
|
QFont Meter::labelFont(int index) const
|
|
{
|
|
return d->fonts[index];
|
|
}
|
|
|
|
void Meter::setLabelAlignment(int index, const Qt::Alignment alignment)
|
|
{
|
|
while (d->alignments.count() <= index) {
|
|
d->alignments << alignment;
|
|
}
|
|
d->alignments[index] = alignment;
|
|
}
|
|
|
|
Qt::Alignment Meter::labelAlignment(int index) const
|
|
{
|
|
return d->alignments[index];
|
|
}
|
|
|
|
QRectF Meter::labelRect(int index) const
|
|
{
|
|
QString elementID = QString("label%1").arg(index);
|
|
return d->image->elementRect(elementID);
|
|
}
|
|
|
|
void Meter::dataUpdated(const QString &sourceName, const Plasma::DataEngine::Data &data)
|
|
{
|
|
Q_UNUSED(sourceName)
|
|
|
|
foreach (const QVariant &v, data) {
|
|
if (v.type() == QVariant::Int ||
|
|
v.type() == QVariant::UInt ||
|
|
v.type() == QVariant::LongLong ||
|
|
v.type() == QVariant::ULongLong) {
|
|
setValue(v.toInt());
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Meter::setSvg(const QString &svg)
|
|
{
|
|
d->svg = svg;
|
|
delete d->image;
|
|
d->image = new Plasma::FrameSvg(this);
|
|
d->image->setImagePath(svg);
|
|
// To create renderer and get default size
|
|
d->image->resize();
|
|
d->setSizePolicyAndPreferredSize();
|
|
if (d->image->hasElement("rotateminmax")) {
|
|
QRectF r = d->image->elementRect("rotateminmax");
|
|
d->minrotate = (int)r.height();
|
|
d->maxrotate = (int)r.width();
|
|
}
|
|
}
|
|
|
|
QString Meter::svg() const
|
|
{
|
|
return d->svg;
|
|
}
|
|
|
|
void Meter::setMeterType(MeterType meterType)
|
|
{
|
|
d->meterType = meterType;
|
|
if (d->svg.isEmpty()) {
|
|
if (meterType == BarMeterHorizontal) {
|
|
setSvg("widgets/bar_meter_horizontal");
|
|
} else if (meterType == BarMeterVertical) {
|
|
setSvg("widgets/bar_meter_vertical");
|
|
} else if (meterType == AnalogMeter) {
|
|
setSvg("widgets/analog_meter");
|
|
}
|
|
}
|
|
d->setSizePolicyAndPreferredSize();
|
|
}
|
|
|
|
Meter::MeterType Meter::meterType() const
|
|
{
|
|
return d->meterType;
|
|
}
|
|
|
|
void Meter::paint(QPainter *p,
|
|
const QStyleOptionGraphicsItem *option,
|
|
QWidget *widget)
|
|
{
|
|
Q_UNUSED(option)
|
|
Q_UNUSED(widget)
|
|
|
|
if (!d->image) {
|
|
return;
|
|
}
|
|
|
|
QRectF rect(QPointF(0, 0), size());
|
|
QRectF clipRect;
|
|
qreal percentage = 0.0;
|
|
qreal angle = 0.0;
|
|
QPointF rotateCenter;
|
|
QSize intSize = QSize((int)size().width(), (int)size().height());
|
|
|
|
if (intSize != d->image->size()) {
|
|
d->image->resize(intSize);
|
|
}
|
|
|
|
if (d->maximum != d->minimum) {
|
|
percentage = (qreal)d->value / (d->maximum - d->minimum);
|
|
}
|
|
|
|
p->setRenderHint(QPainter::SmoothPixmapTransform);
|
|
switch (d->meterType) {
|
|
case BarMeterHorizontal:
|
|
case BarMeterVertical:
|
|
d->paintBackground(p);
|
|
|
|
p->save();
|
|
clipRect = d->barRect();
|
|
if (clipRect.width() > clipRect.height()) {
|
|
clipRect.setWidth(clipRect.width() * percentage);
|
|
} else {
|
|
qreal bottom = clipRect.bottom();
|
|
clipRect.setHeight(clipRect.height() * percentage);
|
|
clipRect.moveBottom(bottom);
|
|
}
|
|
p->setClipRect(clipRect);
|
|
|
|
//be retrocompatible
|
|
if (d->image->hasElement("bar-active-center")) {
|
|
d->paintBar(p, "bar-active");
|
|
} else {
|
|
d->paint(p, "bar");
|
|
}
|
|
p->restore();
|
|
|
|
d->paintForeground(p);
|
|
break;
|
|
case AnalogMeter:
|
|
d->paintBackground(p);
|
|
|
|
p->save();
|
|
if (d->image->hasElement("rotatecenter")) {
|
|
QRectF r = d->image->elementRect("rotatecenter");
|
|
rotateCenter = QPointF(r.left() + r.width() / 2,
|
|
r.top() + r.height() / 2);
|
|
} else {
|
|
rotateCenter = QPointF(rect.width() / 2, rect.height() / 2);
|
|
}
|
|
angle = percentage * (d->maxrotate - d->minrotate) + d->minrotate;
|
|
|
|
if (d->image->hasElement("pointer-shadow")) {
|
|
p->save();
|
|
p->translate(rotateCenter+QPoint(2,3));
|
|
p->rotate(angle);
|
|
p->translate(-1 * rotateCenter);
|
|
d->paint(p, "pointer-shadow");
|
|
p->restore();
|
|
}
|
|
|
|
p->translate(rotateCenter);
|
|
p->rotate(angle);
|
|
p->translate(-1 * rotateCenter);
|
|
d->paint(p, "pointer");
|
|
p->restore();
|
|
|
|
d->paintForeground(p);
|
|
break;
|
|
}
|
|
}
|
|
|
|
} // End of namepace
|
|
|
|
#include "meter.moc"
|