qwt_text.cpp 16.6 KB
Newer Older
pixhawk's avatar
pixhawk committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 * Copyright (C) 1997   Josef Wilgen
 * Copyright (C) 2003   Uwe Rathmann
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the Qwt License, Version 1.0
 *****************************************************************************/

// vim: expandtab

#include <qmap.h>
#include <qfont.h>
#include <qcolor.h>
#include <qpen.h>
#include <qbrush.h>
#include <qpainter.h>
#include "qwt_painter.h"
#include "qwt_text_engine.h"
#include "qwt_text.h"
#if QT_VERSION >= 0x040000
#include <qapplication.h>
#include <qdesktopwidget.h>
#endif

class QwtTextEngineDict
{
public:
    QwtTextEngineDict();
    ~QwtTextEngineDict();

    void setTextEngine(QwtText::TextFormat, QwtTextEngine *);
    const QwtTextEngine *textEngine(QwtText::TextFormat) const;
34 35
    const QwtTextEngine *textEngine(const QString &,
                                    QwtText::TextFormat) const;
pixhawk's avatar
pixhawk committed
36 37 38 39

private:
    typedef QMap<int, QwtTextEngine *> EngineMap;

40
    inline const QwtTextEngine *engine(EngineMap::const_iterator &it) const {
pixhawk's avatar
pixhawk committed
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
#if QT_VERSION < 0x040000
        return it.data();
#else
        return it.value();
#endif
    }

    EngineMap d_map;
};

QwtTextEngineDict::QwtTextEngineDict()
{
    d_map.insert(QwtText::PlainText, new QwtPlainTextEngine());
#ifndef QT_NO_RICHTEXT
    d_map.insert(QwtText::RichText, new QwtRichTextEngine());
#endif
}

QwtTextEngineDict::~QwtTextEngineDict()
{
61 62
    for ( EngineMap::const_iterator it = d_map.begin();
            it != d_map.end(); ++it ) {
pixhawk's avatar
pixhawk committed
63 64 65 66 67 68
        QwtTextEngine *textEngine = (QwtTextEngine *)engine(it);
        delete textEngine;
    }
}

const QwtTextEngine *QwtTextEngineDict::textEngine(const QString& text,
69
        QwtText::TextFormat format) const
pixhawk's avatar
pixhawk committed
70
{
71 72 73 74
    if ( format == QwtText::AutoText ) {
        for ( EngineMap::const_iterator it = d_map.begin();
                it != d_map.end(); ++it ) {
            if ( it.key() != QwtText::PlainText ) {
pixhawk's avatar
pixhawk committed
75 76 77 78 79 80 81 82
                const QwtTextEngine *e = engine(it);
                if ( e && e->mightRender(text) )
                    return (QwtTextEngine *)e;
            }
        }
    }

    EngineMap::const_iterator it = d_map.find(format);
83
    if ( it != d_map.end() ) {
pixhawk's avatar
pixhawk committed
84 85 86 87 88 89 90 91 92
        const QwtTextEngine *e = engine(it);
        if ( e )
            return e;
    }

    it = d_map.find(QwtText::PlainText);
    return engine(it);
}

93 94
void QwtTextEngineDict::setTextEngine(QwtText::TextFormat format,
                                      QwtTextEngine *engine)
pixhawk's avatar
pixhawk committed
95 96 97 98 99 100 101 102
{
    if ( format == QwtText::AutoText )
        return;

    if ( format == QwtText::PlainText && engine == NULL )
        return;

    EngineMap::const_iterator it = d_map.find(format);
103
    if ( it != d_map.end() ) {
pixhawk's avatar
pixhawk committed
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
        const QwtTextEngine *e = this->engine(it);
        if ( e )
            delete e;

        d_map.remove(format);
    }

    if ( engine != NULL )
        d_map.insert(format, engine);
}

const QwtTextEngine *QwtTextEngineDict::textEngine(
    QwtText::TextFormat format) const
{
    const QwtTextEngine *e = NULL;

    EngineMap::const_iterator it = d_map.find(format);
    if ( it != d_map.end() )
        e = engine(it);

    return e;
}

static QwtTextEngineDict *engineDict = NULL;

class QwtText::PrivateData
{
public:
    PrivateData():
        renderFlags(Qt::AlignCenter),
        backgroundPen(Qt::NoPen),
        backgroundBrush(Qt::NoBrush),
        paintAttributes(0),
        layoutAttributes(0),
138
        textEngine(NULL) {
pixhawk's avatar
pixhawk committed
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
    }

    int renderFlags;
    QString text;
    QFont font;
    QColor color;
    QPen backgroundPen;
    QBrush backgroundBrush;

    int paintAttributes;
    int layoutAttributes;

    const QwtTextEngine *textEngine;
};

class QwtText::LayoutCache
{
public:
157
    void invalidate() {
pixhawk's avatar
pixhawk committed
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
        textSize = QSize();
    }

    QFont font;
    QSize textSize;
};

/*!
   Constructor

   \param text Text content
   \param textFormat Text format
*/
QwtText::QwtText(const QString &text, QwtText::TextFormat textFormat)
{
    d_data = new PrivateData;
    d_data->text = text;
    d_data->textEngine = textEngine(text, textFormat);

    d_layoutCache = new LayoutCache;
}

//! Copy constructor
QwtText::QwtText(const QwtText &other)
{
    d_data = new PrivateData;
    *d_data = *other.d_data;

    d_layoutCache = new LayoutCache;
    *d_layoutCache = *other.d_layoutCache;
}

//! Destructor
191
QwtText::~QwtText()
pixhawk's avatar
pixhawk committed
192 193 194 195 196 197 198 199 200 201 202 203
{
    delete d_data;
    delete d_layoutCache;
}

//! Assignement operator
QwtText &QwtText::operator=(const QwtText &other)
{
    *d_data = *other.d_data;
    *d_layoutCache = *other.d_layoutCache;
    return *this;
}
204

pixhawk's avatar
pixhawk committed
205 206 207
int QwtText::operator==(const QwtText &other) const
{
    return d_data->renderFlags == other.d_data->renderFlags &&
208 209 210 211 212 213 214
           d_data->text == other.d_data->text &&
           d_data->font == other.d_data->font &&
           d_data->color == other.d_data->color &&
           d_data->backgroundPen == other.d_data->backgroundPen &&
           d_data->backgroundBrush == other.d_data->backgroundBrush &&
           d_data->paintAttributes == other.d_data->paintAttributes &&
           d_data->textEngine == other.d_data->textEngine;
pixhawk's avatar
pixhawk committed
215 216 217 218
}

int QwtText::operator!=(const QwtText &other) const // invalidate
{
219
    return !(other == *this);
pixhawk's avatar
pixhawk committed
220 221 222 223 224 225 226 227
}

/*!
   Assign a new text content

   \param text Text content
   \param textFormat Text format
*/
228 229 230 231
void QwtText::setText(const QString &text,
                      QwtText::TextFormat textFormat)
{
    d_data->text = text;
pixhawk's avatar
pixhawk committed
232 233 234 235
    d_data->textEngine = textEngine(text, textFormat);
    d_layoutCache->invalidate();
}

236
/*!
pixhawk's avatar
pixhawk committed
237 238 239
   Return the text.
   \sa setText
*/
240 241 242
QString QwtText::text() const
{
    return d_data->text;
pixhawk's avatar
pixhawk committed
243 244 245 246 247 248 249 250 251 252 253 254
}

/*!
   \brief Change the render flags

   The default setting is Qt::AlignCenter

   \param renderFlags Bitwise OR of the flags used like in QPainter::drawText

   \sa renderFlags, QwtTextEngine::draw
   \note Some renderFlags might have no effect, depending on the text format.
*/
255 256 257 258
void QwtText::setRenderFlags(int renderFlags)
{
    if ( renderFlags != d_data->renderFlags ) {
        d_data->renderFlags = renderFlags;
pixhawk's avatar
pixhawk committed
259 260 261 262 263 264 265 266
        d_layoutCache->invalidate();
    }
}

/*!
   \return Render flags
   \sa setRenderFlags
*/
267 268 269
int QwtText::renderFlags() const
{
    return d_data->renderFlags;
pixhawk's avatar
pixhawk committed
270 271
}

272
/*!
pixhawk's avatar
pixhawk committed
273 274 275 276 277 278
   Set the font.

   \param font Font
   \note Setting the font might have no effect, when
         the text contains control sequences for setting fonts.
*/
279
void QwtText::setFont(const QFont &font)
pixhawk's avatar
pixhawk committed
280
{
281
    d_data->font = font;
pixhawk's avatar
pixhawk committed
282 283 284 285
    setPaintAttribute(PaintUsingTextFont);
}

//! Return the font.
286 287 288
QFont QwtText::font() const
{
    return d_data->font;
pixhawk's avatar
pixhawk committed
289 290 291
}

/*!
292
  Return the font of the text, if it has one.
pixhawk's avatar
pixhawk committed
293 294 295 296 297 298 299 300 301 302 303 304 305
  Otherwise return defaultFont.

  \param defaultFont Default font
  \sa setFont, font, PaintAttributes
*/
QFont QwtText::usedFont(const QFont &defaultFont) const
{
    if ( d_data->paintAttributes & PaintUsingTextFont )
        return d_data->font;

    return defaultFont;
}

306
/*!
pixhawk's avatar
pixhawk committed
307 308 309 310 311 312
   Set the pen color used for painting the text.

   \param color Color
   \note Setting the color might have no effect, when
         the text contains control sequences for setting colors.
*/
313 314 315
void QwtText::setColor(const QColor &color)
{
    d_data->color = color;
pixhawk's avatar
pixhawk committed
316 317 318 319
    setPaintAttribute(PaintUsingTextColor);
}

//! Return the pen color, used for painting the text
320 321 322
QColor QwtText::color() const
{
    return d_data->color;
pixhawk's avatar
pixhawk committed
323 324 325
}

/*!
326
  Return the color of the text, if it has one.
pixhawk's avatar
pixhawk committed
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
  Otherwise return defaultColor.

  \param defaultColor Default color
  \sa setColor, color, PaintAttributes
*/
QColor QwtText::usedColor(const QColor &defaultColor) const
{
    if ( d_data->paintAttributes & PaintUsingTextColor )
        return d_data->color;

    return defaultColor;
}

/*!
   Set the background pen

   \param pen Background pen
   \sa backgroundPen, setBackgroundBrush
*/
346 347 348
void QwtText::setBackgroundPen(const QPen &pen)
{
    d_data->backgroundPen = pen;
pixhawk's avatar
pixhawk committed
349 350 351
    setPaintAttribute(PaintBackground);
}

352
/*!
pixhawk's avatar
pixhawk committed
353 354 355
   \return Background pen
   \sa setBackgroundPen, backgroundBrush
*/
356 357 358
QPen QwtText::backgroundPen() const
{
    return d_data->backgroundPen;
pixhawk's avatar
pixhawk committed
359 360 361 362 363 364 365 366
}

/*!
   Set the background brush

   \param brush Background brush
   \sa backgroundBrush, setBackgroundPen
*/
367 368 369
void QwtText::setBackgroundBrush(const QBrush &brush)
{
    d_data->backgroundBrush = brush;
pixhawk's avatar
pixhawk committed
370 371 372
    setPaintAttribute(PaintBackground);
}

373
/*!
pixhawk's avatar
pixhawk committed
374 375 376
   \return Background brush
   \sa setBackgroundBrush, backgroundPen
*/
377 378 379
QBrush QwtText::backgroundBrush() const
{
    return d_data->backgroundBrush;
pixhawk's avatar
pixhawk committed
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
}

/*!
   Change a paint attribute

   \param attribute Paint attribute
   \param on On/Off

   \note Used by setFont, setColor, setBackgroundPen and setBackgroundBrush
   \sa testPaintAttribute
*/
void QwtText::setPaintAttribute(PaintAttribute attribute, bool on)
{
    if ( on )
        d_data->paintAttributes |= attribute;
    else
        d_data->paintAttributes &= ~attribute;
}

/*!
   Test a paint attribute

   \param attribute Paint attribute
   \return true, if attribute is enabled

   \sa setPaintAttribute
*/
bool QwtText::testPaintAttribute(PaintAttribute attribute) const
{
    return d_data->paintAttributes & attribute;
}

/*!
   Change a layout attribute

   \param attribute Layout attribute
   \param on On/Off
   \sa testLayoutAttribute
418
*/
pixhawk's avatar
pixhawk committed
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
void QwtText::setLayoutAttribute(LayoutAttribute attribute, bool on)
{
    if ( on )
        d_data->layoutAttributes |= attribute;
    else
        d_data->layoutAttributes &= ~attribute;
}

/*!
   Test a layout attribute

   \param attribute Layout attribute
   \return true, if attribute is enabled

   \sa setLayoutAttribute
*/
bool QwtText::testLayoutAttribute(LayoutAttribute attribute) const
{
    return d_data->layoutAttributes | attribute;
}

/*!
   Find the height for a given width

   \param defaultFont Font, used for the calculation if the text has no font
   \param width Width

   \return Calculated height
*/
int QwtText::heightForWidth(int width, const QFont &defaultFont) const
{
    const QwtMetricsMap map = QwtPainter::metricsMap();
    width = map.layoutToScreenX(width);

#if QT_VERSION < 0x040000
    const QFont font = usedFont(defaultFont);
#else
    // We want to calculate in screen metrics. So
    // we need a font that uses screen metrics

    const QFont font(usedFont(defaultFont), QApplication::desktop());
#endif

    int h = 0;

464
    if ( d_data->layoutAttributes & MinimumLayout ) {
pixhawk's avatar
pixhawk committed
465 466
        int left, right, top, bottom;
        d_data->textEngine->textMargins(font, d_data->text,
467
                                        left, right, top, bottom);
pixhawk's avatar
pixhawk committed
468 469

        h = d_data->textEngine->heightForWidth(
470 471
                font, d_data->renderFlags, d_data->text,
                width + left + right);
pixhawk's avatar
pixhawk committed
472 473

        h -= top + bottom;
474
    } else {
pixhawk's avatar
pixhawk committed
475
        h = d_data->textEngine->heightForWidth(
476
                font, d_data->renderFlags, d_data->text, width);
pixhawk's avatar
pixhawk committed
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
    }

    h = map.screenToLayoutY(h);
    return h;
}

/*!
   Find the height for a given width

   \param defaultFont Font, used for the calculation if the text has no font

   \return Calculated height
*/

/*!
   Returns the size, that is needed to render text

   \param defaultFont Font of the text
   \return Caluclated size
*/
QSize QwtText::textSize(const QFont &defaultFont) const
{
#if QT_VERSION < 0x040000
    const QFont font(usedFont(defaultFont));
#else
    // We want to calculate in screen metrics. So
    // we need a font that uses screen metrics

    const QFont font(usedFont(defaultFont), QApplication::desktop());
#endif

508 509
    if ( !d_layoutCache->textSize.isValid()
            || d_layoutCache->font != font ) {
pixhawk's avatar
pixhawk committed
510
        d_layoutCache->textSize = d_data->textEngine->textSize(
511
                                      font, d_data->renderFlags, d_data->text);
pixhawk's avatar
pixhawk committed
512 513 514 515 516 517 518
        d_layoutCache->font = font;
    }

    QSize sz = d_layoutCache->textSize;

    const QwtMetricsMap map = QwtPainter::metricsMap();

519
    if ( d_data->layoutAttributes & MinimumLayout ) {
pixhawk's avatar
pixhawk committed
520 521
        int left, right, top, bottom;
        d_data->textEngine->textMargins(font, d_data->text,
522
                                        left, right, top, bottom);
pixhawk's avatar
pixhawk committed
523 524
        sz -= QSize(left + right, top + bottom);
#if QT_VERSION >= 0x040000
525
        if ( !map.isIdentity() ) {
pixhawk's avatar
pixhawk committed
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
#ifdef __GNUC__
#endif
            /*
                When printing in high resolution, the tick labels
                of are cut of. We need to find out why, but for
                the moment we add a couple of pixels instead.
             */
            sz += QSize(3, 0);
        }
#endif
    }

    sz = map.screenToLayout(sz);
    return sz;
}

/*!
   Draw a text into a rectangle

   \param painter Painter
   \param rect Rectangle
*/
void QwtText::draw(QPainter *painter, const QRect &rect) const
{
550 551 552
    if ( d_data->paintAttributes & PaintBackground ) {
        if ( d_data->backgroundPen != Qt::NoPen ||
                d_data->backgroundBrush != Qt::NoBrush ) {
pixhawk's avatar
pixhawk committed
553 554 555 556 557 558
            painter->save();
            painter->setPen(d_data->backgroundPen);
            painter->setBrush(d_data->backgroundBrush);
#if QT_VERSION < 0x040000
            QwtPainter::drawRect(painter, rect);
#else
559 560
            const QRect r(rect.x(), rect.y(),
                          rect.width() - 1, rect.height() - 1);
pixhawk's avatar
pixhawk committed
561 562 563 564 565 566 567 568
            QwtPainter::drawRect(painter, r);
#endif
            painter->restore();
        }
    }

    painter->save();

569
    if ( d_data->paintAttributes & PaintUsingTextFont ) {
pixhawk's avatar
pixhawk committed
570 571 572
        painter->setFont(d_data->font);
    }

573
    if ( d_data->paintAttributes & PaintUsingTextColor ) {
pixhawk's avatar
pixhawk committed
574 575 576 577 578
        if ( d_data->color.isValid() )
            painter->setPen(d_data->color);
    }

    QRect expandedRect = rect;
579
    if ( d_data->layoutAttributes & MinimumLayout ) {
pixhawk's avatar
pixhawk committed
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
#if QT_VERSION < 0x040000
        const QFont font(painter->font());
#else
        // We want to calculate in screen metrics. So
        // we need a font that uses screen metrics

        const QFont font(painter->font(), QApplication::desktop());
#endif

        int left, right, top, bottom;
        d_data->textEngine->textMargins(
            font, d_data->text,
            left, right, top, bottom);

        const QwtMetricsMap map = QwtPainter::metricsMap();
        left = map.screenToLayoutX(left);
        right = map.screenToLayoutX(right);
        top = map.screenToLayoutY(top);
        bottom = map.screenToLayoutY(bottom);

        expandedRect.setTop(rect.top() - top);
        expandedRect.setBottom(rect.bottom() + bottom);
        expandedRect.setLeft(rect.left() - left);
        expandedRect.setRight(rect.right() + right);
    }

606 607
    d_data->textEngine->draw(painter, expandedRect,
                             d_data->renderFlags, d_data->text);
pixhawk's avatar
pixhawk committed
608 609 610 611 612 613 614

    painter->restore();
}

/*!
   Find the text engine for a text format

615
   In case of QwtText::AutoText the first text engine
pixhawk's avatar
pixhawk committed
616 617 618
   (beside QwtPlainTextEngine) is returned, where QwtTextEngine::mightRender
   returns true. If there is none QwtPlainTextEngine is returnd.

619
   If no text engine is registered for the format QwtPlainTextEngine
pixhawk's avatar
pixhawk committed
620 621 622 623 624 625
   is returnd.

   \param text Text, needed in case of AutoText
   \param format Text format
*/
const QwtTextEngine *QwtText::textEngine(const QString &text,
626
        QwtText::TextFormat format)
pixhawk's avatar
pixhawk committed
627
{
628
    if ( engineDict == NULL ) {
pixhawk's avatar
pixhawk committed
629
        /*
630
          Note: engineDict is allocated, the first time it is used,
pixhawk's avatar
pixhawk committed
631 632 633 634 635 636 637 638 639 640 641 642 643 644
                but never deleted, because there is no known last access time.
                So don't be irritated, if it is reported as a memory leak
                from your memory profiler.
         */
        engineDict = new QwtTextEngineDict();
    }

    return engineDict->textEngine(text, format);
}

/*!
   Assign/Replace a text engine for a text format

   With setTextEngine it is possible to extend Qwt with
645
   other types of text formats.
pixhawk's avatar
pixhawk committed
646

647 648
   Owner of a commercial Qt license can build the qwtmathml library,
   that is based on the MathML renderer, that is included in MML Widget
pixhawk's avatar
pixhawk committed
649 650 651
   component of the Qt solutions package.

   For QwtText::PlainText it is not allowed to assign a engine == NULL.
652

pixhawk's avatar
pixhawk committed
653 654 655 656 657 658
   \param format Text format
   \param engine Text engine

   \sa QwtMathMLTextEngine
   \warning Using QwtText::AutoText does nothing.
*/
659 660
void QwtText::setTextEngine(QwtText::TextFormat format,
                            QwtTextEngine *engine)
pixhawk's avatar
pixhawk committed
661 662 663 664 665 666 667 668 669 670
{
    if ( engineDict == NULL )
        engineDict = new QwtTextEngineDict();

    engineDict->setTextEngine(format, engine);
}

/*!
   \brief Find the text engine for a text format

671 672 673
   textEngine can be used to find out if a text format is supported.
   F.e, if one wants to use MathML labels, the MathML renderer from the
   commercial Qt solutions package might be required, that is not
pixhawk's avatar
pixhawk committed
674 675 676 677 678 679 680 681 682 683 684 685
   available in Qt Open Source Edition environments.

   \param format Text format
   \return The text engine, or NULL if no engine is available.
*/
const QwtTextEngine *QwtText::textEngine(QwtText::TextFormat format)
{
    if ( engineDict == NULL )
        engineDict = new QwtTextEngineDict();

    return engineDict->textEngine(format);
}