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 } 00329 00330 int MeterPrivate::meterValue() const 00331 { 00332 return value; 00333 } 00334 00335 void MeterPrivate::setMeterValue(int value) 00336 { 00337 progressChanged(value); 00338 } 00339 00340 void Meter::setLabel(int index, const QString &text) 00341 { 00342 while (d->labels.count() <= index) { 00343 d->labels << QString(); 00344 } 00345 d->labels[index] = text; 00346 } 00347 00348 QString Meter::label(int index) const 00349 { 00350 return d->labels[index]; 00351 } 00352 00353 void Meter::setLabelColor(int index, const QColor &color) 00354 { 00355 while (d->colors.count() <= index) { 00356 d->colors << color; 00357 } 00358 d->colors[index] = color; 00359 } 00360 00361 QColor Meter::labelColor(int index) const 00362 { 00363 return d->colors[index]; 00364 } 00365 00366 void Meter::setLabelFont(int index, const QFont &font) 00367 { 00368 while (d->fonts.count() <= index) { 00369 d->fonts << font; 00370 } 00371 d->fonts[index] = font; 00372 } 00373 00374 QFont Meter::labelFont(int index) const 00375 { 00376 return d->fonts[index]; 00377 } 00378 00379 void Meter::setLabelAlignment(int index, const Qt::Alignment alignment) 00380 { 00381 while (d->alignments.count() <= index) { 00382 d->alignments << alignment; 00383 } 00384 d->alignments[index] = alignment; 00385 } 00386 00387 Qt::Alignment Meter::labelAlignment(int index) const 00388 { 00389 return d->alignments[index]; 00390 } 00391 00392 QRectF Meter::labelRect(int index) const 00393 { 00394 QString elementID = QString("label%1").arg(index); 00395 return d->image->elementRect(elementID); 00396 } 00397 00398 void Meter::dataUpdated(const QString &sourceName, const Plasma::DataEngine::Data &data) 00399 { 00400 Q_UNUSED(sourceName) 00401 00402 foreach (const QVariant &v, data) { 00403 if (v.type() == QVariant::Int || 00404 v.type() == QVariant::UInt || 00405 v.type() == QVariant::LongLong || 00406 v.type() == QVariant::ULongLong) { 00407 setValue(v.toInt()); 00408 return; 00409 } 00410 } 00411 } 00412 00413 void Meter::setSvg(const QString &svg) 00414 { 00415 if (d->svg == svg) { 00416 return; 00417 } 00418 00419 d->svg = svg; 00420 delete d->image; 00421 d->image = new Plasma::FrameSvg(this); 00422 d->image->setImagePath(svg); 00423 // To create renderer and get default size 00424 d->image->resize(); 00425 d->setSizePolicyAndPreferredSize(); 00426 if (d->image->hasElement("rotateminmax")) { 00427 QRectF r = d->image->elementRect("rotateminmax"); 00428 d->minrotate = (int)r.height(); 00429 d->maxrotate = (int)r.width(); 00430 } 00431 } 00432 00433 QString Meter::svg() const 00434 { 00435 return d->svg; 00436 } 00437 00438 void Meter::setMeterType(MeterType meterType) 00439 { 00440 d->meterType = meterType; 00441 if (d->svg.isEmpty()) { 00442 if (meterType == BarMeterHorizontal) { 00443 setSvg("widgets/bar_meter_horizontal"); 00444 } else if (meterType == BarMeterVertical) { 00445 setSvg("widgets/bar_meter_vertical"); 00446 } else if (meterType == AnalogMeter) { 00447 setSvg("widgets/analog_meter"); 00448 } 00449 } 00450 d->setSizePolicyAndPreferredSize(); 00451 } 00452 00453 Meter::MeterType Meter::meterType() const 00454 { 00455 return d->meterType; 00456 } 00457 00458 void Meter::paint(QPainter *p, 00459 const QStyleOptionGraphicsItem *option, 00460 QWidget *widget) 00461 { 00462 Q_UNUSED(option) 00463 Q_UNUSED(widget) 00464 00465 if (d->svg.isEmpty()) { 00466 setMeterType(d->meterType); 00467 } 00468 00469 if (!d->image) { 00470 return; 00471 } 00472 00473 QRectF rect(QPointF(0, 0), size()); 00474 QRectF clipRect; 00475 qreal percentage = 0.0; 00476 qreal angle = 0.0; 00477 QPointF rotateCenter; 00478 QSize intSize = QSize((int)size().width(), (int)size().height()); 00479 00480 if (intSize != d->image->size()) { 00481 d->image->resize(intSize); 00482 } 00483 00484 if (d->maximum != d->minimum) { 00485 percentage = (qreal)(d->value - d->minimum) / (d->maximum - d->minimum); 00486 } 00487 00488 p->setRenderHint(QPainter::SmoothPixmapTransform); 00489 switch (d->meterType) { 00490 case BarMeterHorizontal: 00491 case BarMeterVertical: 00492 d->paintBackground(p); 00493 00494 p->save(); 00495 clipRect = d->barRect(); 00496 if (clipRect.width() > clipRect.height()) { 00497 clipRect.setWidth(clipRect.width() * percentage); 00498 } else { 00499 qreal bottom = clipRect.bottom(); 00500 clipRect.setHeight(clipRect.height() * percentage); 00501 clipRect.moveBottom(bottom); 00502 } 00503 p->setClipRect(clipRect, Qt::IntersectClip); 00504 00505 //be retrocompatible 00506 if (d->image->hasElement("bar-active-center")) { 00507 d->paintBar(p, "bar-active"); 00508 } else { 00509 d->paint(p, "bar"); 00510 } 00511 p->restore(); 00512 00513 d->paintForeground(p); 00514 break; 00515 case AnalogMeter: 00516 d->paintBackground(p); 00517 00518 p->save(); 00519 if (d->image->hasElement("rotatecenter")) { 00520 QRectF r = d->image->elementRect("rotatecenter"); 00521 rotateCenter = QPointF(r.left() + r.width() / 2, 00522 r.top() + r.height() / 2); 00523 } else { 00524 rotateCenter = QPointF(rect.width() / 2, rect.height() / 2); 00525 } 00526 angle = percentage * (d->maxrotate - d->minrotate) + d->minrotate; 00527 00528 if (d->image->hasElement("pointer-shadow")) { 00529 p->save(); 00530 p->translate(rotateCenter+QPoint(2,3)); 00531 p->rotate(angle); 00532 p->translate(-1 * rotateCenter); 00533 d->paint(p, "pointer-shadow"); 00534 p->restore(); 00535 } 00536 00537 p->translate(rotateCenter); 00538 p->rotate(angle); 00539 p->translate(-1 * rotateCenter); 00540 d->paint(p, "pointer"); 00541 p->restore(); 00542 00543 d->paintForeground(p); 00544 break; 00545 } 00546 } 00547 00548 QSizeF Meter::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const 00549 { 00550 return QGraphicsWidget::sizeHint(which, constraint); 00551 } 00552 00553 } // End of namepace 00554 00555 #include "meter.moc" 00556 #include "../private/meter_p.moc"
KDE 4.6 API Reference