Update spec to build Qt 5.0
[profile/ivi/qtbase.git] / src / gui / text / qstatictext.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qstatictext.h"
43 #include "qstatictext_p.h"
44 #include <private/qtextengine_p.h>
45 #include <private/qfontengine_p.h>
46 #include <qabstracttextdocumentlayout.h>
47
48 QT_BEGIN_NAMESPACE
49
50 /*!
51     \class QStaticText
52     \brief The QStaticText class enables optimized drawing of text when the text and its layout
53     is updated rarely.
54     \since 4.7
55     \inmodule QtGui
56
57     \ingroup multimedia
58     \ingroup text
59     \ingroup shared
60     \mainclass
61
62     QStaticText provides a way to cache layout data for a block of text so that it can be drawn
63     more efficiently than by using QPainter::drawText() in which the layout information is 
64     recalculated with every call. 
65
66     The class primarily provides an optimization for cases where the text, its font and the
67     transformations on the painter are static over several paint events. If the text or its layout
68     is changed for every iteration, QPainter::drawText() is the more efficient alternative, since
69     the static text's layout would have to be recalculated to take the new state into consideration.
70
71     Translating the painter will not cause the layout of the text to be recalculated, but will cause
72     a very small performance impact on drawStaticText(). Altering any other parts of the painter's
73     transformation or the painter's font will cause the layout of the static text to be
74     recalculated. This should be avoided as often as possible to maximize the performance
75     benefit of using QStaticText.
76
77     In addition, only affine transformations are supported by drawStaticText(). Calling
78     drawStaticText() on a projected painter will perform slightly worse than using the regular
79     drawText() call, so this should be avoided.
80
81     \code
82     class MyWidget: public QWidget
83     {
84     public:
85         MyWidget(QWidget *parent = 0) : QWidget(parent), m_staticText("This is static text")
86
87     protected:
88         void paintEvent(QPaintEvent *)
89         {
90             QPainter painter(this);
91             painter.drawStaticText(0, 0, m_staticText);
92         }
93
94     private:
95         QStaticText m_staticText;
96     };
97     \endcode
98
99     The QStaticText class can be used to mimic the behavior of QPainter::drawText() to a specific
100     point with no boundaries, and also when QPainter::drawText() is called with a bounding 
101     rectangle. 
102
103     If a bounding rectangle is not required, create a QStaticText object without setting a preferred
104     text width. The text will then occupy a single line.
105
106     If you set a text width on the QStaticText object, this will bound the text. The text will
107     be formatted so that no line exceeds the given width. The text width set for QStaticText will
108     not automatically be used for clipping. To achieve clipping in addition to line breaks, use
109     QPainter::setClipRect(). The position of the text is decided by the argument passed to
110     QPainter::drawStaticText() and can change from call to call with a minimal impact on
111     performance.
112
113     For extra convenience, it is possible to apply formatting to the text using the HTML subset
114     supported by QTextDocument. QStaticText will attempt to guess the format of the input text using
115     Qt::mightBeRichText(), and interpret it as rich text if this function returns true. To force
116     QStaticText to display its contents as either plain text or rich text, use the function
117     QStaticText::setTextFormat() and pass in, respectively, Qt::PlainText and Qt::RichText.
118
119     QStaticText can only represent text, so only HTML tags which alter the layout or appearance of
120     the text will be respected. Adding an image to the input HTML, for instance, will cause the
121     image to be included as part of the layout, affecting the positions of the text glyphs, but it
122     will not be displayed. The result will be an empty area the size of the image in the output.
123     Similarly, using tables will cause the text to be laid out in table format, but the borders
124     will not be drawn.
125
126     If it's the first time the static text is drawn, or if the static text, or the painter's font
127     has been altered since the last time it was drawn, the text's layout has to be
128     recalculated. On some paint engines, changing the matrix of the painter will also cause the
129     layout to be recalculated. In particular, this will happen for any engine except for the
130     OpenGL2 paint engine. Recalculating the layout will impose an overhead on the
131     QPainter::drawStaticText() call where it occurs. To avoid this overhead in the paint event, you
132     can call prepare() ahead of time to ensure that the layout is calculated.
133
134     \sa QPainter::drawText(), QPainter::drawStaticText(), QTextLayout, QTextDocument
135 */
136
137 /*!
138     \enum QStaticText::PerformanceHint
139
140     This enum the different performance hints that can be set on the QStaticText. These hints
141     can be used to indicate that the QStaticText should use additional caches, if possible,
142     to improve performance at the expense of memory. In particular, setting the performance hint
143     AggressiveCaching on the QStaticText will improve performance when using the OpenGL graphics
144     system or when drawing to a QOpenGLWidget.
145
146     \value ModerateCaching Do basic caching for high performance at a low memory cost.
147     \value AggressiveCaching Use additional caching when available. This may improve performance
148            at a higher memory cost.
149 */
150
151 /*!
152     Constructs an empty QStaticText
153 */
154 QStaticText::QStaticText()    
155     : data(new QStaticTextPrivate)
156 {
157 }
158
159 /*!
160     Constructs a QStaticText object with the given \a text.
161 */
162 QStaticText::QStaticText(const QString &text)
163     : data(new QStaticTextPrivate)
164 {    
165     data->text = text;
166     data->invalidate();
167 }
168
169 /*!
170     Constructs a QStaticText object which is a copy of \a other.
171 */
172 QStaticText::QStaticText(const QStaticText &other)    
173 {
174     data = other.data;
175 }
176
177 /*!
178     Destroys the QStaticText.
179 */
180 QStaticText::~QStaticText()
181 {
182     Q_ASSERT(!data || data->ref.load() >= 1);
183 }
184
185 /*!
186     \internal
187 */
188 void QStaticText::detach()
189 {    
190     if (data->ref.load() != 1)
191         data.detach();
192 }
193
194 /*!
195   Prepares the QStaticText object for being painted with the given \a matrix and the given \a font
196   to avoid overhead when the actual drawStaticText() call is made.
197
198   When drawStaticText() is called, the layout of the QStaticText will be recalculated if any part
199   of the QStaticText object has changed since the last time it was drawn. It will also be
200   recalculated if the painter's font is not the same as when the QStaticText was last drawn, or,
201   on any other paint engine than the OpenGL2 engine, if the painter's matrix has been altered
202   since the static text was last drawn.
203
204   To avoid the overhead of creating the layout the first time you draw the QStaticText after
205   making changes, you can use the prepare() function and pass in the \a matrix and \a font you
206   expect to use when drawing the text.
207
208   \sa QPainter::setFont(), QPainter::setMatrix()
209 */
210 void QStaticText::prepare(const QTransform &matrix, const QFont &font)
211 {
212     data->matrix = matrix;
213     data->font = font;
214     data->init();
215 }
216
217
218 /*!
219     Assigns \a other to this QStaticText.
220 */
221 QStaticText &QStaticText::operator=(const QStaticText &other)
222 {    
223     data = other.data;
224     return *this;
225 }
226
227 /*!
228     \fn void QStaticText::swap(QStaticText &other)
229     \since 5.0
230
231     Swaps this static text instance with \a other. This function is
232     very fast and never fails.
233 */
234
235 /*!
236     Compares \a other to this QStaticText. Returns true if the texts, fonts and text widths
237     are equal.
238 */
239 bool QStaticText::operator==(const QStaticText &other) const
240 {
241     return (data == other.data
242             || (data->text == other.data->text
243                 && data->font == other.data->font
244                 && data->textWidth == other.data->textWidth));
245 }
246
247 /*!
248     Compares \a other to this QStaticText. Returns true if the texts, fonts or maximum sizes
249     are different.
250 */
251 bool QStaticText::operator!=(const QStaticText &other) const
252 {
253     return !(*this == other);
254 }
255
256 /*!
257     Sets the text of the QStaticText to \a text.
258
259     \note This function will cause the layout of the text to require recalculation.
260
261     \sa text()
262 */
263 void QStaticText::setText(const QString &text)
264 {
265     detach();
266     data->text = text;
267     data->invalidate();
268 }
269
270 /*!
271    Sets the text format of the QStaticText to \a textFormat. If \a textFormat is set to
272    Qt::AutoText (the default), the format of the text will try to be determined using the
273    function Qt::mightBeRichText(). If the text format is Qt::PlainText, then the text will be
274    displayed as is, whereas it will be interpreted as HTML if the format is Qt::RichText. HTML tags
275    that alter the font of the text, its color, or its layout are supported by QStaticText.
276
277    \note This function will cause the layout of the text to require recalculation.
278
279    \sa textFormat(), setText(), text()
280 */
281 void QStaticText::setTextFormat(Qt::TextFormat textFormat)
282 {
283     detach();
284     data->textFormat = textFormat;
285     data->invalidate();
286 }
287
288 /*!
289   Returns the text format of the QStaticText.
290
291   \sa setTextFormat(), setText(), text()
292 */
293 Qt::TextFormat QStaticText::textFormat() const
294 {
295     return Qt::TextFormat(data->textFormat);
296 }
297
298 /*!
299     Returns the text of the QStaticText.
300
301     \sa setText()
302 */
303 QString QStaticText::text() const 
304 {
305     return data->text;
306 }
307
308 /*!
309   Sets the performance hint of the QStaticText according to the \a
310   performanceHint provided. The \a performanceHint is used to
311   customize how much caching is done internally to improve
312   performance.
313
314   The default is QStaticText::ModerateCaching.
315
316   \note This function will cause the layout of the text to require recalculation.
317
318   \sa performanceHint()
319 */
320 void QStaticText::setPerformanceHint(PerformanceHint performanceHint)
321 {
322     if ((performanceHint == ModerateCaching && !data->useBackendOptimizations)
323         || (performanceHint == AggressiveCaching && data->useBackendOptimizations)) {
324         return;
325     }
326     detach();
327     data->useBackendOptimizations = (performanceHint == AggressiveCaching);
328     data->invalidate();
329 }
330
331 /*!
332   Returns which performance hint is set for the QStaticText.
333
334   \sa setPerformanceHint()
335 */
336 QStaticText::PerformanceHint QStaticText::performanceHint() const
337 {
338     return data->useBackendOptimizations ? AggressiveCaching : ModerateCaching;
339 }
340
341 /*!
342    Sets the text option structure that controls the layout process to the given \a textOption.
343
344    \sa textOption()
345 */
346 void QStaticText::setTextOption(const QTextOption &textOption)
347 {
348     detach();
349     data->textOption = textOption;
350     data->invalidate();
351 }
352
353 /*!
354     Returns the current text option used to control the layout process.
355 */
356 QTextOption QStaticText::textOption() const
357 {
358     return data->textOption;
359 }
360
361 /*!
362     Sets the preferred width for this QStaticText. If the text is wider than the specified width,
363     it will be broken into multiple lines and grow vertically. If the text cannot be split into
364     multiple lines, it will be larger than the specified \a textWidth.
365
366     Setting the preferred text width to a negative number will cause the text to be unbounded.
367
368     Use size() to get the actual size of the text.
369
370     \note This function will cause the layout of the text to require recalculation.
371
372     \sa textWidth(), size()
373 */
374 void QStaticText::setTextWidth(qreal textWidth)
375 {
376     detach();
377     data->textWidth = textWidth;
378     data->invalidate();
379 }
380
381 /*!
382     Returns the preferred width for this QStaticText.
383
384     \sa setTextWidth()
385 */
386 qreal QStaticText::textWidth() const
387 {
388     return data->textWidth;
389 }
390
391 /*!
392   Returns the size of the bounding rect for this QStaticText.
393
394   \sa textWidth()
395 */
396 QSizeF QStaticText::size() const
397 {
398     if (data->needsRelayout)
399         data->init();
400     return data->actualSize;
401 }
402
403 QStaticTextPrivate::QStaticTextPrivate()
404         : textWidth(-1.0), items(0), itemCount(0), glyphPool(0), positionPool(0), charPool(0),
405           needsRelayout(true), useBackendOptimizations(false), textFormat(Qt::AutoText),
406           untransformedCoordinates(false)
407 {
408 }
409
410 QStaticTextPrivate::QStaticTextPrivate(const QStaticTextPrivate &other)
411     : text(other.text), font(other.font), textWidth(other.textWidth), matrix(other.matrix),
412       items(0), itemCount(0), glyphPool(0), positionPool(0), charPool(0), textOption(other.textOption),
413       needsRelayout(true), useBackendOptimizations(other.useBackendOptimizations),
414       textFormat(other.textFormat), untransformedCoordinates(other.untransformedCoordinates)
415 {
416 }
417
418 QStaticTextPrivate::~QStaticTextPrivate()
419 {
420     delete[] items;
421     delete[] glyphPool;
422     delete[] positionPool;
423     delete[] charPool;
424 }
425
426 QStaticTextPrivate *QStaticTextPrivate::get(const QStaticText *q)
427 {
428     return q->data.data();
429 }
430
431 namespace {
432
433     class DrawTextItemRecorder: public QPaintEngine
434     {
435     public:
436         DrawTextItemRecorder(bool untransformedCoordinates, bool useBackendOptimizations)
437                 : m_dirtyPen(false), m_useBackendOptimizations(useBackendOptimizations),
438                   m_untransformedCoordinates(untransformedCoordinates), m_currentColor(Qt::black)
439         {
440         }
441
442         virtual void updateState(const QPaintEngineState &newState)
443         {
444             if (newState.state() & QPaintEngine::DirtyPen
445                 && newState.pen().color() != m_currentColor) {
446                 m_dirtyPen = true;
447                 m_currentColor = newState.pen().color();
448             }
449         }
450
451         virtual void drawTextItem(const QPointF &position, const QTextItem &textItem)
452         {
453             const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
454
455             QStaticTextItem currentItem;
456             currentItem.setFontEngine(ti.fontEngine);
457             currentItem.font = ti.font();
458             currentItem.charOffset = m_chars.size();
459             currentItem.numChars = ti.num_chars;
460             currentItem.glyphOffset = m_glyphs.size(); // Store offset into glyph pool
461             currentItem.positionOffset = m_glyphs.size(); // Offset into position pool
462             currentItem.useBackendOptimizations = m_useBackendOptimizations;
463             if (m_dirtyPen)
464                 currentItem.color = m_currentColor;
465
466             QTransform matrix = m_untransformedCoordinates ? QTransform() : state->transform();
467             matrix.translate(position.x(), position.y());
468
469             QVarLengthArray<glyph_t> glyphs;
470             QVarLengthArray<QFixedPoint> positions;
471             ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
472
473             int size = glyphs.size();
474             Q_ASSERT(size == positions.size());
475             currentItem.numGlyphs = size;
476
477             m_glyphs.resize(m_glyphs.size() + size);
478             m_positions.resize(m_glyphs.size());
479             m_chars.resize(m_chars.size() + ti.num_chars);
480
481             glyph_t *glyphsDestination = m_glyphs.data() + currentItem.glyphOffset;
482             memcpy(glyphsDestination, glyphs.constData(), sizeof(glyph_t) * currentItem.numGlyphs);
483
484             QFixedPoint *positionsDestination = m_positions.data() + currentItem.positionOffset;
485             memcpy(positionsDestination, positions.constData(), sizeof(QFixedPoint) * currentItem.numGlyphs);
486
487             QChar *charsDestination = m_chars.data() + currentItem.charOffset;
488             memcpy(charsDestination, ti.chars, sizeof(QChar) * currentItem.numChars);
489
490             m_items.append(currentItem);
491         }
492
493         virtual void drawPolygon(const QPointF *, int , PolygonDrawMode )
494         {
495             /* intentionally empty */
496         }
497
498         virtual bool begin(QPaintDevice *)  { return true; }
499         virtual bool end() { return true; }
500         virtual void drawPixmap(const QRectF &, const QPixmap &, const QRectF &) {}
501         virtual Type type() const
502         {
503             return User;
504         }
505
506         QVector<QStaticTextItem> items() const
507         {
508             return m_items;
509         }
510
511         QVector<QFixedPoint> positions() const
512         {
513             return m_positions;
514         }
515
516         QVector<glyph_t> glyphs() const
517         {
518             return m_glyphs;
519         }
520
521         QVector<QChar> chars() const
522         {
523             return m_chars;
524         }
525
526     private:
527         QVector<QStaticTextItem> m_items;
528         QVector<QFixedPoint> m_positions;
529         QVector<glyph_t> m_glyphs;
530         QVector<QChar> m_chars;
531
532         bool m_dirtyPen;
533         bool m_useBackendOptimizations;
534         bool m_untransformedCoordinates;
535         QColor m_currentColor;
536     };
537
538     class DrawTextItemDevice: public QPaintDevice
539     {
540     public:
541         DrawTextItemDevice(bool untransformedCoordinates, bool useBackendOptimizations)
542         {
543             m_paintEngine = new DrawTextItemRecorder(untransformedCoordinates,
544                                                      useBackendOptimizations);
545         }
546
547         ~DrawTextItemDevice()
548         {
549             delete m_paintEngine;
550         }
551
552         int metric(PaintDeviceMetric m) const
553         {
554             int val;
555             switch (m) {
556             case PdmWidth:
557             case PdmHeight:
558             case PdmWidthMM:
559             case PdmHeightMM:
560                 val = 0;
561                 break;
562             case PdmDpiX:
563             case PdmPhysicalDpiX:
564                 val = qt_defaultDpiX();
565                 break;
566             case PdmDpiY:
567             case PdmPhysicalDpiY:
568                 val = qt_defaultDpiY();
569                 break;
570             case PdmNumColors:
571                 val = 16777216;
572                 break;
573             case PdmDepth:
574                 val = 24;
575                 break;
576             default:
577                 val = 0;
578                 qWarning("DrawTextItemDevice::metric: Invalid metric command");
579             }
580             return val;
581         }
582
583         virtual QPaintEngine *paintEngine() const
584         {
585             return m_paintEngine;
586         }
587
588         QVector<glyph_t> glyphs() const
589         {
590             return m_paintEngine->glyphs();
591         }
592
593         QVector<QFixedPoint> positions() const
594         {
595             return m_paintEngine->positions();
596         }
597
598         QVector<QStaticTextItem> items() const
599         {
600             return m_paintEngine->items();
601         }
602
603         QVector<QChar> chars() const
604         {
605             return m_paintEngine->chars();
606         }
607
608     private:
609         DrawTextItemRecorder *m_paintEngine;
610     };
611 }
612
613 void QStaticTextPrivate::paintText(const QPointF &topLeftPosition, QPainter *p)
614 {
615     bool preferRichText = textFormat == Qt::RichText
616                           || (textFormat == Qt::AutoText && Qt::mightBeRichText(text));
617
618     if (!preferRichText) {
619         QTextLayout textLayout;
620         textLayout.setText(text);
621         textLayout.setFont(font);
622         textLayout.setTextOption(textOption);
623         textLayout.setCacheEnabled(true);
624
625         qreal leading = QFontMetricsF(font).leading();
626         qreal height = -leading;
627
628         textLayout.beginLayout();
629         while (1) {
630             QTextLine line = textLayout.createLine();
631             if (!line.isValid())
632                 break;
633
634             if (textWidth >= 0.0)
635                 line.setLineWidth(textWidth);
636             height += leading;
637             line.setPosition(QPointF(0.0, height));
638             height += line.height();
639         }
640         textLayout.endLayout();
641
642         actualSize = textLayout.boundingRect().size();
643         textLayout.draw(p, topLeftPosition);
644     } else {
645         QTextDocument document;
646 #ifndef QT_NO_CSSPARSER
647         QColor color = p->pen().color();
648         document.setDefaultStyleSheet(QString::fromLatin1("body { color: #%1%2%3 }")
649                                       .arg(QString::number(color.red(), 16), 2, QLatin1Char('0'))
650                                       .arg(QString::number(color.green(), 16), 2, QLatin1Char('0'))
651                                       .arg(QString::number(color.blue(), 16), 2, QLatin1Char('0')));
652 #endif
653         document.setDefaultFont(font);
654         document.setDocumentMargin(0.0);        
655 #ifndef QT_NO_TEXTHTMLPARSER
656         document.setHtml(text);
657 #else
658         document.setPlainText(text);
659 #endif
660         if (textWidth >= 0.0)
661             document.setTextWidth(textWidth);
662         else
663             document.adjustSize();
664         document.setDefaultTextOption(textOption);
665
666         p->save();
667         p->translate(topLeftPosition);
668         QAbstractTextDocumentLayout::PaintContext ctx;
669         ctx.palette.setColor(QPalette::Text, p->pen().color());
670         document.documentLayout()->draw(p, ctx);
671         p->restore();
672
673         if (textWidth >= 0.0)
674             document.adjustSize(); // Find optimal size
675
676         actualSize = document.size();
677     }
678 }
679
680 void QStaticTextPrivate::init()
681 {
682     delete[] items;
683     delete[] glyphPool;
684     delete[] positionPool;
685     delete[] charPool;
686
687     position = QPointF(0, 0);
688
689     DrawTextItemDevice device(untransformedCoordinates, useBackendOptimizations);
690     {
691         QPainter painter(&device);
692         painter.setFont(font);
693         painter.setTransform(matrix);
694
695         paintText(QPointF(0, 0), &painter);
696     }
697
698     QVector<QStaticTextItem> deviceItems = device.items();
699     QVector<QFixedPoint> positions = device.positions();
700     QVector<glyph_t> glyphs = device.glyphs();
701     QVector<QChar> chars = device.chars();
702
703     itemCount = deviceItems.size();
704     items = new QStaticTextItem[itemCount];
705
706     glyphPool = new glyph_t[glyphs.size()];
707     memcpy(glyphPool, glyphs.constData(), glyphs.size() * sizeof(glyph_t));
708
709     positionPool = new QFixedPoint[positions.size()];
710     memcpy(positionPool, positions.constData(), positions.size() * sizeof(QFixedPoint));
711
712     charPool = new QChar[chars.size()];
713     memcpy(charPool, chars.constData(), chars.size() * sizeof(QChar));
714
715     for (int i=0; i<itemCount; ++i) {
716         items[i] = deviceItems.at(i);
717
718         items[i].glyphs = glyphPool + items[i].glyphOffset;
719         items[i].glyphPositions = positionPool + items[i].positionOffset;
720         items[i].chars = charPool + items[i].charOffset;
721     }
722
723     needsRelayout = false;
724 }
725
726 QStaticTextItem::~QStaticTextItem()
727 {
728     if (m_userData != 0 && !m_userData->ref.deref())
729         delete m_userData;
730     m_fontEngine->ref.deref();
731 }
732
733 void QStaticTextItem::setFontEngine(QFontEngine *fe)
734 {
735     if (m_fontEngine != 0)
736         m_fontEngine->ref.deref();
737     m_fontEngine = fe;
738     if (m_fontEngine != 0)
739         m_fontEngine->ref.ref();
740 }
741
742 QT_END_NAMESPACE