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