qwt_painter.cpp

00001 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
00002  * Qwt Widget Library
00003  * Copyright (C) 1997   Josef Wilgen
00004  * Copyright (C) 2002   Uwe Rathmann
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the Qwt License, Version 1.0
00008  *****************************************************************************/
00009 
00010 // vim: expandtab
00011 
00012 #include <qwindowdefs.h>
00013 #include <qwidget.h>
00014 #include <qrect.h>
00015 #include <qpainter.h>
00016 #include <qpalette.h>
00017 #include <qpaintdevice.h>
00018 #include <qpixmap.h>
00019 #include <qstyle.h>
00020 #if QT_VERSION < 0x040000
00021 #include <qsimplerichtext.h>
00022 #else
00023 #include <qtextdocument.h>
00024 #include <qabstracttextdocumentlayout.h>
00025 #include <qstyleoption.h>
00026 #endif
00027 
00028 #include "qwt_rect.h"
00029 #include "qwt_math.h"
00030 #include "qwt_color_map.h"
00031 #include "qwt_scale_map.h"
00032 #include "qwt_painter.h"
00033 
00034 QwtMetricsMap QwtPainter::d_metricsMap;
00035 
00036 #if defined(Q_WS_X11)
00037 bool QwtPainter::d_deviceClipping = true;
00038 #else
00039 bool QwtPainter::d_deviceClipping = false;
00040 #endif
00041 
00042 #if QT_VERSION < 0x040000
00043 bool QwtPainter::d_SVGMode = false;
00044 #endif
00045 
00046 static inline bool needDeviceClipping(
00047     const QPainter *painter, bool deviceClipping)
00048 {
00049     return deviceClipping && 
00050         (painter->device()->devType() == QInternal::Widget ||
00051           painter->device()->devType() == QInternal::Pixmap );
00052 }
00053 
00061 void QwtPainter::setDeviceClipping(bool enable)
00062 {
00063     d_deviceClipping = enable;
00064 }
00065 
00072 bool QwtPainter::deviceClipping()
00073 {
00074     return d_deviceClipping;
00075 }
00076 
00081 const QRect &QwtPainter::deviceClipRect()
00082 {
00083     static QRect clip;
00084 
00085     if ( !clip.isValid() )
00086     {
00087         clip.setCoords(QWT_COORD_MIN, QWT_COORD_MIN,
00088             QWT_COORD_MAX, QWT_COORD_MAX);
00089     }
00090     return clip;
00091 }
00092 
00094 QwtPolygon QwtPainter::clip(const QwtPolygon &pa)
00095 {
00096     const QwtRect rect(deviceClipRect());
00097     return rect.clip(pa);
00098 }
00099 
00100 #if QT_VERSION < 0x040000 
00101 
00113 void QwtPainter::setSVGMode(bool on)
00114 {
00115     d_SVGMode = on;
00116 }
00117 
00118 bool QwtPainter::isSVGMode()
00119 {
00120     return d_SVGMode;
00121 }
00122 
00123 #endif // QT_VERSION < 0x040000
00124 
00133 void QwtPainter::setMetricsMap(const QPaintDevice *layout,
00134     const QPaintDevice *device)
00135 {
00136     d_metricsMap.setMetrics(layout, device);
00137 }
00138 
00143 void QwtPainter::setMetricsMap(const QwtMetricsMap &map)
00144 {
00145     d_metricsMap = map;
00146 }
00147 
00152 void QwtPainter::resetMetricsMap()
00153 {
00154     d_metricsMap = QwtMetricsMap();
00155 }
00156 
00160 const QwtMetricsMap &QwtPainter::metricsMap()
00161 {
00162     return d_metricsMap;
00163 }
00164 
00168 void QwtPainter::setClipRect(QPainter *painter, const QRect &rect)
00169 {
00170     painter->setClipRect(d_metricsMap.layoutToDevice(rect, painter));
00171 }
00172 
00176 void QwtPainter::drawRect(QPainter *painter, int x, int y, int w, int h) 
00177 {
00178     drawRect(painter, QRect(x, y, w, h));
00179 }
00180 
00184 void QwtPainter::drawRect(QPainter *painter, const QRect &rect) 
00185 {
00186     QRect r = d_metricsMap.layoutToDevice(rect, painter);
00187 
00188     QRect clipRect;
00189 
00190     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
00191     if ( deviceClipping )
00192         clipRect = deviceClipRect();
00193 
00194     if ( clipRect.isValid() )
00195     {
00196         if ( !clipRect.intersects(r) )
00197             return;
00198 
00199         if ( !clipRect.contains(r) )
00200         {
00201             fillRect(painter, r & clipRect, painter->brush());
00202 
00203             int pw = painter->pen().width();
00204             pw = pw % 2 + pw / 2;
00205 
00206             QwtPolygon pa(5);
00207             pa.setPoint(0, r.left(), r.top());
00208             pa.setPoint(1, r.right() - pw, r.top());
00209             pa.setPoint(2, r.right() - pw, r.bottom() - pw);
00210             pa.setPoint(3, r.left(), r.bottom() - pw);
00211             pa.setPoint(4, r.left(), r.top());
00212 
00213             painter->save();
00214             painter->setBrush(Qt::NoBrush);
00215             drawPolyline(painter, pa);
00216             painter->restore();
00217 
00218             return;
00219         }
00220     }
00221 
00222 #if QT_VERSION >= 0x040000
00223     if ( painter->pen().style() != Qt::NoPen && 
00224         painter->pen().color().isValid() )
00225     {
00226         // Qt4 adds the pen to the rect, Qt3 not.
00227         int pw = painter->pen().width();
00228         if ( pw == 0 )
00229             pw = 1;
00230 
00231         r.setWidth(r.width() - pw);
00232         r.setHeight(r.height() - pw);
00233     }
00234 #endif
00235     painter->drawRect(r);
00236 }
00237 
00241 void QwtPainter::fillRect(QPainter *painter, 
00242     const QRect &rect, const QBrush &brush)
00243 {
00244     if ( !rect.isValid() )
00245         return;
00246 
00247     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
00248 
00249     QRect clipRect;
00250 #if QT_VERSION >= 0x040000
00251 
00252     /*
00253       Performance of Qt4 is horrible for non trivial brushs. Without
00254       clipping expect minutes or hours for repainting large rects
00255       (might result from zooming)
00256     */
00257 
00258     clipRect = painter->window();
00259     if ( painter->hasClipping() )
00260         clipRect &= painter->clipRegion().boundingRect();
00261     if ( deviceClipping )
00262         clipRect &= deviceClipRect();
00263 #else
00264     if ( deviceClipping )
00265         clipRect = deviceClipRect();
00266 #endif
00267 
00268     QRect r = d_metricsMap.layoutToDevice(rect, painter);
00269     if ( clipRect.isValid() )
00270         r = r.intersect(clipRect);
00271 
00272     if ( r.isValid() )
00273         painter->fillRect(r, brush);
00274 }
00275 
00279 void QwtPainter::drawPie(QPainter *painter, const QRect &rect, 
00280     int a, int alen)
00281 {
00282     QRect r = d_metricsMap.layoutToDevice(rect, painter);
00283 
00284     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
00285     if ( deviceClipping && !deviceClipRect().contains(rect) )
00286         return;
00287 
00288     painter->drawPie(r, a, alen);
00289 }
00290 
00294 void QwtPainter::drawEllipse(QPainter *painter, const QRect &rect)
00295 {
00296     QRect r = d_metricsMap.layoutToDevice(rect, painter);
00297 
00298     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
00299 
00300     if ( deviceClipping && !deviceClipRect().contains(rect) )
00301         return;
00302 
00303 #if QT_VERSION >= 0x040000
00304     if ( painter->pen().style() != Qt::NoPen &&
00305         painter->pen().color().isValid() )
00306     {
00307         // Qt4 adds the pen to the rect, Qt3 not.
00308         int pw = painter->pen().width();
00309         if ( pw == 0 )
00310             pw = 1;
00311 
00312         r.setWidth(r.width() - pw);
00313         r.setHeight(r.height() - pw);
00314     }
00315 #endif
00316 
00317     painter->drawEllipse(r);
00318 }
00319 
00323 void QwtPainter::drawText(QPainter *painter, int x, int y, 
00324         const QString &text)
00325 {
00326     drawText(painter, QPoint(x, y), text);
00327 }
00328 
00332 void QwtPainter::drawText(QPainter *painter, const QPoint &pos, 
00333         const QString &text)
00334 {
00335     const QPoint p = d_metricsMap.layoutToDevice(pos, painter);
00336 
00337     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
00338 
00339     if ( deviceClipping && !deviceClipRect().contains(p) )
00340         return;
00341 
00342     painter->drawText(p, text);
00343 }
00344 
00348 void QwtPainter::drawText(QPainter *painter, int x, int y, int w, int h, 
00349         int flags, const QString &text)
00350 {
00351     drawText(painter, QRect(x, y, w, h), flags, text);
00352 }
00353 
00357 void QwtPainter::drawText(QPainter *painter, const QRect &rect, 
00358         int flags, const QString &text)
00359 {
00360     QRect textRect = d_metricsMap.layoutToDevice(rect, painter);
00361 #if QT_VERSION < 0x040000
00362     if ( d_SVGMode &&
00363         ( flags == 0 || flags & Qt::AlignVCenter ) 
00364         && painter->device()->devType() & QInternal::Picture )
00365     {
00366         /*
00367             Qt3 misalignes texts, when saving a text
00368             to a SVG image. 
00369          */
00370         textRect.setY(textRect.y() - painter->fontMetrics().height() / 4);
00371     }
00372 #endif
00373     painter->drawText(textRect, flags, text);
00374 }
00375 
00376 #ifndef QT_NO_RICHTEXT
00377 
00381 #if QT_VERSION < 0x040000
00382 
00383 void QwtPainter::drawSimpleRichText(QPainter *painter, const QRect &rect,
00384     int flags, QSimpleRichText &text)
00385 {
00386     QColorGroup cg;
00387     cg.setColor(QColorGroup::Text, painter->pen().color());
00388 
00389     const QRect scaledRect = d_metricsMap.layoutToDevice(rect, painter);
00390 
00391     text.setWidth(painter, scaledRect.width());
00392 
00393     // QSimpleRichText is Qt::AlignTop by default
00394 
00395     int y = scaledRect.y();
00396     if (flags & Qt::AlignBottom)
00397         y += (scaledRect.height() - text.height());
00398     else if (flags & Qt::AlignVCenter)
00399         y += (scaledRect.height() - text.height())/2;
00400 
00401     text.draw(painter, scaledRect.x(), y, scaledRect, cg);
00402 }
00403 #else
00404 void QwtPainter::drawSimpleRichText(QPainter *painter, const QRect &rect,
00405     int flags, QTextDocument &text)
00406 {
00407     const QRect scaledRect = d_metricsMap.layoutToDevice(rect, painter);
00408     text.setPageSize(QSize(scaledRect.width(), QWIDGETSIZE_MAX));
00409 
00410     QAbstractTextDocumentLayout* layout = text.documentLayout();
00411 
00412     const int height = qRound(layout->documentSize().height());
00413     int y = scaledRect.y();
00414     if (flags & Qt::AlignBottom)
00415         y += (scaledRect.height() - height);
00416     else if (flags & Qt::AlignVCenter)
00417         y += (scaledRect.height() - height)/2;
00418 
00419     QAbstractTextDocumentLayout::PaintContext context;
00420     context.palette.setColor(QPalette::Text, painter->pen().color());
00421 
00422     painter->save();
00423 
00424     painter->translate(scaledRect.x(), scaledRect.y());
00425     layout->draw(painter, context);
00426 
00427     painter->restore();
00428 }
00429 #endif
00430 
00431 #endif // !QT_NO_RICHTEXT
00432 
00433 
00437 void QwtPainter::drawLine(QPainter *painter, int x1, int y1, int x2, int y2)
00438 {
00439     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
00440 
00441     if ( deviceClipping && 
00442         !(deviceClipRect().contains(x1, y1) && deviceClipRect().contains(x2, y2)) )
00443     {
00444         QwtPolygon pa(2);
00445         pa.setPoint(0, x1, y1);
00446         pa.setPoint(1, x2, y2);
00447         drawPolyline(painter, pa);
00448         return;
00449     }
00450 
00451     if ( d_metricsMap.isIdentity() )
00452     {
00453 #if QT_VERSION >= 0x030200 && QT_VERSION < 0x040000
00454         if ( !painter->device()->isExtDev() )
00455 #endif
00456         {
00457             painter->drawLine(x1, y1, x2, y2);
00458             return;
00459         }
00460     }
00461 
00462     const QPoint p1 = d_metricsMap.layoutToDevice(QPoint(x1, y1));
00463     const QPoint p2 = d_metricsMap.layoutToDevice(QPoint(x2, y2));
00464 
00465 #if QT_VERSION >= 0x030200 && QT_VERSION < 0x040000
00466     if ( painter->device()->isExtDev() )
00467     {
00468         // Strange: the postscript driver of QPrinter adds an offset 
00469         // of 0.5 to the start/endpoint when using drawLine, but not
00470         // for lines painted with drawLineSegments.
00471 
00472         QwtPolygon pa(2);
00473         pa.setPoint(0, p1);
00474         pa.setPoint(1, p2);
00475         painter->drawLineSegments(pa);
00476     }
00477     else
00478         painter->drawLine(p1, p2);
00479 #else
00480     painter->drawLine(p1, p2);
00481 #endif
00482 }
00483 
00487 void QwtPainter::drawPolygon(QPainter *painter, const QwtPolygon &pa)
00488 {
00489     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
00490 
00491     QwtPolygon cpa = d_metricsMap.layoutToDevice(pa);
00492     if ( deviceClipping )
00493     {
00494 #ifdef __GNUC__
00495 #endif
00496         cpa = clip(cpa);
00497     }
00498     painter->drawPolygon(cpa);
00499 }
00500 
00504 void QwtPainter::drawPolyline(QPainter *painter, const QwtPolygon &pa)
00505 {
00506     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
00507 
00508     QwtPolygon cpa = d_metricsMap.layoutToDevice(pa);
00509     if ( deviceClipping )
00510         cpa = clip(cpa);
00511     painter->drawPolyline(cpa);
00512 }
00513 
00518 void QwtPainter::drawPoint(QPainter *painter, int x, int y)
00519 {
00520     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
00521 
00522     const QPoint pos = d_metricsMap.layoutToDevice(QPoint(x, y));
00523 
00524     if ( deviceClipping && !deviceClipRect().contains(pos) )
00525         return;
00526 
00527     painter->drawPoint(pos);
00528 }
00529 
00530 void QwtPainter::drawColoredArc(QPainter *painter, const QRect &rect, 
00531     int peak, int arc, int interval, const QColor &c1, const QColor &c2)
00532 {
00533     int h1, s1, v1;
00534     int h2, s2, v2;
00535 
00536 #if QT_VERSION < 0x040000
00537     c1.hsv(&h1, &s1, &v1);
00538     c2.hsv(&h2, &s2, &v2);
00539 #else
00540     c1.getHsv(&h1, &s1, &v1);
00541     c2.getHsv(&h2, &s2, &v2);
00542 #endif
00543     
00544     arc /= 2;
00545     for ( int angle = -arc; angle < arc; angle += interval)
00546     {
00547         double ratio;
00548         if ( angle >= 0 )
00549             ratio = 1.0 - angle / double(arc);
00550         else
00551             ratio = 1.0 + angle / double(arc);
00552             
00553 
00554         QColor c;
00555         c.setHsv( h1 + qRound(ratio * (h2 - h1)),
00556             s1 + qRound(ratio * (s2 - s1)),
00557             v1 + qRound(ratio * (v2 - v1)) );
00558 
00559         painter->setPen(QPen(c, painter->pen().width()));
00560         painter->drawArc(rect, (peak + angle) * 16, interval * 16);
00561     }
00562 }
00563 
00564 void QwtPainter::drawFocusRect(QPainter *painter, QWidget *widget)
00565 {
00566     drawFocusRect(painter, widget, widget->rect());
00567 }
00568 
00569 void QwtPainter::drawFocusRect(QPainter *painter, QWidget *widget,
00570     const QRect &rect)
00571 {
00572 #if QT_VERSION < 0x040000
00573         widget->style().drawPrimitive(QStyle::PE_FocusRect, painter,
00574             rect, widget->colorGroup());
00575 #else
00576         QStyleOptionFocusRect opt;
00577         opt.init(widget);
00578         opt.rect = rect;
00579         opt.state |= QStyle::State_HasFocus;
00580 
00581         widget->style()->drawPrimitive(QStyle::PE_FrameFocusRect, 
00582             &opt, painter, widget);
00583 #endif
00584 
00585 }
00586 
00588 #if QT_VERSION < 0x040000
00589 void QwtPainter::drawRoundFrame(QPainter *painter, const QRect &rect,
00590     int width, const QColorGroup &cg, bool sunken)
00591 #else
00592 void QwtPainter::drawRoundFrame(QPainter *painter, const QRect &rect,
00593     int width, const QPalette &palette, bool sunken)
00594 #endif
00595 {
00596 
00597 #if QT_VERSION < 0x040000
00598     QColor c0 = cg.mid();
00599     QColor c1, c2;
00600     if ( sunken )
00601     {
00602         c1 = cg.dark();
00603         c2 = cg.light();
00604     }
00605     else
00606     {
00607         c1 = cg.light();
00608         c2 = cg.dark();
00609     }
00610 #else
00611     QColor c0 = palette.color(QPalette::Mid);
00612     QColor c1, c2;
00613     if ( sunken )
00614     {
00615         c1 = palette.color(QPalette::Dark);
00616         c2 = palette.color(QPalette::Light);
00617     }
00618     else
00619     {
00620         c1 = palette.color(QPalette::Light);
00621         c2 = palette.color(QPalette::Dark);
00622     }
00623 #endif
00624 
00625     painter->setPen(QPen(c0, width));
00626     painter->drawArc(rect, 0, 360 * 16); // full
00627 
00628     const int peak = 150;
00629     const int interval = 2;
00630 
00631     if ( c0 != c1 )
00632         drawColoredArc(painter, rect, peak, 160, interval, c0, c1);
00633     if ( c0 != c2 )
00634         drawColoredArc(painter, rect, peak + 180, 120, interval, c0, c2);
00635 }
00636 
00637 void QwtPainter::drawColorBar(QPainter *painter,
00638         const QwtColorMap &colorMap, const QwtDoubleInterval &interval,
00639         const QwtScaleMap &scaleMap, Qt::Orientation orientation,
00640         const QRect &rect)
00641 {
00642     painter->save();
00643 
00644     QwtPainter::setClipRect(painter, rect);
00645 
00646 #if QT_VERSION < 0x040000
00647     QValueVector<QRgb> colorTable;
00648 #else
00649     QVector<QRgb> colorTable;
00650 #endif
00651     if ( colorMap.format() == QwtColorMap::Indexed )
00652         colorTable = colorMap.colorTable(interval);
00653 
00654     QColor c;
00655 
00656     const QRect devRect = d_metricsMap.layoutToDevice(rect);
00657 
00658     if ( orientation == Qt::Horizontal )
00659     {
00660         QwtScaleMap sMap = scaleMap;
00661         sMap.setPaintInterval(devRect.left(), devRect.right());
00662 
00663         for ( int x = devRect.left(); x <= devRect.right(); x++ )
00664         {
00665             const double value = sMap.invTransform(x);
00666 
00667             if ( colorMap.format() == QwtColorMap::RGB )
00668                 c.setRgb(colorMap.rgb(interval, value));
00669             else
00670                 c = colorTable[colorMap.colorIndex(interval, value)];
00671 
00672             painter->setBrush(QBrush(c));
00673 
00674             const QRect r(x, devRect.top(), 1, devRect.height());
00675             QwtPainter::drawRect(painter, r);
00676             painter->setPen(c);
00677             painter->drawLine(x, devRect.top(), x, devRect.bottom() - 1);
00678         }
00679     }
00680     else // Vertical
00681     {
00682         QwtScaleMap sMap = scaleMap;
00683         sMap.setPaintInterval(devRect.bottom(), devRect.top());
00684 
00685         for ( int y = devRect.top(); y <= devRect.bottom(); y++ )
00686         {
00687             const double value = sMap.invTransform(y);
00688 
00689             if ( colorMap.format() == QwtColorMap::RGB )
00690                 c.setRgb(colorMap.rgb(interval, value));
00691             else
00692                 c = colorTable[colorMap.colorIndex(interval, value)];
00693 
00694             painter->setPen(c);
00695             painter->drawLine(devRect.left(), y, devRect.right() - 1, y);
00696         }
00697     }
00698     painter->restore();
00699 }

Generated on Mon Feb 26 21:22:37 2007 for Qwt User's Guide by  doxygen 1.4.6