1 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
2  * Qwt Widget Library
3  * Copyright (C) 1997   Josef Wilgen
4  * Copyright (C) 2003   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 // vim: expandtab
11 
12 #include <qmap.h>
13 #include <qfont.h>
14 #include <qcolor.h>
15 #include <qpen.h>
16 #include <qbrush.h>
17 #include <qpainter.h>
18 #include "qwt_painter.h"
19 #include "qwt_text_engine.h"
20 #include "qwt_text.h"
21 #if QT_VERSION >= 0x040000
22 #include <qapplication.h>
23 #include <qdesktopwidget.h>
24 #endif
25 
26 class QwtTextEngineDict
27 {
28 public:
29     QwtTextEngineDict();
30     ~QwtTextEngineDict();
31 
32     void setTextEngine(QwtText::TextFormat, QwtTextEngine *);
33     const QwtTextEngine *textEngine(QwtText::TextFormat) const;
34     const QwtTextEngine *textEngine(const QString &,
35         QwtText::TextFormat) const;
36 
37 private:
38     typedef QMap<int, QwtTextEngine *> EngineMap;
39 
engine(EngineMap::const_iterator & it) const40     inline const QwtTextEngine *engine(EngineMap::const_iterator &it) const
41     {
42 #if QT_VERSION < 0x040000
43         return it.data();
44 #else
45         return it.value();
46 #endif
47     }
48 
49     EngineMap d_map;
50 };
51 
QwtTextEngineDict()52 QwtTextEngineDict::QwtTextEngineDict()
53 {
54     d_map.insert(QwtText::PlainText, new QwtPlainTextEngine());
55 #ifndef QT_NO_RICHTEXT
56     d_map.insert(QwtText::RichText, new QwtRichTextEngine());
57 #endif
58 }
59 
~QwtTextEngineDict()60 QwtTextEngineDict::~QwtTextEngineDict()
61 {
62     for ( EngineMap::const_iterator it = d_map.begin();
63         it != d_map.end(); ++it )
64     {
65         QwtTextEngine *textEngine = (QwtTextEngine *)engine(it);
66         delete textEngine;
67     }
68 }
69 
textEngine(const QString & text,QwtText::TextFormat format) const70 const QwtTextEngine *QwtTextEngineDict::textEngine(const QString& text,
71     QwtText::TextFormat format) const
72 {
73     if ( format == QwtText::AutoText )
74     {
75         for ( EngineMap::const_iterator it = d_map.begin();
76             it != d_map.end(); ++it )
77         {
78             if ( it.key() != QwtText::PlainText )
79             {
80                 const QwtTextEngine *e = engine(it);
81                 if ( e && e->mightRender(text) )
82                     return (QwtTextEngine *)e;
83             }
84         }
85     }
86 
87     EngineMap::const_iterator it = d_map.find(format);
88     if ( it != d_map.end() )
89     {
90         const QwtTextEngine *e = engine(it);
91         if ( e )
92             return e;
93     }
94 
95     it = d_map.find(QwtText::PlainText);
96     return engine(it);
97 }
98 
setTextEngine(QwtText::TextFormat format,QwtTextEngine * engine)99 void QwtTextEngineDict::setTextEngine(QwtText::TextFormat format,
100     QwtTextEngine *engine)
101 {
102     if ( format == QwtText::AutoText )
103         return;
104 
105     if ( format == QwtText::PlainText && engine == NULL )
106         return;
107 
108     EngineMap::const_iterator it = d_map.find(format);
109     if ( it != d_map.end() )
110     {
111         const QwtTextEngine *e = this->engine(it);
112         if ( e )
113             delete e;
114 
115         d_map.remove(format);
116     }
117 
118     if ( engine != NULL )
119         d_map.insert(format, engine);
120 }
121 
textEngine(QwtText::TextFormat format) const122 const QwtTextEngine *QwtTextEngineDict::textEngine(
123     QwtText::TextFormat format) const
124 {
125     const QwtTextEngine *e = NULL;
126 
127     EngineMap::const_iterator it = d_map.find(format);
128     if ( it != d_map.end() )
129         e = engine(it);
130 
131     return e;
132 }
133 
134 static QwtTextEngineDict *engineDict = NULL;
135 
136 class QwtText::PrivateData
137 {
138 public:
PrivateData()139     PrivateData():
140         renderFlags(Qt::AlignCenter),
141         backgroundPen(Qt::NoPen),
142         backgroundBrush(Qt::NoBrush),
143         paintAttributes(0),
144         layoutAttributes(0),
145         textEngine(NULL)
146     {
147     }
148 
149     int renderFlags;
150     QString text;
151     QFont font;
152     QColor color;
153     QPen backgroundPen;
154     QBrush backgroundBrush;
155 
156     int paintAttributes;
157     int layoutAttributes;
158 
159     const QwtTextEngine *textEngine;
160 };
161 
162 class QwtText::LayoutCache
163 {
164 public:
invalidate()165     void invalidate()
166     {
167         textSize = QSize();
168     }
169 
170     QFont font;
171     QSize textSize;
172 };
173 
174 /*!
175    Constructor
176 
177    \param text Text content
178    \param textFormat Text format
179 */
QwtText(const QString & text,QwtText::TextFormat textFormat)180 QwtText::QwtText(const QString &text, QwtText::TextFormat textFormat)
181 {
182     d_data = new PrivateData;
183     d_data->text = text;
184     d_data->textEngine = textEngine(text, textFormat);
185 
186     d_layoutCache = new LayoutCache;
187 }
188 
189 //! Copy constructor
QwtText(const QwtText & other)190 QwtText::QwtText(const QwtText &other)
191 {
192     d_data = new PrivateData;
193     *d_data = *other.d_data;
194 
195     d_layoutCache = new LayoutCache;
196     *d_layoutCache = *other.d_layoutCache;
197 }
198 
199 //! Destructor
~QwtText()200 QwtText::~QwtText()
201 {
202     delete d_data;
203     delete d_layoutCache;
204 }
205 
206 //! Assignment operator
operator =(const QwtText & other)207 QwtText &QwtText::operator=(const QwtText &other)
208 {
209     *d_data = *other.d_data;
210     *d_layoutCache = *other.d_layoutCache;
211     return *this;
212 }
213 
214 //! Relational operator
operator ==(const QwtText & other) const215 int QwtText::operator==(const QwtText &other) const
216 {
217     return d_data->renderFlags == other.d_data->renderFlags &&
218         d_data->text == other.d_data->text &&
219         d_data->font == other.d_data->font &&
220         d_data->color == other.d_data->color &&
221         d_data->backgroundPen == other.d_data->backgroundPen &&
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 int 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 the text.
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   \sa setFont(), font(), PaintAttributes
311 */
usedFont(const QFont & defaultFont) const312 QFont QwtText::usedFont(const QFont &defaultFont) const
313 {
314     if ( d_data->paintAttributes & PaintUsingTextFont )
315         return d_data->font;
316 
317     return defaultFont;
318 }
319 
320 /*!
321    Set the pen color used for painting the text.
322 
323    \param color Color
324    \note Setting the color might have no effect, when
325          the text contains control sequences for setting colors.
326 */
setColor(const QColor & color)327 void QwtText::setColor(const QColor &color)
328 {
329     d_data->color = color;
330     setPaintAttribute(PaintUsingTextColor);
331 }
332 
333 //! Return the pen color, used for painting the text
color() const334 QColor QwtText::color() const
335 {
336     return d_data->color;
337 }
338 
339 /*!
340   Return the color of the text, if it has one.
341   Otherwise return defaultColor.
342 
343   \param defaultColor Default color
344   \sa setColor(), color(), PaintAttributes
345 */
usedColor(const QColor & defaultColor) const346 QColor QwtText::usedColor(const QColor &defaultColor) const
347 {
348     if ( d_data->paintAttributes & PaintUsingTextColor )
349         return d_data->color;
350 
351     return defaultColor;
352 }
353 
354 /*!
355    Set the background pen
356 
357    \param pen Background pen
358    \sa backgroundPen(), setBackgroundBrush()
359 */
setBackgroundPen(const QPen & pen)360 void QwtText::setBackgroundPen(const QPen &pen)
361 {
362     d_data->backgroundPen = pen;
363     setPaintAttribute(PaintBackground);
364 }
365 
366 /*!
367    \return Background pen
368    \sa setBackgroundPen(), backgroundBrush()
369 */
backgroundPen() const370 QPen QwtText::backgroundPen() const
371 {
372     return d_data->backgroundPen;
373 }
374 
375 /*!
376    Set the background brush
377 
378    \param brush Background brush
379    \sa backgroundBrush(), setBackgroundPen()
380 */
setBackgroundBrush(const QBrush & brush)381 void QwtText::setBackgroundBrush(const QBrush &brush)
382 {
383     d_data->backgroundBrush = brush;
384     setPaintAttribute(PaintBackground);
385 }
386 
387 /*!
388    \return Background brush
389    \sa setBackgroundBrush(), backgroundPen()
390 */
backgroundBrush() const391 QBrush QwtText::backgroundBrush() const
392 {
393     return d_data->backgroundBrush;
394 }
395 
396 /*!
397    Change a paint attribute
398 
399    \param attribute Paint attribute
400    \param on On/Off
401 
402    \note Used by setFont(), setColor(),
403          setBackgroundPen() and setBackgroundBrush()
404    \sa testPaintAttribute()
405 */
setPaintAttribute(PaintAttribute attribute,bool on)406 void QwtText::setPaintAttribute(PaintAttribute attribute, bool on)
407 {
408     if ( on )
409         d_data->paintAttributes |= attribute;
410     else
411         d_data->paintAttributes &= ~attribute;
412 }
413 
414 /*!
415    Test a paint attribute
416 
417    \param attribute Paint attribute
418    \return true, if attribute is enabled
419 
420    \sa setPaintAttribute()
421 */
testPaintAttribute(PaintAttribute attribute) const422 bool QwtText::testPaintAttribute(PaintAttribute attribute) const
423 {
424     return d_data->paintAttributes & attribute;
425 }
426 
427 /*!
428    Change a layout attribute
429 
430    \param attribute Layout attribute
431    \param on On/Off
432    \sa testLayoutAttribute()
433 */
setLayoutAttribute(LayoutAttribute attribute,bool on)434 void QwtText::setLayoutAttribute(LayoutAttribute attribute, bool on)
435 {
436     if ( on )
437         d_data->layoutAttributes |= attribute;
438     else
439         d_data->layoutAttributes &= ~attribute;
440 }
441 
442 /*!
443    Test a layout attribute
444 
445    \param attribute Layout attribute
446    \return true, if attribute is enabled
447 
448    \sa setLayoutAttribute()
449 */
testLayoutAttribute(LayoutAttribute attribute) const450 bool QwtText::testLayoutAttribute(LayoutAttribute attribute) const
451 {
452     return d_data->layoutAttributes | attribute;
453 }
454 
455 /*!
456    Find the height for a given width
457 
458    \param defaultFont Font, used for the calculation if the text has no font
459    \param width Width
460 
461    \return Calculated height
462 */
heightForWidth(int width,const QFont & defaultFont) const463 int QwtText::heightForWidth(int width, const QFont &defaultFont) const
464 {
465     const QwtMetricsMap map = QwtPainter::metricsMap();
466     width = map.layoutToScreenX(width);
467 
468 #if QT_VERSION < 0x040000
469     const QFont font = usedFont(defaultFont);
470 #else
471     // We want to calculate in screen metrics. So
472     // we need a font that uses screen metrics
473 
474     const QFont font(usedFont(defaultFont), QApplication::desktop());
475 #endif
476 
477     int h = 0;
478 
479     if ( d_data->layoutAttributes & MinimumLayout )
480     {
481         int left, right, top, bottom;
482         d_data->textEngine->textMargins(font, d_data->text,
483             left, right, top, bottom);
484 
485         h = d_data->textEngine->heightForWidth(
486             font, d_data->renderFlags, d_data->text,
487             width + left + right);
488 
489         h -= top + bottom;
490     }
491     else
492     {
493         h = d_data->textEngine->heightForWidth(
494             font, d_data->renderFlags, d_data->text, width);
495     }
496 
497     h = map.screenToLayoutY(h);
498     return h;
499 }
500 
501 /*!
502    Find the height for a given width
503 
504    \param defaultFont Font, used for the calculation if the text has no font
505 
506    \return Calculated height
507 */
508 
509 /*!
510    Returns the size, that is needed to render text
511 
512    \param defaultFont Font of the text
513    \return Caluclated size
514 */
textSize(const QFont & defaultFont) const515 QSize QwtText::textSize(const QFont &defaultFont) const
516 {
517 #if QT_VERSION < 0x040000
518     const QFont font(usedFont(defaultFont));
519 #else
520     // We want to calculate in screen metrics. So
521     // we need a font that uses screen metrics
522 
523     const QFont font(usedFont(defaultFont), QApplication::desktop());
524 #endif
525 
526     if ( !d_layoutCache->textSize.isValid()
527         || d_layoutCache->font != font )
528     {
529         d_layoutCache->textSize = d_data->textEngine->textSize(
530             font, d_data->renderFlags, d_data->text);
531         d_layoutCache->font = font;
532     }
533 
534     QSize sz = d_layoutCache->textSize;
535 
536     const QwtMetricsMap map = QwtPainter::metricsMap();
537 
538     if ( d_data->layoutAttributes & MinimumLayout )
539     {
540         int left, right, top, bottom;
541         d_data->textEngine->textMargins(font, d_data->text,
542             left, right, top, bottom);
543         sz -= QSize(left + right, top + bottom);
544 
545 #if QT_VERSION >= 0x040000
546         if ( !map.isIdentity() )
547         {
548 #ifdef __GNUC__
549 #endif
550             /*
551                 When printing in high resolution, the tick labels
552                 of are cut of. We need to find out why, but for
553                 the moment we add a couple of pixels instead.
554              */
555             sz += QSize(3, 2);
556         }
557 #endif
558     }
559 
560     sz = map.screenToLayout(sz);
561     return sz;
562 }
563 
564 /*!
565    Draw a text into a rectangle
566 
567    \param painter Painter
568    \param rect Rectangle
569 */
draw(QPainter * painter,const QRect & rect) const570 void QwtText::draw(QPainter *painter, const QRect &rect) const
571 {
572     if ( d_data->paintAttributes & PaintBackground )
573     {
574         if ( d_data->backgroundPen != Qt::NoPen ||
575             d_data->backgroundBrush != Qt::NoBrush )
576         {
577             painter->save();
578             painter->setPen(QwtPainter::scaledPen(d_data->backgroundPen));
579             painter->setBrush(d_data->backgroundBrush);
580 #if QT_VERSION < 0x040000
581             QwtPainter::drawRect(painter, rect);
582 #else
583             const QRect r(rect.x(), rect.y(),
584                 rect.width() - 1, rect.height() - 1);
585             QwtPainter::drawRect(painter, r);
586 #endif
587             painter->restore();
588         }
589     }
590 
591     painter->save();
592 
593     if ( d_data->paintAttributes & PaintUsingTextFont )
594     {
595         painter->setFont(d_data->font);
596     }
597 
598     if ( d_data->paintAttributes & PaintUsingTextColor )
599     {
600         if ( d_data->color.isValid() )
601             painter->setPen(d_data->color);
602     }
603 
604     QRect expandedRect = rect;
605     if ( d_data->layoutAttributes & MinimumLayout )
606     {
607 #if QT_VERSION < 0x040000
608         const QFont font(painter->font());
609 #else
610         // We want to calculate in screen metrics. So
611         // we need a font that uses screen metrics
612 
613         const QFont font(painter->font(), QApplication::desktop());
614 #endif
615 
616         int left, right, top, bottom;
617         d_data->textEngine->textMargins(
618             font, d_data->text,
619             left, right, top, bottom);
620 
621         const QwtMetricsMap map = QwtPainter::metricsMap();
622         left = map.screenToLayoutX(left);
623         right = map.screenToLayoutX(right);
624         top = map.screenToLayoutY(top);
625         bottom = map.screenToLayoutY(bottom);
626 
627         expandedRect.setTop(rect.top() - top);
628         expandedRect.setBottom(rect.bottom() + bottom);
629         expandedRect.setLeft(rect.left() - left);
630         expandedRect.setRight(rect.right() + right);
631     }
632 
633     d_data->textEngine->draw(painter, expandedRect,
634         d_data->renderFlags, d_data->text);
635 
636     painter->restore();
637 }
638 
639 /*!
640    Find the text engine for a text format
641 
642    In case of QwtText::AutoText the first text engine
643    (beside QwtPlainTextEngine) is returned, where QwtTextEngine::mightRender
644    returns true. If there is none QwtPlainTextEngine is returnd.
645 
646    If no text engine is registered for the format QwtPlainTextEngine
647    is returnd.
648 
649    \param text Text, needed in case of AutoText
650    \param format Text format
651 */
textEngine(const QString & text,QwtText::TextFormat format)652 const QwtTextEngine *QwtText::textEngine(const QString &text,
653     QwtText::TextFormat format)
654 {
655     if ( engineDict == NULL )
656     {
657         /*
658           Note: engineDict is allocated, the first time it is used,
659                 but never deleted, because there is no known last access time.
660                 So don't be irritated, if it is reported as a memory leak
661                 from your memory profiler.
662          */
663         engineDict = new QwtTextEngineDict();
664     }
665 
666     return engineDict->textEngine(text, format);
667 }
668 
669 /*!
670    Assign/Replace a text engine for a text format
671 
672    With setTextEngine it is possible to extend Qwt with
673    other types of text formats.
674 
675    Owner of a commercial Qt license can build the qwtmathml library,
676    that is based on the MathML renderer, that is included in MML Widget
677    component of the Qt solutions package.
678 
679    For QwtText::PlainText it is not allowed to assign a engine == NULL.
680 
681    \param format Text format
682    \param engine Text engine
683 
684    \sa QwtMathMLTextEngine
685    \warning Using QwtText::AutoText does nothing.
686 */
setTextEngine(QwtText::TextFormat format,QwtTextEngine * engine)687 void QwtText::setTextEngine(QwtText::TextFormat format,
688     QwtTextEngine *engine)
689 {
690     if ( engineDict == NULL )
691         engineDict = new QwtTextEngineDict();
692 
693     engineDict->setTextEngine(format, engine);
694 }
695 
696 /*!
697    \brief Find the text engine for a text format
698 
699    textEngine can be used to find out if a text format is supported.
700    F.e, if one wants to use MathML labels, the MathML renderer from the
701    commercial Qt solutions package might be required, that is not
702    available in Qt Open Source Edition environments.
703 
704    \param format Text format
705    \return The text engine, or NULL if no engine is available.
706 */
textEngine(QwtText::TextFormat format)707 const QwtTextEngine *QwtText::textEngine(QwtText::TextFormat format)
708 {
709     if ( engineDict == NULL )
710         engineDict = new QwtTextEngineDict();
711 
712     return engineDict->textEngine(format);
713 }
714