00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "meter.h"
00021
00022 #include <cmath>
00023
00024 #include <QPainter>
00025 #include <QTimeLine>
00026
00027 #include <kdebug.h>
00028 #include <kglobalsettings.h>
00029
00030 #include "plasma/animator.h"
00031 #include "plasma/framesvg.h"
00032 #include "plasma/theme.h"
00033
00034 namespace Plasma {
00035
00036 class MeterPrivate
00037 {
00038 public:
00039 MeterPrivate(Meter *m)
00040 : minimum(0),
00041 maximum(100),
00042 value(0),
00043 targetValue(0),
00044 meterType(Meter::AnalogMeter),
00045 image(0),
00046 minrotate(0),
00047 maxrotate(360),
00048 meter(m),
00049 movementId(0)
00050 {
00051 }
00052
00053 void progressChanged(qreal progress)
00054 {
00055 bool over = qFuzzyCompare(progress, qreal(1.0));
00056
00057 if (value == targetValue) {
00058 if (!over && movementId) {
00059 Animator::self()->stopCustomAnimation(movementId);
00060 }
00061
00062 return;
00063 }
00064
00065 if (over) {
00066 value = targetValue;
00067
00068 movementId = 0;
00069 } else {
00070 int frame = progress * 10;
00071 int delta = targetValue - value;
00072 value += (delta / qreal(10 - frame));
00073
00074 }
00075
00076 meter->update();
00077 }
00078
00079 void paint(QPainter *p, const QString &elementID)
00080 {
00081 if (image->hasElement(elementID)) {
00082 QRectF elementRect = image->elementRect(elementID);
00083 image->paint(p, elementRect, elementID);
00084 }
00085 }
00086
00087 void text(QPainter *p, int index)
00088 {
00089 QString elementID = QString("label%1").arg(index);
00090 QString text = labels[index];
00091
00092 if (image->hasElement(elementID)) {
00093 QRectF elementRect = image->elementRect(elementID);
00094 Qt::Alignment align = Qt::AlignCenter;
00095
00096 if (colors.count() > index) {
00097 p->setPen(QPen(colors[index]));
00098 }
00099 if (fonts.count() > index) {
00100 p->setFont(fonts[index]);
00101 }
00102 if (alignments.count() > index) {
00103 align = alignments[index];
00104 }
00105 if (elementRect.width() > elementRect.height()) {
00106 p->drawText(elementRect, align, text);
00107 } else {
00108 p->save();
00109 QPointF rotateCenter(
00110 elementRect.left() + elementRect.width() / 2,
00111 elementRect.top() + elementRect.height() / 2);
00112 p->translate(rotateCenter);
00113 p->rotate(-90);
00114 p->translate(elementRect.height() / -2,
00115 elementRect.width() / -2);
00116 QRectF r(0, 0, elementRect.height(), elementRect.width());
00117 p->drawText(r, align, text);
00118 p->restore();
00119 }
00120 }
00121 }
00122
00123 QRectF barRect()
00124 {
00125 QRectF elementRect;
00126
00127 if (labels.count() > 0) {
00128 elementRect = image->elementRect("background");
00129 } else {
00130 elementRect = QRectF(QPoint(0,0), meter->size());
00131 }
00132
00133 if (image->hasElement("hint-bar-stretch") || !image->hasElement("bar-active-center")) {
00134 return elementRect;
00135 }
00136
00137 QSize imageSize = image->size();
00138 image->resize();
00139 QSize tileSize = image->elementSize("bar-active-center");
00140 image->resize(imageSize);
00141
00142 if (elementRect.width() > elementRect.height()) {
00143 qreal ratio = qMax(1, tileSize.height() / tileSize.width());
00144 int numTiles = qMax(qreal(1.0), qreal(elementRect.width())/(qreal(elementRect.height())/ratio));
00145 tileSize = QSize(elementRect.width()/numTiles, elementRect.height());
00146
00147 QPoint center = elementRect.center().toPoint();
00148 elementRect.setWidth(tileSize.width()*numTiles);
00149 elementRect.moveCenter(center);
00150 } else {
00151 qreal ratio = qMax(1, tileSize.width() / tileSize.height());
00152 int numTiles = qMax(qreal(1.0), qreal(elementRect.height())/(qreal(elementRect.width())/ratio));
00153 tileSize = QSize(elementRect.width(), elementRect.height()/numTiles);
00154
00155 QPoint center = elementRect.center().toPoint();
00156 elementRect.setHeight(tileSize.height()*numTiles);
00157 elementRect.moveCenter(center);
00158 }
00159
00160 return elementRect;
00161 }
00162
00163 void paintBackground(QPainter *p)
00164 {
00165
00166 if (image->hasElement("background-center")) {
00167 QRectF elementRect = barRect();
00168 if (elementRect.isEmpty()) {
00169 return;
00170 }
00171
00172 QSize imageSize = image->size();
00173 image->resize();
00174
00175 image->setElementPrefix("background");
00176 image->resizeFrame(elementRect.size());
00177 image->paintFrame(p, elementRect.topLeft());
00178 image->resize(imageSize);
00179
00180 paintBar(p, "bar-inactive");
00181 } else {
00182 paint(p, "background");
00183 }
00184 }
00185
00186 void paintBar(QPainter *p, const QString &prefix)
00187 {
00188 QRectF elementRect = barRect();
00189
00190 image->setUsingRenderingCache(false);
00191 if (image->hasElement("hint-bar-stretch")) {
00192 image->resizeFrame(elementRect.size());
00193 image->paintFrame(p);
00194 } else {
00195 QSize imageSize = image->size();
00196 image->resize();
00197 QSize tileSize = image->elementSize("bar-active-center");
00198
00199 if (elementRect.width() > elementRect.height()) {
00200 qreal ratio = tileSize.height() / tileSize.width();
00201 int numTiles = elementRect.width()/(elementRect.height()/ratio);
00202 tileSize = QSize(elementRect.width()/numTiles, elementRect.height());
00203 } else {
00204 qreal ratio = tileSize.width() / tileSize.height();
00205 int numTiles = elementRect.height()/(elementRect.width()/ratio);
00206 tileSize = QSize(elementRect.width(), elementRect.height()/numTiles);
00207 }
00208
00209 image->setElementPrefix(prefix);
00210 image->resizeFrame(tileSize);
00211 p->drawTiledPixmap(elementRect, image->framePixmap());
00212 image->resize(imageSize);
00213 }
00214 image->setUsingRenderingCache(true);
00215 }
00216
00217 void paintForeground(QPainter *p)
00218 {
00219 for (int i = 0; i < labels.count(); ++i) {
00220 text(p, i);
00221 }
00222
00223 paint(p, "foreground");
00224 }
00225
00226 void setSizePolicyAndPreferredSize()
00227 {
00228 switch (meterType) {
00229 case Meter::BarMeterHorizontal:
00230 meter->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
00231 break;
00232 case Meter::BarMeterVertical:
00233 meter->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
00234 break;
00235 case Meter::AnalogMeter:
00236 default:
00237 meter->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
00238 break;
00239 }
00240
00241 if (image) {
00242
00243
00244
00245
00246
00247 uint i = 0;
00248 uint rows = 0;
00249 qreal prevY = -1;
00250 QString labelName = "label0";
00251 while (image->hasElement(labelName)) {
00252 if (image->elementRect(labelName).y() > prevY) {
00253 prevY = image->elementRect(labelName).y();
00254 rows++;
00255 }
00256 i++;
00257 labelName = QString("label%0").arg(i);
00258 }
00259
00260 Plasma::Theme *theme = Plasma::Theme::defaultTheme();
00261 QFont font = theme->font(Plasma::Theme::DefaultFont);
00262 QFontMetrics fm(font);
00263
00264 meter->setPreferredHeight((rows + 1) * fm.height());
00265 } else {
00266 meter->setPreferredSize(QSizeF(30, 30));
00267 }
00268 }
00269
00270 int minimum;
00271 int maximum;
00272 int value;
00273 int targetValue;
00274 QStringList labels;
00275 QList<Qt::Alignment> alignments;
00276 QList<QColor> colors;
00277 QList<QFont> fonts;
00278 QString svg;
00279 Meter::MeterType meterType;
00280 Plasma::FrameSvg *image;
00281 int minrotate;
00282 int maxrotate;
00283 Meter *meter;
00284 int movementId;
00285 };
00286
00287 Meter::Meter(QGraphicsItem *parent) :
00288 QGraphicsWidget(parent),
00289 d(new MeterPrivate(this))
00290 {
00291 d->setSizePolicyAndPreferredSize();
00292 }
00293
00294 Meter::~Meter()
00295 {
00296 delete d;
00297 }
00298
00299 void Meter::setMaximum(int maximum)
00300 {
00301 d->maximum = maximum;
00302 }
00303
00304 int Meter::maximum() const
00305 {
00306 return d->maximum;
00307 }
00308
00309 void Meter::setMinimum(int minimum)
00310 {
00311 d->minimum = minimum;
00312 }
00313
00314 int Meter::minimum() const
00315 {
00316 return d->minimum;
00317 }
00318
00319 void Meter::setValue(int value)
00320 {
00321 if (value == d->targetValue) {
00322 return;
00323 }
00324
00325 d->targetValue = qBound(d->minimum, value, d->maximum);
00326 int delta = abs(d->value - d->targetValue);
00327
00328 if (d->movementId) {
00329 Animator::self()->stopCustomAnimation(d->movementId);
00330 d->movementId = 0;
00331 }
00332
00333
00334 if (!(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) ||
00335 delta / qreal(d->maximum) < 0.1) {
00336 d->value = value;
00337 update();
00338 } else {
00339 d->movementId = Animator::self()->customAnimation(10, 100, Animator::EaseOutCurve,
00340 this, "progressChanged");
00341 }
00342 }
00343
00344 int Meter::value() const
00345 {
00346 return d->value;
00347 }
00348
00349 void Meter::setLabel(int index, const QString &text)
00350 {
00351 while (d->labels.count() <= index) {
00352 d->labels << QString();
00353 }
00354 d->labels[index] = text;
00355 }
00356
00357 QString Meter::label(int index) const
00358 {
00359 return d->labels[index];
00360 }
00361
00362 void Meter::setLabelColor(int index, const QColor &color)
00363 {
00364 while (d->colors.count() <= index) {
00365 d->colors << color;
00366 }
00367 d->colors[index] = color;
00368 }
00369
00370 QColor Meter::labelColor(int index) const
00371 {
00372 return d->colors[index];
00373 }
00374
00375 void Meter::setLabelFont(int index, const QFont &font)
00376 {
00377 while (d->fonts.count() <= index) {
00378 d->fonts << font;
00379 }
00380 d->fonts[index] = font;
00381 }
00382
00383 QFont Meter::labelFont(int index) const
00384 {
00385 return d->fonts[index];
00386 }
00387
00388 void Meter::setLabelAlignment(int index, const Qt::Alignment alignment)
00389 {
00390 while (d->alignments.count() <= index) {
00391 d->alignments << alignment;
00392 }
00393 d->alignments[index] = alignment;
00394 }
00395
00396 Qt::Alignment Meter::labelAlignment(int index) const
00397 {
00398 return d->alignments[index];
00399 }
00400
00401 QRectF Meter::labelRect(int index) const
00402 {
00403 QString elementID = QString("label%1").arg(index);
00404 return d->image->elementRect(elementID);
00405 }
00406
00407 void Meter::dataUpdated(const QString &sourceName, const Plasma::DataEngine::Data &data)
00408 {
00409 Q_UNUSED(sourceName)
00410
00411 foreach (const QVariant &v, data) {
00412 if (v.type() == QVariant::Int ||
00413 v.type() == QVariant::UInt ||
00414 v.type() == QVariant::LongLong ||
00415 v.type() == QVariant::ULongLong) {
00416 setValue(v.toInt());
00417 return;
00418 }
00419 }
00420 }
00421
00422 void Meter::setSvg(const QString &svg)
00423 {
00424 d->svg = svg;
00425 delete d->image;
00426 d->image = new Plasma::FrameSvg(this);
00427 d->image->setImagePath(svg);
00428
00429 d->image->resize();
00430 d->setSizePolicyAndPreferredSize();
00431 if (d->image->hasElement("rotateminmax")) {
00432 QRectF r = d->image->elementRect("rotateminmax");
00433 d->minrotate = (int)r.height();
00434 d->maxrotate = (int)r.width();
00435 }
00436 }
00437
00438 QString Meter::svg() const
00439 {
00440 return d->svg;
00441 }
00442
00443 void Meter::setMeterType(MeterType meterType)
00444 {
00445 d->meterType = meterType;
00446 if (d->svg.isEmpty()) {
00447 if (meterType == BarMeterHorizontal) {
00448 setSvg("widgets/bar_meter_horizontal");
00449 } else if (meterType == BarMeterVertical) {
00450 setSvg("widgets/bar_meter_vertical");
00451 } else if (meterType == AnalogMeter) {
00452 setSvg("widgets/analog_meter");
00453 }
00454 }
00455 d->setSizePolicyAndPreferredSize();
00456 }
00457
00458 Meter::MeterType Meter::meterType() const
00459 {
00460 return d->meterType;
00461 }
00462
00463 void Meter::paint(QPainter *p,
00464 const QStyleOptionGraphicsItem *option,
00465 QWidget *widget)
00466 {
00467 Q_UNUSED(option)
00468 Q_UNUSED(widget)
00469
00470 if (!d->image) {
00471 return;
00472 }
00473
00474 QRectF rect(QPointF(0, 0), size());
00475 QRectF clipRect;
00476 qreal percentage = 0.0;
00477 qreal angle = 0.0;
00478 QPointF rotateCenter;
00479 QSize intSize = QSize((int)size().width(), (int)size().height());
00480
00481 if (intSize != d->image->size()) {
00482 d->image->resize(intSize);
00483 }
00484
00485 if (d->maximum != d->minimum) {
00486 percentage = (qreal)d->value / (d->maximum - d->minimum);
00487 }
00488
00489 p->setRenderHint(QPainter::SmoothPixmapTransform);
00490 switch (d->meterType) {
00491 case BarMeterHorizontal:
00492 case BarMeterVertical:
00493 d->paintBackground(p);
00494
00495 p->save();
00496 clipRect = d->barRect();
00497 if (clipRect.width() > clipRect.height()) {
00498 clipRect.setWidth(clipRect.width() * percentage);
00499 } else {
00500 qreal bottom = clipRect.bottom();
00501 clipRect.setHeight(clipRect.height() * percentage);
00502 clipRect.moveBottom(bottom);
00503 }
00504 p->setClipRect(clipRect);
00505
00506
00507 if (d->image->hasElement("bar-active-center")) {
00508 d->paintBar(p, "bar-active");
00509 } else {
00510 d->paint(p, "bar");
00511 }
00512 p->restore();
00513
00514 d->paintForeground(p);
00515 break;
00516 case AnalogMeter:
00517 d->paintBackground(p);
00518
00519 p->save();
00520 if (d->image->hasElement("rotatecenter")) {
00521 QRectF r = d->image->elementRect("rotatecenter");
00522 rotateCenter = QPointF(r.left() + r.width() / 2,
00523 r.top() + r.height() / 2);
00524 } else {
00525 rotateCenter = QPointF(rect.width() / 2, rect.height() / 2);
00526 }
00527 angle = percentage * (d->maxrotate - d->minrotate) + d->minrotate;
00528
00529 if (d->image->hasElement("pointer-shadow")) {
00530 p->save();
00531 p->translate(rotateCenter+QPoint(2,3));
00532 p->rotate(angle);
00533 p->translate(-1 * rotateCenter);
00534 d->paint(p, "pointer-shadow");
00535 p->restore();
00536 }
00537
00538 p->translate(rotateCenter);
00539 p->rotate(angle);
00540 p->translate(-1 * rotateCenter);
00541 d->paint(p, "pointer");
00542 p->restore();
00543
00544 d->paintForeground(p);
00545 break;
00546 }
00547 }
00548
00549 }
00550
00551 #include "meter.moc"