# qwt_text.cpp

    00001 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
    00002  * Qwt Widget Library
    00003  * Copyright (C) 1997   Josef Wilgen
    00004  * Copyright (C) 2003   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 <qmap.h>
    00013 #include <qfont.h>
    00014 #include <qcolor.h>
    00015 #include <qpen.h>
    00016 #include <qbrush.h>
    00017 #include <qpainter.h>
    00018 #include "qwt_painter.h"
    00019 #include "qwt_text_engine.h"
    00020 #include "qwt_text.h"
    00021 #if QT_VERSION >= 0x040000
    00022 #include <qapplication.h>
    00023 #include <qdesktopwidget.h>
    00024 #endif
    00025
    00026 class QwtTextEngineDict
    00027 {
    00028 public:
    00029     QwtTextEngineDict();
    00030     ~QwtTextEngineDict();
    00031
    00032     void setTextEngine(QwtText::TextFormat, QwtTextEngine *);
    00033     const QwtTextEngine *textEngine(QwtText::TextFormat) const;
    00034     const QwtTextEngine *textEngine(const QString &,
    00035         QwtText::TextFormat) const;
    00036
    00037 private:
    00038     typedef QMap<int, QwtTextEngine *> EngineMap;
    00039
    00040     inline const QwtTextEngine *engine(EngineMap::const_iterator &it) const
    00041     {
    00042 #if QT_VERSION < 0x040000
    00043         return it.data();
    00044 #else
    00045         return it.value();
    00046 #endif
    00047     }
    00048
    00049     EngineMap d_map;
    00050 };
    00051
    00052 QwtTextEngineDict::QwtTextEngineDict()
    00053 {
    00054     d_map.insert(QwtText::PlainText, new QwtPlainTextEngine());
    00055 #ifndef QT_NO_RICHTEXT
    00056     d_map.insert(QwtText::RichText, new QwtRichTextEngine());
    00057 #endif
    00058 }
    00059
    00060 QwtTextEngineDict::~QwtTextEngineDict()
    00061 {
    00062     for ( EngineMap::const_iterator it = d_map.begin();
    00063         it != d_map.end(); ++it )
    00064     {
    00065         QwtTextEngine *textEngine = (QwtTextEngine *)engine(it);
    00066         delete textEngine;
    00067     }
    00068 }
    00069
    00070 const QwtTextEngine *QwtTextEngineDict::textEngine(const QString& text,
    00071     QwtText::TextFormat format) const
    00072 {
    00073     if ( format == QwtText::AutoText )
    00074     {
    00075         for ( EngineMap::const_iterator it = d_map.begin();
    00076             it != d_map.end(); ++it )
    00077         {
    00078             if ( it.key() != QwtText::PlainText )
    00079             {
    00080                 const QwtTextEngine *e = engine(it);
    00081                 if ( e && e->mightRender(text) )
    00082                     return (QwtTextEngine *)e;
    00083             }
    00084         }
    00085     }
    00086
    00087     EngineMap::const_iterator it = d_map.find(format);
    00088     if ( it != d_map.end() )
    00089     {
    00090         const QwtTextEngine *e = engine(it);
    00091         if ( e )
    00092             return e;
    00093     }
    00094
    00095     it = d_map.find(QwtText::PlainText);
    00096     return engine(it);
    00097 }
    00098
    00099 void QwtTextEngineDict::setTextEngine(QwtText::TextFormat format,
    00100     QwtTextEngine *engine)
    00101 {
    00102     if ( format == QwtText::AutoText )
    00103         return;
    00104
    00105     if ( format == QwtText::PlainText && engine == NULL )
    00106         return;
    00107
    00108     EngineMap::const_iterator it = d_map.find(format);
    00109     if ( it != d_map.end() )
    00110     {
    00111         const QwtTextEngine *e = this->engine(it);
    00112         if ( e )
    00113             delete e;
    00114
    00115         d_map.remove(format);
    00116     }
    00117
    00118     if ( engine != NULL )
    00119         d_map.insert(format, engine);
    00120 }
    00121
    00122 const QwtTextEngine *QwtTextEngineDict::textEngine(
    00123     QwtText::TextFormat format) const
    00124 {
    00125     const QwtTextEngine *e = NULL;
    00126
    00127     EngineMap::const_iterator it = d_map.find(format);
    00128     if ( it != d_map.end() )
    00129         e = engine(it);
    00130
    00131     return e;
    00132 }
    00133
    00134 static QwtTextEngineDict *engineDict = NULL;
    00135
    00136 class QwtText::PrivateData
    00137 {
    00138 public:
    00139     PrivateData():
    00140         renderFlags(Qt::AlignCenter),
    00141         backgroundPen(Qt::NoPen),
    00142         backgroundBrush(Qt::NoBrush),
    00143         paintAttributes(0),
    00144         layoutAttributes(0),
    00145         textEngine(NULL)
    00146     {
    00147     }
    00148
    00149     int renderFlags;
    00150     QString text;
    00151     QFont font;
    00152     QColor color;
    00153     QPen backgroundPen;
    00154     QBrush backgroundBrush;
    00155
    00156     int paintAttributes;
    00157     int layoutAttributes;
    00158
    00159     const QwtTextEngine *textEngine;
    00160 };
    00161
    00162 class QwtText::LayoutCache
    00163 {
    00164 public:
    00165     void invalidate()
    00166     {
    00167         textSize = QSize();
    00168     }
    00169
    00170     QFont font;
    00171     QSize textSize;
    00172 };
    00173
    00180 QwtText::QwtText(const QString &text, QwtText::TextFormat textFormat)
    00181 {
    00182     d_data = new PrivateData;
    00183     d_data->text = text;
    00184     d_data->textEngine = textEngine(text, textFormat);
    00185
    00186     d_layoutCache = new LayoutCache;
    00187 }
    00188
    00190 QwtText::QwtText(const QwtText &other)
    00191 {
    00192     d_data = new PrivateData;
    00193     *d_data = *other.d_data;
    00194
    00195     d_layoutCache = new LayoutCache;
    00196     *d_layoutCache = *other.d_layoutCache;
    00197 }
    00198
    00200 QwtText::~QwtText()
    00201 {
    00202     delete d_data;
    00203     delete d_layoutCache;
    00204 }
    00205
    00207 QwtText &QwtText::operator=(const QwtText &other)
    00208 {
    00209     *d_data = *other.d_data;
    00210     *d_layoutCache = *other.d_layoutCache;
    00211     return *this;
    00212 }
    00213
    00214 int QwtText::operator==(const QwtText &other) const
    00215 {
    00216     return d_data->renderFlags == other.d_data->renderFlags &&
    00217         d_data->text == other.d_data->text &&
    00218         d_data->font == other.d_data->font &&
    00219         d_data->color == other.d_data->color &&
    00220         d_data->backgroundPen == other.d_data->backgroundPen &&
    00221         d_data->backgroundBrush == other.d_data->backgroundBrush &&
    00222         d_data->paintAttributes == other.d_data->paintAttributes &&
    00223         d_data->textEngine == other.d_data->textEngine;
    00224 }
    00225
    00226 int QwtText::operator!=(const QwtText &other) const // invalidate
    00227 {
    00228    return !(other == *this);
    00229 }
    00230
    00237 void QwtText::setText(const QString &text,
    00238     QwtText::TextFormat textFormat)
    00239 {
    00240     d_data->text = text;
    00241     d_data->textEngine = textEngine(text, textFormat);
    00242     d_layoutCache->invalidate();
    00243 }
    00244
    00249 QString QwtText::text() const
    00250 {
    00251     return d_data->text;
    00252 }
    00253
    00264 void QwtText::setRenderFlags(int renderFlags)
    00265 {
    00266     if ( renderFlags != d_data->renderFlags )
    00267     {
    00268         d_data->renderFlags = renderFlags;
    00269         d_layoutCache->invalidate();
    00270     }
    00271 }
    00272
    00277 int QwtText::renderFlags() const
    00278 {
    00279     return d_data->renderFlags;
    00280 }
    00281
    00289 void QwtText::setFont(const QFont &font)
    00290 {
    00291     d_data->font = font;
    00292     setPaintAttribute(PaintUsingTextFont);
    00293 }
    00294
    00296 QFont QwtText::font() const
    00297 {
    00298     return d_data->font;
    00299 }
    00300
    00308 QFont QwtText::usedFont(const QFont &defaultFont) const
    00309 {
    00310     if ( d_data->paintAttributes & PaintUsingTextFont )
    00311         return d_data->font;
    00312
    00313     return defaultFont;
    00314 }
    00315
    00323 void QwtText::setColor(const QColor &color)
    00324 {
    00325     d_data->color = color;
    00326     setPaintAttribute(PaintUsingTextColor);
    00327 }
    00328
    00330 QColor QwtText::color() const
    00331 {
    00332     return d_data->color;
    00333 }
    00334
    00342 QColor QwtText::usedColor(const QColor &defaultColor) const
    00343 {
    00344     if ( d_data->paintAttributes & PaintUsingTextColor )
    00345         return d_data->color;
    00346
    00347     return defaultColor;
    00348 }
    00349
    00356 void QwtText::setBackgroundPen(const QPen &pen)
    00357 {
    00358     d_data->backgroundPen = pen;
    00359     setPaintAttribute(PaintBackground);
    00360 }
    00361
    00366 QPen QwtText::backgroundPen() const
    00367 {
    00368     return d_data->backgroundPen;
    00369 }
    00370
    00377 void QwtText::setBackgroundBrush(const QBrush &brush)
    00378 {
    00379     d_data->backgroundBrush = brush;
    00380     setPaintAttribute(PaintBackground);
    00381 }
    00382
    00387 QBrush QwtText::backgroundBrush() const
    00388 {
    00389     return d_data->backgroundBrush;
    00390 }
    00391
    00401 void QwtText::setPaintAttribute(PaintAttribute attribute, bool on)
    00402 {
    00403     if ( on )
    00404         d_data->paintAttributes |= attribute;
    00405     else
    00406         d_data->paintAttributes &= ~attribute;
    00407 }
    00408
    00417 bool QwtText::testPaintAttribute(PaintAttribute attribute) const
    00418 {
    00419     return d_data->paintAttributes & attribute;
    00420 }
    00421
    00429 void QwtText::setLayoutAttribute(LayoutAttribute attribute, bool on)
    00430 {
    00431     if ( on )
    00432         d_data->layoutAttributes |= attribute;
    00433     else
    00434         d_data->layoutAttributes &= ~attribute;
    00435 }
    00436
    00445 bool QwtText::testLayoutAttribute(LayoutAttribute attribute) const
    00446 {
    00447     return d_data->layoutAttributes | attribute;
    00448 }
    00449
    00458 int QwtText::heightForWidth(int width, const QFont &defaultFont) const
    00459 {
    00460     const QwtMetricsMap map = QwtPainter::metricsMap();
    00461     width = map.layoutToScreenX(width);
    00462
    00463 #if QT_VERSION < 0x040000
    00464     const QFont font = usedFont(defaultFont);
    00465 #else
    00466     // We want to calculate in screen metrics. So
    00467     // we need a font that uses screen metrics
    00468
    00469     const QFont font(usedFont(defaultFont), QApplication::desktop());
    00470 #endif
    00471
    00472     int h = 0;
    00473
    00474     if ( d_data->layoutAttributes & MinimumLayout )
    00475     {
    00476         int left, right, top, bottom;
    00477         d_data->textEngine->textMargins(font, d_data->text,
    00478             left, right, top, bottom);
    00479
    00480         h = d_data->textEngine->heightForWidth(
    00481             font, d_data->renderFlags, d_data->text,
    00482             width + left + right);
    00483
    00484         h -= top + bottom;
    00485     }
    00486     else
    00487     {
    00488         h = d_data->textEngine->heightForWidth(
    00489             font, d_data->renderFlags, d_data->text, width);
    00490     }
    00491
    00492     h = map.screenToLayoutY(h);
    00493     return h;
    00494 }
    00495
    00510 QSize QwtText::textSize(const QFont &defaultFont) const
    00511 {
    00512 #if QT_VERSION < 0x040000
    00513     const QFont font(usedFont(defaultFont));
    00514 #else
    00515     // We want to calculate in screen metrics. So
    00516     // we need a font that uses screen metrics
    00517
    00518     const QFont font(usedFont(defaultFont), QApplication::desktop());
    00519 #endif
    00520
    00521     if ( !d_layoutCache->textSize.isValid()
    00522         || d_layoutCache->font != font )
    00523     {
    00524         d_layoutCache->textSize = d_data->textEngine->textSize(
    00525             font, d_data->renderFlags, d_data->text);
    00526         d_layoutCache->font = font;
    00527     }
    00528
    00529     QSize sz = d_layoutCache->textSize;
    00530
    00531     const QwtMetricsMap map = QwtPainter::metricsMap();
    00532
    00533     if ( d_data->layoutAttributes & MinimumLayout )
    00534     {
    00535         int left, right, top, bottom;
    00536         d_data->textEngine->textMargins(font, d_data->text,
    00537             left, right, top, bottom);
    00538         sz -= QSize(left + right, top + bottom);
    00539 #if QT_VERSION >= 0x040000
    00540         if ( !map.isIdentity() )
    00541         {
    00542 #ifdef __GNUC__
    00543 #endif
    00544             /*
    00545                 When printing in high resolution, the tick labels
    00546                 of are cut of. We need to find out why, but for
    00547                 the moment we add a couple of pixels instead.
    00548              */
    00549             sz += QSize(3, 0);
    00550         }
    00551 #endif
    00552     }
    00553
    00554     sz = map.screenToLayout(sz);
    00555     return sz;
    00556 }
    00557
    00564 void QwtText::draw(QPainter *painter, const QRect &rect) const
    00565 {
    00566     if ( d_data->paintAttributes & PaintBackground )
    00567     {
    00568         if ( d_data->backgroundPen != Qt::NoPen ||
    00569             d_data->backgroundBrush != Qt::NoBrush )
    00570         {
    00571             painter->save();
    00572             painter->setPen(d_data->backgroundPen);
    00573             painter->setBrush(d_data->backgroundBrush);
    00574 #if QT_VERSION < 0x040000
    00575             QwtPainter::drawRect(painter, rect);
    00576 #else
    00577             const QRect r(rect.x(), rect.y(),
    00578                 rect.width() - 1, rect.height() - 1);
    00579             QwtPainter::drawRect(painter, r);
    00580 #endif
    00581             painter->restore();
    00582         }
    00583     }
    00584
    00585     painter->save();
    00586
    00587     if ( d_data->paintAttributes & PaintUsingTextFont )
    00588     {
    00589         painter->setFont(d_data->font);
    00590     }
    00591
    00592     if ( d_data->paintAttributes & PaintUsingTextColor )
    00593     {
    00594         if ( d_data->color.isValid() )
    00595             painter->setPen(d_data->color);
    00596     }
    00597
    00598     QRect expandedRect = rect;
    00599     if ( d_data->layoutAttributes & MinimumLayout )
    00600     {
    00601 #if QT_VERSION < 0x040000
    00602         const QFont font(painter->font());
    00603 #else
    00604         // We want to calculate in screen metrics. So
    00605         // we need a font that uses screen metrics
    00606
    00607         const QFont font(painter->font(), QApplication::desktop());
    00608 #endif
    00609
    00610         int left, right, top, bottom;
    00611         d_data->textEngine->textMargins(
    00612             font, d_data->text,
    00613             left, right, top, bottom);
    00614
    00615         const QwtMetricsMap map = QwtPainter::metricsMap();
    00616         left = map.screenToLayoutX(left);
    00617         right = map.screenToLayoutX(right);
    00618         top = map.screenToLayoutY(top);
    00619         bottom = map.screenToLayoutY(bottom);
    00620
    00621         expandedRect.setTop(rect.top() - top);
    00622         expandedRect.setBottom(rect.bottom() + bottom);
    00623         expandedRect.setLeft(rect.left() - left);
    00624         expandedRect.setRight(rect.right() + right);
    00625     }
    00626
    00627     d_data->textEngine->draw(painter, expandedRect,
    00628         d_data->renderFlags, d_data->text);
    00629
    00630     painter->restore();
    00631 }
    00632
    00646 const QwtTextEngine *QwtText::textEngine(const QString &text,
    00647     QwtText::TextFormat format)
    00648 {
    00649     if ( engineDict == NULL )
    00650     {
    00651         /*
    00652           Note: engineDict is allocated, the first time it is used,
    00653                 but never deleted, because there is no known last access time.
    00654                 So don't be irritated, if it is reported as a memory leak
    00655                 from your memory profiler.
    00656          */
    00657         engineDict = new QwtTextEngineDict();
    00658     }
    00659
    00660     return engineDict->textEngine(text, format);
    00661 }
    00662
    00681 void QwtText::setTextEngine(QwtText::TextFormat format,
    00682     QwtTextEngine *engine)
    00683 {
    00684     if ( engineDict == NULL )
    00685         engineDict = new QwtTextEngineDict();
    00686
    00687     engineDict->setTextEngine(format, engine);
    00688 }
    00689
    00701 const QwtTextEngine *QwtText::textEngine(QwtText::TextFormat format)
    00702 {
    00703     if ( engineDict == NULL )
    00704         engineDict = new QwtTextEngineDict();
    00705
    00706     return engineDict->textEngine(format);
    00707 }