• Skip to content
  • Skip to link menu
KDE 4.7 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • 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 #include "private/meter_p.h"
00022 
00023 #include <cmath>
00024 
00025 #include <QPainter>
00026 #include <QTimeLine>
00027 #include <QPropertyAnimation>
00028 
00029 #include <kdebug.h>
00030 #include <kglobalsettings.h>
00031 
00032 #include "plasma/animator.h"
00033 #include "plasma/framesvg.h"
00034 #include "plasma/theme.h"
00035 
00036 namespace Plasma {
00037 
00038 MeterPrivate::MeterPrivate(Meter *m)
00039         : QObject(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 {
00050 }
00051 
00052 void MeterPrivate::progressChanged(int progress)
00053     {
00054         value = progress;
00055         meter->update();
00056     }
00057 
00058 void MeterPrivate::paint(QPainter *p, const QString &elementID)
00059     {
00060         if (image->hasElement(elementID)) {
00061             QRectF elementRect = image->elementRect(elementID);
00062             image->paint(p, elementRect, elementID);
00063         }
00064     }
00065 
00066 void MeterPrivate::text(QPainter *p, int index)
00067     {
00068         QString elementID = QString("label%1").arg(index);
00069         QString text = labels[index];
00070 
00071         if (image->hasElement(elementID)) {
00072             QRectF elementRect = image->elementRect(elementID);
00073             Qt::Alignment align = Qt::AlignCenter;
00074 
00075 
00076             if (colors.count() > index) {
00077                 p->setPen(QPen(colors[index]));
00078             } else {
00079                 p->setPen(Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor));
00080             }
00081             if (fonts.count() > index) {
00082                 p->setFont(fonts[index]);
00083             }
00084 
00085             QFontMetricsF fm(p->font());
00086             // If the height is too small increase the Height of the button to shall the whole text #192988
00087             if (elementRect.height() < fm.height()) {
00088                 QPointF oldCenter = elementRect.center();
00089                 elementRect.setHeight(fm.height());
00090                 elementRect.moveCenter(oldCenter);
00091             }
00092 
00093             if (alignments.count() > index) {
00094                 align = alignments[index];
00095             }
00096             if (elementRect.width() > elementRect.height()) {
00097                 if (align&Qt::AlignLeft) {
00098                     p->drawText(elementRect.bottomLeft(), text);
00099                 } else {
00100                     p->drawText(elementRect, align, text);
00101                 }
00102             } else {
00103                 p->save();
00104                 QPointF rotateCenter(
00105                         elementRect.left() + elementRect.width() / 2,
00106                         elementRect.top() + elementRect.height() / 2);
00107                 p->translate(rotateCenter);
00108                 p->rotate(-90);
00109                 p->translate(elementRect.height() / -2,
00110                              elementRect.width() / -2);
00111                 QRectF r(0, 0, elementRect.height(), elementRect.width());
00112                 p->drawText(r, align, text);
00113                 p->restore();
00114             }
00115         }
00116     }
00117 
00118 QRectF MeterPrivate::barRect()
00119     {
00120         QRectF elementRect;
00121 
00122         if (labels.count() > 0) {
00123             elementRect = image->elementRect("background");
00124         } else {
00125             elementRect = QRectF(QPoint(0,0), meter->size());
00126         }
00127 
00128         if (image->hasElement("hint-bar-stretch") || !image->hasElement("bar-active-center")) {
00129             return elementRect;
00130         }
00131 
00132         QSize imageSize = image->size();
00133         image->resize();
00134         QSize tileSize = image->elementSize("bar-active-center");
00135         image->resize(imageSize);
00136 
00137         if (elementRect.width() > elementRect.height()) {
00138             qreal ratio = qMax(1, tileSize.height() / tileSize.width());
00139             int numTiles = qMax(qreal(1.0), qreal(elementRect.width())/(qreal(elementRect.height())/ratio));
00140             tileSize = QSize(elementRect.width()/numTiles, elementRect.height());
00141 
00142             QPoint center = elementRect.center().toPoint();
00143             elementRect.setWidth(tileSize.width()*numTiles);
00144             elementRect.moveCenter(center);
00145         } else {
00146             qreal ratio = qMax(1, tileSize.width() / tileSize.height());
00147             int numTiles = qMax(qreal(1.0), qreal(elementRect.height())/(qreal(elementRect.width())/ratio));
00148             tileSize = QSize(elementRect.width(), elementRect.height()/numTiles);
00149 
00150             QPoint center = elementRect.center().toPoint();
00151             elementRect.setHeight(tileSize.height()*numTiles);
00152             elementRect.moveCenter(center);
00153         }
00154 
00155         return elementRect;
00156     }
00157 
00158 void MeterPrivate::paintBackground(QPainter *p)
00159     {
00160         //be retrocompatible with themes for kde <= 4.1
00161         if (image->hasElement("background-center")) {
00162             QRectF elementRect = barRect();
00163             if (elementRect.isEmpty()) {
00164                 return; // nothing to be done
00165             }
00166 
00167             QSize imageSize = image->size();
00168             image->resize();
00169 
00170             image->setElementPrefix("background");
00171             image->resizeFrame(elementRect.size());
00172             image->paintFrame(p, elementRect.topLeft());
00173             image->resize(imageSize);
00174 
00175             paintBar(p, "bar-inactive");
00176         } else {
00177             paint(p, "background");
00178         }
00179     }
00180 
00181 void MeterPrivate::paintBar(QPainter *p, const QString &prefix)
00182     {
00183         QRectF elementRect = barRect();
00184 
00185         image->setUsingRenderingCache(false);
00186         if (image->hasElement("hint-bar-stretch")) {
00187             image->resizeFrame(elementRect.size());
00188             image->paintFrame(p);
00189         } else {
00190             QSize imageSize = image->size();
00191             image->resize();
00192             QSize tileSize = image->elementSize("bar-active-center");
00193 
00194             if (elementRect.width() > elementRect.height()) {
00195                 qreal ratio = tileSize.height() / tileSize.width();
00196                 int numTiles = elementRect.width()/(elementRect.height()/ratio);
00197                 tileSize = QSize(elementRect.width()/numTiles, elementRect.height());
00198             } else {
00199                 qreal ratio = tileSize.width() / tileSize.height();
00200                 int numTiles = elementRect.height()/(elementRect.width()/ratio);
00201                 tileSize = QSize(elementRect.width(), elementRect.height()/numTiles);
00202             }
00203 
00204             image->setElementPrefix(prefix);
00205             image->resizeFrame(tileSize);
00206             p->drawTiledPixmap(elementRect, image->framePixmap());
00207             image->resize(imageSize);
00208         }
00209         image->setUsingRenderingCache(true);
00210     }
00211 
00212 void MeterPrivate::paintForeground(QPainter *p)
00213     {
00214         for (int i = 0; i < labels.count(); ++i) {
00215             text(p, i);
00216         }
00217 
00218         paint(p, "foreground");
00219     }
00220 
00221 void MeterPrivate::setSizePolicyAndPreferredSize()
00222     {
00223         switch (meterType) {
00224             case Meter::BarMeterHorizontal:
00225                 meter->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
00226                 break;
00227             case Meter::BarMeterVertical:
00228                 meter->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
00229                 break;
00230             case Meter::AnalogMeter:
00231             default:
00232                 meter->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
00233                 break;
00234         }
00235 
00236         if (image) {
00237             //set a sane preferredSize. We can't just use the svg's native size, since that way
00238             //letters get cut off if the user uses a font larger then usual. Check how many rows of
00239             //labels we have, add 1 (the progress bar), and multiply by the font height to get a
00240             //somewhat sane size height. This is not perfect but work well enough for 4.2. I suggest
00241             //we look into alternatives for 4.3.
00242             uint i = 0;
00243             uint rows = 0;
00244             qreal prevY = -1;
00245             QString labelName = "label0";
00246             while (image->hasElement(labelName)) {
00247                 if (image->elementRect(labelName).y() > prevY) {
00248                     prevY = image->elementRect(labelName).y();
00249                     rows++;
00250                 }
00251                 i++;
00252                 labelName = QString("label%0").arg(i);
00253             }
00254 
00255             Plasma::Theme *theme = Plasma::Theme::defaultTheme();
00256             QFont font = theme->font(Plasma::Theme::DefaultFont);
00257             QFontMetrics fm(font);
00258 
00259             meter->setPreferredHeight((rows + 1) * fm.height());
00260         } else {
00261             meter->setPreferredSize(QSizeF(30, 30));
00262         }
00263     }
00264 
00265 Meter::Meter(QGraphicsItem *parent) :
00266         QGraphicsWidget(parent),
00267         d(new MeterPrivate(this))
00268 {
00269     d->setSizePolicyAndPreferredSize();
00270 
00271     d->animation = new QPropertyAnimation(d, "meterValue");
00272 }
00273 
00274 Meter::~Meter()
00275 {
00276     delete d->animation;
00277     delete d;
00278 }
00279 
00280 void Meter::setMaximum(int maximum)
00281 {
00282     d->maximum = maximum;
00283 }
00284 
00285 int Meter::maximum() const
00286 {
00287     return d->maximum;
00288 }
00289 
00290 void Meter::setMinimum(int minimum)
00291 {
00292     d->minimum = minimum;
00293 }
00294 
00295 int Meter::minimum() const
00296 {
00297     return d->minimum;
00298 }
00299 
00300 int Meter::value() const
00301 {
00302     return d->value;
00303 }
00304 
00305 void Meter::setValue(int value)
00306 {
00307     if (value == d->targetValue) {
00308         return;
00309     }
00310 
00311     d->targetValue = qBound(d->minimum, value, d->maximum);
00312     int delta = abs(d->value - d->targetValue);
00313 
00314     if (d->animation->state() != QAbstractAnimation::Running) {
00315         d->animation->stop();
00316     }
00317 
00318     //kDebug() << d->targetValue << d->value << delta;
00319     if (!(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) ||
00320         delta / qreal(d->maximum) < 0.1) {
00321         d->value = value;
00322         update();
00323     } else  {
00324         d->animation->setStartValue(d->value);
00325         d->animation->setEndValue(value);
00326         d->animation->start();
00327     }
00328     emit valueChanged(value);
00329 }
00330 
00331 int MeterPrivate::meterValue() const
00332 {
00333     return value;
00334 }
00335 
00336 void MeterPrivate::setMeterValue(int value)
00337 {
00338     progressChanged(value);
00339 }
00340 
00341 void Meter::setLabel(int index, const QString &text)
00342 {
00343     while (d->labels.count() <= index) {
00344         d->labels << QString();
00345     }
00346     d->labels[index] = text;
00347 }
00348 
00349 QString Meter::label(int index) const
00350 {
00351     return d->labels[index];
00352 }
00353 
00354 void Meter::setLabelColor(int index, const QColor &color)
00355 {
00356     while (d->colors.count() <= index) {
00357         d->colors << color;
00358     }
00359     d->colors[index] = color;
00360 }
00361 
00362 QColor Meter::labelColor(int index) const
00363 {
00364     return d->colors[index];
00365 }
00366 
00367 void Meter::setLabelFont(int index, const QFont &font)
00368 {
00369     while (d->fonts.count() <= index) {
00370         d->fonts << font;
00371     }
00372     d->fonts[index] = font;
00373 }
00374 
00375 QFont Meter::labelFont(int index) const
00376 {
00377     return d->fonts[index];
00378 }
00379 
00380 void Meter::setLabelAlignment(int index, const Qt::Alignment alignment)
00381 {
00382     while (d->alignments.count() <= index) {
00383         d->alignments << alignment;
00384     }
00385     d->alignments[index] = alignment;
00386 }
00387 
00388 Qt::Alignment Meter::labelAlignment(int index) const
00389 {
00390     return d->alignments[index];
00391 }
00392 
00393 QRectF Meter::labelRect(int index) const
00394 {
00395     QString elementID = QString("label%1").arg(index);
00396     return d->image->elementRect(elementID);
00397 }
00398 
00399 void Meter::dataUpdated(const QString &sourceName, const Plasma::DataEngine::Data &data)
00400 {
00401     Q_UNUSED(sourceName)
00402 
00403     foreach (const QVariant &v, data) {
00404         if (v.type() == QVariant::Int ||
00405             v.type() == QVariant::UInt ||
00406             v.type() == QVariant::LongLong ||
00407             v.type() == QVariant::ULongLong) {
00408             setValue(v.toInt());
00409             return;
00410         }
00411     }
00412 }
00413 
00414 void Meter::setSvg(const QString &svg)
00415 {
00416     if (d->svg == svg) {
00417         return;
00418     }
00419 
00420     d->svg = svg;
00421     delete d->image;
00422     d->image = new Plasma::FrameSvg(this);
00423     d->image->setImagePath(svg);
00424     // To create renderer and get default size
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->svg.isEmpty()) {
00467         setMeterType(d->meterType);
00468     }
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->minimum) / (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, Qt::IntersectClip);
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 QSizeF Meter::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
00550 {
00551     return QGraphicsWidget::sizeHint(which, constraint);
00552 }
00553 
00554 } // End of namepace
00555 
00556 #include "meter.moc"
00557 #include "../private/meter_p.moc"

Plasma

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.5
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