1 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
2  * Qwt Widget Library
3  * Copyright (C) 1997   Josef Wilgen
4  * Copyright (C) 2002   Uwe Rathmann
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the Qwt License, Version 1.0
8  *****************************************************************************/
9 
10 #include "qwt_text.h"
11 #include "qwt_painter.h"
12 #include "qwt_text_engine.h"
13 #include <qmap.h>
14 #include <qfont.h>
15 #include <qcolor.h>
16 #include <qpen.h>
17 #include <qbrush.h>
18 #include <qpainter.h>
19 #include <qapplication.h>
20 #include <qdesktopwidget.h>
21 #include <qmath.h>
22 
23 class QwtTextEngineDict
24 {
25 public:
26     static QwtTextEngineDict &dict();
27 
28     void setTextEngine( QwtText::TextFormat, QwtTextEngine * );
29 
30     const QwtTextEngine *textEngine( QwtText::TextFormat ) const;
31     const QwtTextEngine *textEngine( const QString &,
32         QwtText::TextFormat ) const;
33 
34 private:
35     QwtTextEngineDict();
36     ~QwtTextEngineDict();
37 
38     typedef QMap<int, QwtTextEngine *> EngineMap;
39 
engine(EngineMap::const_iterator & it) const40     inline const QwtTextEngine *engine( EngineMap::const_iterator &it ) const
41     {
42         return it.value();
43     }
44 
45     EngineMap d_map;
46 };
47 
dict()48 QwtTextEngineDict &QwtTextEngineDict::dict()
49 {
50     static QwtTextEngineDict engineDict;
51     return engineDict;
52 }
53 
QwtTextEngineDict()54 QwtTextEngineDict::QwtTextEngineDict()
55 {
56     d_map.insert( QwtText::PlainText, new QwtPlainTextEngine() );
57 #ifndef QT_NO_RICHTEXT
58     d_map.insert( QwtText::RichText, new QwtRichTextEngine() );
59 #endif
60 }
61 
~QwtTextEngineDict()62 QwtTextEngineDict::~QwtTextEngineDict()
63 {
64     for ( EngineMap::const_iterator it = d_map.constBegin();
65         it != d_map.constEnd(); ++it )
66     {
67         const QwtTextEngine *textEngine = engine( it );
68         delete textEngine;
69     }
70 }
71 
textEngine(const QString & text,QwtText::TextFormat format) const72 const QwtTextEngine *QwtTextEngineDict::textEngine( const QString& text,
73     QwtText::TextFormat format ) const
74 {
75     if ( format == QwtText::AutoText )
76     {
77         for ( EngineMap::const_iterator it = d_map.begin();
78             it != d_map.end(); ++it )
79         {
80             if ( it.key() != QwtText::PlainText )
81             {
82                 const QwtTextEngine *e = engine( it );
83                 if ( e && e->mightRender( text ) )
84                     return e;
85             }
86         }
87     }
88 
89     EngineMap::const_iterator it = d_map.find( format );
90     if ( it != d_map.end() )
91     {
92         const QwtTextEngine *e = engine( it );
93         if ( e )
94             return e;
95     }
96 
97     it = d_map.find( QwtText::PlainText );
98     return engine( it );
99 }
100 
setTextEngine(QwtText::TextFormat format,QwtTextEngine * engine)101 void QwtTextEngineDict::setTextEngine( QwtText::TextFormat format,
102     QwtTextEngine *engine )
103 {
104     if ( format == QwtText::AutoText )
105         return;
106 
107     if ( format == QwtText::PlainText && engine == NULL )
108         return;
109 
110     EngineMap::const_iterator it = d_map.constFind( format );
111     if ( it != d_map.constEnd() )
112     {
113         delete this->engine( it );
114         d_map.remove( format );
115     }
116 
117     if ( engine != NULL )
118         d_map.insert( format, engine );
119 }
120 
textEngine(QwtText::TextFormat format) const121 const QwtTextEngine *QwtTextEngineDict::textEngine(
122     QwtText::TextFormat format ) const
123 {
124     const QwtTextEngine *e = NULL;
125 
126     EngineMap::const_iterator it = d_map.find( format );
127     if ( it != d_map.end() )
128         e = engine( it );
129 
130     return e;
131 }
132 
133 class QwtText::PrivateData
134 {
135 public:
PrivateData()136     PrivateData():
137         renderFlags( Qt::AlignCenter ),
138         borderRadius( 0 ),
139         borderPen( Qt::NoPen ),
140         backgroundBrush( Qt::NoBrush ),
141         paintAttributes( 0 ),
142         layoutAttributes( 0 ),
143         textEngine( NULL )
144     {
145     }
146 
147     int renderFlags;
148     QString text;
149     QFont font;
150     QColor color;
151     double borderRadius;
152     QPen borderPen;
153     QBrush backgroundBrush;
154 
155     QwtText::PaintAttributes paintAttributes;
156     QwtText::LayoutAttributes layoutAttributes;
157 
158     const QwtTextEngine *textEngine;
159 };
160 
161 class QwtText::LayoutCache
162 {
163 public:
invalidate()164     void invalidate()
165     {
166         textSize = QSizeF();
167     }
168 
169     QFont font;
170     QSizeF textSize;
171 };
172 
173 /*!
174    Constructor
175 
176    \param text Text content
177    \param textFormat Text format
178 */
QwtText(const QString & text,QwtText::TextFormat textFormat)179 QwtText::QwtText( const QString &text, QwtText::TextFormat textFormat )
180 {
181     d_data = new PrivateData;
182     d_data->text = text;
183     d_data->textEngine = textEngine( text, textFormat );
184 
185     d_layoutCache = new LayoutCache;
186 }
187 
188 //! Copy constructor
QwtText(const QwtText & other)189 QwtText::QwtText( const QwtText &other )
190 {
191     d_data = new PrivateData;
192     *d_data = *other.d_data;
193 
194     d_layoutCache = new LayoutCache;
195     *d_layoutCache = *other.d_layoutCache;
196 }
197 
198 //! Destructor
~QwtText()199 QwtText::~QwtText()
200 {
201     delete d_data;
202     delete d_layoutCache;
203 }
204 
205 //! Assignment operator
operator =(const QwtText & other)206 QwtText &QwtText::operator=( const QwtText & other )
207 {
208     *d_data = *other.d_data;
209     *d_layoutCache = *other.d_layoutCache;
210     return *this;
211 }
212 
213 //! Relational operator
operator ==(const QwtText & other) const214 bool QwtText::operator==( const QwtText &other ) const
215 {
216     return d_data->renderFlags == other.d_data->renderFlags &&
217         d_data->text == other.d_data->text &&
218         d_data->font == other.d_data->font &&
219         d_data->color == other.d_data->color &&
220         d_data->borderRadius == other.d_data->borderRadius &&
221         d_data->borderPen == other.d_data->borderPen &&
222         d_data->backgroundBrush == other.d_data->backgroundBrush &&
223         d_data->paintAttributes == other.d_data->paintAttributes &&
224         d_data->textEngine == other.d_data->textEngine;
225 }
226 
227 //! Relational operator
operator !=(const QwtText & other) const228 bool QwtText::operator!=( const QwtText &other ) const // invalidate
229 {
230     return !( other == *this );
231 }
232 
233 /*!
234    Assign a new text content
235 
236    \param text Text content
237    \param textFormat Text format
238 
239    \sa text()
240 */
setText(const QString & text,QwtText::TextFormat textFormat)241 void QwtText::setText( const QString &text,
242     QwtText::TextFormat textFormat )
243 {
244     d_data->text = text;
245     d_data->textEngine = textEngine( text, textFormat );
246     d_layoutCache->invalidate();
247 }
248 
249 /*!
250    \return Text as QString.
251    \sa setText()
252 */
text() const253 QString QwtText::text() const
254 {
255     return d_data->text;
256 }
257 
258 /*!
259    \brief Change the render flags
260 
261    The default setting is Qt::AlignCenter
262 
263    \param renderFlags Bitwise OR of the flags used like in QPainter::drawText()
264 
265    \sa renderFlags(), QwtTextEngine::draw()
266    \note Some renderFlags might have no effect, depending on the text format.
267 */
setRenderFlags(int renderFlags)268 void QwtText::setRenderFlags( int renderFlags )
269 {
270     if ( renderFlags != d_data->renderFlags )
271     {
272         d_data->renderFlags = renderFlags;
273         d_layoutCache->invalidate();
274     }
275 }
276 
277 /*!
278    \return Render flags
279    \sa setRenderFlags()
280 */
renderFlags() const281 int QwtText::renderFlags() const
282 {
283     return d_data->renderFlags;
284 }
285 
286 /*!
287    Set the font.
288 
289    \param font Font
290    \note Setting the font might have no effect, when
291          the text contains control sequences for setting fonts.
292 */
setFont(const QFont & font)293 void QwtText::setFont( const QFont &font )
294 {
295     d_data->font = font;
296     setPaintAttribute( PaintUsingTextFont );
297 }
298 
299 //! Return the font.
font() const300 QFont QwtText::font() const
301 {
302     return d_data->font;
303 }
304 
305 /*!
306    Return the font of the text, if it has one.
307    Otherwise return defaultFont.
308 
309    \param defaultFont Default font
310    \return Font used for drawing the text
311 
312    \sa setFont(), font(), PaintAttributes
313 */
usedFont(const QFont & defaultFont) const314 QFont QwtText::usedFont( const QFont &defaultFont ) const
315 {
316     if ( d_data->paintAttributes & PaintUsingTextFont )
317         return d_data->font;
318 
319     return defaultFont;
320 }
321 
322 /*!
323    Set the pen color used for drawing the text.
324 
325    \param color Color
326    \note Setting the color might have no effect, when
327          the text contains control sequences for setting colors.
328 */
setColor(const QColor & color)329 void QwtText::setColor( const QColor &color )
330 {
331     d_data->color = color;
332     setPaintAttribute( PaintUsingTextColor );
333 }
334 
335 //! Return the pen color, used for painting the text
color() const336 QColor QwtText::color() const
337 {
338     return d_data->color;
339 }
340 
341 /*!
342   Return the color of the text, if it has one.
343   Otherwise return defaultColor.
344 
345   \param defaultColor Default color
346   \return Color used for drawing the text
347 
348   \sa setColor(), color(), PaintAttributes
349 */
usedColor(const QColor & defaultColor) const350 QColor QwtText::usedColor( const QColor &defaultColor ) const
351 {
352     if ( d_data->paintAttributes & PaintUsingTextColor )
353         return d_data->color;
354 
355     return defaultColor;
356 }
357 
358 /*!
359   Set the radius for the corners of the border frame
360 
361   \param radius Radius of a rounded corner
362   \sa borderRadius(), setBorderPen(), setBackgroundBrush()
363 */
setBorderRadius(double radius)364 void QwtText::setBorderRadius( double radius )
365 {
366     d_data->borderRadius = qMax( 0.0, radius );
367 }
368 
369 /*!
370   \return Radius for the corners of the border frame
371   \sa setBorderRadius(), borderPen(), backgroundBrush()
372 */
borderRadius() const373 double QwtText::borderRadius() const
374 {
375     return d_data->borderRadius;
376 }
377 
378 /*!
379    Set the background pen
380 
381    \param pen Background pen
382    \sa borderPen(), setBackgroundBrush()
383 */
setBorderPen(const QPen & pen)384 void QwtText::setBorderPen( const QPen &pen )
385 {
386     d_data->borderPen = pen;
387     setPaintAttribute( PaintBackground );
388 }
389 
390 /*!
391    \return Background pen
392    \sa setBorderPen(), backgroundBrush()
393 */
borderPen() const394 QPen QwtText::borderPen() const
395 {
396     return d_data->borderPen;
397 }
398 
399 /*!
400    Set the background brush
401 
402    \param brush Background brush
403    \sa backgroundBrush(), setBorderPen()
404 */
setBackgroundBrush(const QBrush & brush)405 void QwtText::setBackgroundBrush( const QBrush &brush )
406 {
407     d_data->backgroundBrush = brush;
408     setPaintAttribute( PaintBackground );
409 }
410 
411 /*!
412    \return Background brush
413    \sa setBackgroundBrush(), borderPen()
414 */
backgroundBrush() const415 QBrush QwtText::backgroundBrush() const
416 {
417     return d_data->backgroundBrush;
418 }
419 
420 /*!
421    Change a paint attribute
422 
423    \param attribute Paint attribute
424    \param on On/Off
425 
426    \note Used by setFont(), setColor(),
427          setBorderPen() and setBackgroundBrush()
428    \sa testPaintAttribute()
429 */
setPaintAttribute(PaintAttribute attribute,bool on)430 void QwtText::setPaintAttribute( PaintAttribute attribute, bool on )
431 {
432     if ( on )
433         d_data->paintAttributes |= attribute;
434     else
435         d_data->paintAttributes &= ~attribute;
436 }
437 
438 /*!
439    Test a paint attribute
440 
441    \param attribute Paint attribute
442    \return true, if attribute is enabled
443 
444    \sa setPaintAttribute()
445 */
testPaintAttribute(PaintAttribute attribute) const446 bool QwtText::testPaintAttribute( PaintAttribute attribute ) const
447 {
448     return d_data->paintAttributes & attribute;
449 }
450 
451 /*!
452    Change a layout attribute
453 
454    \param attribute Layout attribute
455    \param on On/Off
456    \sa testLayoutAttribute()
457 */
setLayoutAttribute(LayoutAttribute attribute,bool on)458 void QwtText::setLayoutAttribute( LayoutAttribute attribute, bool on )
459 {
460     if ( on )
461         d_data->layoutAttributes |= attribute;
462     else
463         d_data->layoutAttributes &= ~attribute;
464 }
465 
466 /*!
467    Test a layout attribute
468 
469    \param attribute Layout attribute
470    \return true, if attribute is enabled
471 
472    \sa setLayoutAttribute()
473 */
testLayoutAttribute(LayoutAttribute attribute) const474 bool QwtText::testLayoutAttribute( LayoutAttribute attribute ) const
475 {
476     return d_data->layoutAttributes | attribute;
477 }
478 
479 /*!
480    Find the height for a given width
481 
482    \param defaultFont Font, used for the calculation if the text has no font
483    \param width Width
484 
485    \return Calculated height
486 */
heightForWidth(double width,const QFont & defaultFont) const487 double QwtText::heightForWidth( double width, const QFont &defaultFont ) const
488 {
489     // We want to calculate in screen metrics. So
490     // we need a font that uses screen metrics
491 
492     const QFont font( usedFont( defaultFont ), QApplication::desktop() );
493 
494     double h = 0;
495 
496     if ( d_data->layoutAttributes & MinimumLayout )
497     {
498         double left, right, top, bottom;
499         d_data->textEngine->textMargins( font, d_data->text,
500             left, right, top, bottom );
501 
502         h = d_data->textEngine->heightForWidth(
503             font, d_data->renderFlags, d_data->text,
504             width + left + right );
505 
506         h -= top + bottom;
507     }
508     else
509     {
510         h = d_data->textEngine->heightForWidth(
511             font, d_data->renderFlags, d_data->text, width );
512     }
513 
514     return h;
515 }
516 
517 /*!
518    Returns the size, that is needed to render text
519 
520    \param defaultFont Font of the text
521    \return Calculated size
522 */
textSize(const QFont & defaultFont) const523 QSizeF QwtText::textSize( const QFont &defaultFont ) const
524 {
525     // We want to calculate in screen metrics. So
526     // we need a font that uses screen metrics
527 
528     const QFont font( usedFont( defaultFont ), QApplication::desktop() );
529 
530     if ( !d_layoutCache->textSize.isValid()
531         || d_layoutCache->font != font )
532     {
533         d_layoutCache->textSize = d_data->textEngine->textSize(
534             font, d_data->renderFlags, d_data->text );
535         d_layoutCache->font = font;
536     }
537 
538     QSizeF sz = d_layoutCache->textSize;
539 
540     if ( d_data->layoutAttributes & MinimumLayout )
541     {
542         double left, right, top, bottom;
543         d_data->textEngine->textMargins( font, d_data->text,
544             left, right, top, bottom );
545         sz -= QSizeF( left + right, top + bottom );
546     }
547 
548     return sz;
549 }
550 
551 /*!
552    Draw a text into a rectangle
553 
554    \param painter Painter
555    \param rect Rectangle
556 */
draw(QPainter * painter,const QRectF & rect) const557 void QwtText::draw( QPainter *painter, const QRectF &rect ) const
558 {
559     if ( d_data->paintAttributes & PaintBackground )
560     {
561         if ( d_data->borderPen != Qt::NoPen ||
562             d_data->backgroundBrush != Qt::NoBrush )
563         {
564             painter->save();
565 
566             painter->setPen( d_data->borderPen );
567             painter->setBrush( d_data->backgroundBrush );
568 
569             if ( d_data->borderRadius == 0 )
570             {
571                 QwtPainter::drawRect( painter, rect );
572             }
573             else
574             {
575                 painter->setRenderHint( QPainter::Antialiasing, true );
576                 painter->drawRoundedRect( rect,
577                     d_data->borderRadius, d_data->borderRadius );
578             }
579 
580             painter->restore();
581         }
582     }
583 
584     painter->save();
585 
586     if ( d_data->paintAttributes & PaintUsingTextFont )
587     {
588         painter->setFont( d_data->font );
589     }
590 
591     if ( d_data->paintAttributes & PaintUsingTextColor )
592     {
593         if ( d_data->color.isValid() )
594             painter->setPen( d_data->color );
595     }
596 
597     QRectF expandedRect = rect;
598     if ( d_data->layoutAttributes & MinimumLayout )
599     {
600         // We want to calculate in screen metrics. So
601         // we need a font that uses screen metrics
602 
603         const QFont font( painter->font(), QApplication::desktop() );
604 
605         double left, right, top, bottom;
606         d_data->textEngine->textMargins(
607             font, d_data->text, left, right, top, bottom );
608 
609         expandedRect.setTop( rect.top() - top );
610         expandedRect.setBottom( rect.bottom() + bottom );
611         expandedRect.setLeft( rect.left() - left );
612         expandedRect.setRight( rect.right() + right );
613     }
614 
615     d_data->textEngine->draw( painter, expandedRect,
616         d_data->renderFlags, d_data->text );
617 
618     painter->restore();
619 }
620 
621 /*!
622    Find the text engine for a text format
623 
624    In case of QwtText::AutoText the first text engine
625    (beside QwtPlainTextEngine) is returned, where QwtTextEngine::mightRender
626    returns true. If there is none QwtPlainTextEngine is returned.
627 
628    If no text engine is registered for the format QwtPlainTextEngine
629    is returnd.
630 
631    \param text Text, needed in case of AutoText
632    \param format Text format
633 
634    \return Corresponding text engine
635 */
textEngine(const QString & text,QwtText::TextFormat format)636 const QwtTextEngine *QwtText::textEngine( const QString &text,
637     QwtText::TextFormat format )
638 {
639     return QwtTextEngineDict::dict().textEngine( text, format );
640 }
641 
642 /*!
643    Assign/Replace a text engine for a text format
644 
645    With setTextEngine it is possible to extend Qwt with
646    other types of text formats.
647 
648    For QwtText::PlainText it is not allowed to assign a engine == NULL.
649 
650    \param format Text format
651    \param engine Text engine
652 
653    \sa QwtMathMLTextEngine
654    \warning Using QwtText::AutoText does nothing.
655 */
setTextEngine(QwtText::TextFormat format,QwtTextEngine * engine)656 void QwtText::setTextEngine( QwtText::TextFormat format,
657     QwtTextEngine *engine )
658 {
659     QwtTextEngineDict::dict().setTextEngine( format, engine );
660 }
661 
662 /*!
663    \brief Find the text engine for a text format
664 
665    textEngine can be used to find out if a text format is supported.
666 
667    \param format Text format
668    \return The text engine, or NULL if no engine is available.
669 */
textEngine(QwtText::TextFormat format)670 const QwtTextEngine *QwtText::textEngine( QwtText::TextFormat format )
671 {
672     return  QwtTextEngineDict::dict().textEngine( format );
673 }
674