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 QSize imageSize = image->size();
00171 image->resize();
00172
00173 image->setElementPrefix("background");
00174 image->resizeFrame(elementRect.size());
00175 image->paintFrame(p, elementRect.topLeft());
00176 image->resize(imageSize);
00177
00178 paintBar(p, "bar-inactive");
00179 } else {
00180 paint(p, "background");
00181 }
00182 }
00183
00184 void paintBar(QPainter *p, const QString &prefix)
00185 {
00186 QRectF elementRect = barRect();
00187
00188 if (image->hasElement("hint-bar-stretch")) {
00189 image->resizeFrame(elementRect.size());
00190 image->paintFrame(p);
00191 } else {
00192 QSize imageSize = image->size();
00193 image->resize();
00194 QSize tileSize = image->elementSize("bar-active-center");
00195
00196 if (elementRect.width() > elementRect.height()) {
00197 qreal ratio = tileSize.height() / tileSize.width();
00198 int numTiles = elementRect.width()/(elementRect.height()/ratio);
00199 tileSize = QSize(elementRect.width()/numTiles, elementRect.height());
00200 } else {
00201 qreal ratio = tileSize.width() / tileSize.height();
00202 int numTiles = elementRect.height()/(elementRect.width()/ratio);
00203 tileSize = QSize(elementRect.width(), elementRect.height()/numTiles);
00204 }
00205
00206 image->setElementPrefix(prefix);
00207 image->resizeFrame(tileSize);
00208 p->drawTiledPixmap(elementRect, image->framePixmap());
00209 image->resize(imageSize);
00210 }
00211 }
00212
00213 void paintForeground(QPainter *p)
00214 {
00215 for (int i = 0; i < labels.count(); ++i) {
00216 text(p, i);
00217 }
00218
00219 paint(p, "foreground");
00220 }
00221
00222 void setSizePolicyAndPreferredSize()
00223 {
00224 switch (meterType) {
00225 case Meter::BarMeterHorizontal:
00226 meter->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
00227 break;
00228 case Meter::BarMeterVertical:
00229 meter->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
00230 break;
00231 case Meter::AnalogMeter:
00232 default:
00233 meter->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
00234 break;
00235 }
00236
00237 if (image) {
00238
00239
00240
00241
00242
00243 uint i = 0;
00244 uint rows = 0;
00245 qreal prevY = -1;
00246 QString labelName = "label0";
00247 while (image->hasElement(labelName)) {
00248 if (image->elementRect(labelName).y() > prevY) {
00249 prevY = image->elementRect(labelName).y();
00250 rows++;
00251 }
00252 i++;
00253 labelName = QString("label%0").arg(i);
00254 }
00255
00256 Plasma::Theme *theme = Plasma::Theme::defaultTheme();
00257 QFont font = theme->font(Plasma::Theme::DefaultFont);
00258 QFontMetrics fm(font);
00259
00260 meter->setPreferredHeight((rows + 1) * fm.height());
00261 } else {
00262 meter->setPreferredSize(QSizeF(30, 30));
00263 }
00264 }
00265
00266 int minimum;
00267 int maximum;
00268 int value;
00269 int targetValue;
00270 QStringList labels;
00271 QList<Qt::Alignment> alignments;
00272 QList<QColor> colors;
00273 QList<QFont> fonts;
00274 QString svg;
00275 Meter::MeterType meterType;
00276 Plasma::FrameSvg *image;
00277 int minrotate;
00278 int maxrotate;
00279 Meter *meter;
00280 int movementId;
00281 };
00282
00283 Meter::Meter(QGraphicsItem *parent) :
00284 QGraphicsWidget(parent),
00285 d(new MeterPrivate(this))
00286 {
00287 d->setSizePolicyAndPreferredSize();
00288 }
00289
00290 Meter::~Meter()
00291 {
00292 delete d;
00293 }
00294
00295 void Meter::setMaximum(int maximum)
00296 {
00297 d->maximum = maximum;
00298 }
00299
00300 int Meter::maximum() const
00301 {
00302 return d->maximum;
00303 }
00304
00305 void Meter::setMinimum(int minimum)
00306 {
00307 d->minimum = minimum;
00308 }
00309
00310 int Meter::minimum() const
00311 {
00312 return d->minimum;
00313 }
00314
00315 void Meter::setValue(int value)
00316 {
00317 if (value == d->targetValue) {
00318 return;
00319 }
00320
00321 d->targetValue = qBound(d->minimum, value, d->maximum);
00322 int delta = abs(d->value - d->targetValue);
00323
00324 if (d->movementId) {
00325 Animator::self()->stopCustomAnimation(d->movementId);
00326 d->movementId = 0;
00327 }
00328
00329
00330 if (!(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) ||
00331 delta / qreal(d->maximum) < 0.1) {
00332 d->value = value;
00333 update();
00334 } else {
00335 d->movementId = Animator::self()->customAnimation(10, 100, Animator::EaseOutCurve,
00336 this, "progressChanged");
00337 }
00338 }
00339
00340 int Meter::value() const
00341 {
00342 return d->value;
00343 }
00344
00345 void Meter::setLabel(int index, const QString &text)
00346 {
00347 while (d->labels.count() <= index) {
00348 d->labels << QString();
00349 }
00350 d->labels[index] = text;
00351 }
00352
00353 QString Meter::label(int index) const
00354 {
00355 return d->labels[index];
00356 }
00357
00358 void Meter::setLabelColor(int index, const QColor &color)
00359 {
00360 while (d->colors.count() <= index) {
00361 d->colors << color;
00362 }
00363 d->colors[index] = color;
00364 }
00365
00366 QColor Meter::labelColor(int index) const
00367 {
00368 return d->colors[index];
00369 }
00370
00371 void Meter::setLabelFont(int index, const QFont &font)
00372 {
00373 while (d->fonts.count() <= index) {
00374 d->fonts << font;
00375 }
00376 d->fonts[index] = font;
00377 }
00378
00379 QFont Meter::labelFont(int index) const
00380 {
00381 return d->fonts[index];
00382 }
00383
00384 void Meter::setLabelAlignment(int index, Qt::Alignment alignment)
00385 {
00386 while (d->alignments.count() <= index) {
00387 d->alignments << alignment;
00388 }
00389 d->alignments[index] = alignment;
00390 }
00391
00392 Qt::Alignment Meter::labelAlignment(int index) const
00393 {
00394 return d->alignments[index];
00395 }
00396
00397 QRectF Meter::labelRect(int index) const
00398 {
00399 QString elementID = QString("label%1").arg(index);
00400 return d->image->elementRect(elementID);
00401 }
00402
00403 void Meter::dataUpdated(const QString &sourceName, const Plasma::DataEngine::Data &data)
00404 {
00405 Q_UNUSED(sourceName)
00406
00407 foreach (const QVariant &v, data) {
00408 if (v.type() == QVariant::Int ||
00409 v.type() == QVariant::UInt ||
00410 v.type() == QVariant::LongLong ||
00411 v.type() == QVariant::ULongLong) {
00412 setValue(v.toInt());
00413 return;
00414 }
00415 }
00416 }
00417
00418 void Meter::setSvg(const QString &svg)
00419 {
00420 d->svg = svg;
00421 delete d->image;
00422 d->image = new Plasma::FrameSvg(this);
00423 d->image->setImagePath(svg);
00424
00425 d->image->resize();
00426 d->setSizePolicyAndPreferredSize();
00427 if (d->image->hasElement("rotateminmax")) {
00428 QRectF r = d->image->elementRect("rotateminmax");
00429 d->minrotate = (int)r.height();
00430 d->maxrotate = (int)r.width();
00431 }
00432 }
00433
00434 QString Meter::svg() const
00435 {
00436 return d->svg;
00437 }
00438
00439 void Meter::setMeterType(MeterType meterType)
00440 {
00441 d->meterType = meterType;
00442 if (d->svg.isEmpty()) {
00443 if (meterType == BarMeterHorizontal) {
00444 setSvg("widgets/bar_meter_horizontal");
00445 } else if (meterType == BarMeterVertical) {
00446 setSvg("widgets/bar_meter_vertical");
00447 } else if (meterType == AnalogMeter) {
00448 setSvg("widgets/analog_meter");
00449 }
00450 }
00451 d->setSizePolicyAndPreferredSize();
00452 }
00453
00454 Meter::MeterType Meter::meterType() const
00455 {
00456 return d->meterType;
00457 }
00458
00459 void Meter::paint(QPainter *p,
00460 const QStyleOptionGraphicsItem *option,
00461 QWidget *widget)
00462 {
00463 Q_UNUSED(option)
00464 Q_UNUSED(widget)
00465
00466 if (!d->image) {
00467 return;
00468 }
00469
00470 QRectF rect(QPointF(0, 0), size());
00471 QRectF clipRect;
00472 qreal percentage = 0.0;
00473 qreal angle = 0.0;
00474 QPointF rotateCenter;
00475 QSize intSize = QSize((int)size().width(), (int)size().height());
00476
00477 if (intSize != d->image->size()) {
00478 d->image->resize(intSize);
00479 }
00480
00481 if (d->maximum != d->minimum) {
00482 percentage = (qreal)d->value / (d->maximum - d->minimum);
00483 }
00484 p->setRenderHint(QPainter::SmoothPixmapTransform);
00485 switch (d->meterType) {
00486 case BarMeterHorizontal:
00487 case BarMeterVertical:
00488 d->paintBackground(p);
00489
00490 p->save();
00491 clipRect = d->barRect();
00492 if (clipRect.width() > clipRect.height()) {
00493 clipRect.setWidth(clipRect.width() * percentage);
00494 } else {
00495 qreal bottom = clipRect.bottom();
00496 clipRect.setHeight(clipRect.height() * percentage);
00497 clipRect.moveBottom(bottom);
00498 }
00499 p->setClipRect(clipRect);
00500
00501 if (d->image->hasElement("bar-active-center")) {
00502 d->paintBar(p, "bar-active");
00503 } else {
00504 d->paint(p, "bar");
00505 }
00506 p->restore();
00507
00508 d->paintForeground(p);
00509 break;
00510 case AnalogMeter:
00511 d->paintBackground(p);
00512
00513 p->save();
00514 if (d->image->hasElement("rotatecenter")) {
00515 QRectF r = d->image->elementRect("rotatecenter");
00516 rotateCenter = QPointF(r.left() + r.width() / 2,
00517 r.top() + r.height() / 2);
00518 } else {
00519 rotateCenter = QPointF(rect.width() / 2, rect.height() / 2);
00520 }
00521 angle = percentage * (d->maxrotate - d->minrotate) + d->minrotate;
00522
00523 if (d->image->hasElement("pointer-shadow")) {
00524 p->save();
00525 p->translate(rotateCenter+QPoint(2,3));
00526 p->rotate(angle);
00527 p->translate(-1 * rotateCenter);
00528 d->paint(p, "pointer-shadow");
00529 p->restore();
00530 }
00531
00532 p->translate(rotateCenter);
00533 p->rotate(angle);
00534 p->translate(-1 * rotateCenter);
00535 d->paint(p, "pointer");
00536 p->restore();
00537
00538 d->paintForeground(p);
00539 break;
00540 }
00541 }
00542
00543 }
00544
00545 #include "meter.moc"