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"
KDE 4.7 API Reference