• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

Plasma

meter.cpp

Go to the documentation of this file.
00001 /*
00002  *   Copyright (C) 2007 Petri Damsten <damu@iki.fi>
00003  *
00004  *   This program is free software; you can redistribute it and/or modify
00005  *   it under the terms of the GNU Library General Public License as
00006  *   published by the Free Software Foundation; either version 2, or
00007  *   (at your option) any later version.
00008  *
00009  *   This program is distributed in the hope that it will be useful,
00010  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *   GNU General Public License for more details
00013  *
00014  *   You should have received a copy of the GNU Library General Public
00015  *   License along with this program; if not, write to the
00016  *   Free Software Foundation, Inc.,
00017  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
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             //kDebug() << "done";
00068             movementId = 0;
00069         } else {
00070             int frame = progress * 10;
00071             int delta = targetValue - value;
00072             value += (delta / qreal(10 - frame));
00073             //kDebug() << frame << value << targetValue;
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         //be retrocompatible with themes for kde <= 4.1
00166         if (image->hasElement("background-center")) {
00167             QRectF elementRect = barRect();
00168             if (elementRect.isEmpty()) {
00169                 return; // nothing to be done 
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             //set a sane preferredSize. We can't just use the svg's native size, since that way
00243             //letters get cut off if the user uses a font larger then usual. Check how many rows of
00244             //labels we have, add 1 (the progress bar), and multiply by the font height to get a
00245             //somewhat sane size height. This is not perfect but work well enough for 4.2. I suggest
00246             //we look into alternatives for 4.3.
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     //kDebug() << d->targetValue << d->value << delta;
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     // To create renderer and get default size
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         //be retrocompatible
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 } // End of namepace
00550 
00551 #include "meter.moc"

Plasma

Skip menu "Plasma"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal