a844f79cc6a0c2542c1f644e7501b61985ce3fa1
[profile/ivi/qtdeclarative.git] / src / declarative / items / qsgtext.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qsgtext_p.h"
43 #include "qsgtext_p_p.h"
44
45 #include <private/qsgdistancefieldglyphcache_p.h>
46 #include <private/qsgcontext_p.h>
47 #include <private/qsgadaptationlayer_p.h>
48 #include "qsgtextnode_p.h"
49 #include "qsgimage_p_p.h"
50 #include <private/qsgtexture_p.h>
51
52 #include <QtDeclarative/qdeclarativeinfo.h>
53 #include <QtGui/qgraphicssceneevent.h>
54 #include <QtGui/qabstracttextdocumentlayout.h>
55 #include <QtGui/qpainter.h>
56 #include <QtGui/qtextdocument.h>
57 #include <QtGui/qtextobject.h>
58 #include <QtGui/qtextcursor.h>
59 #include <QtGui/qapplication.h>
60
61 #include <private/qdeclarativestyledtext_p.h>
62 #include <private/qdeclarativepixmapcache_p.h>
63
64 #include <qmath.h>
65 #include <limits.h>
66
67 QT_BEGIN_NAMESPACE
68
69 extern Q_GUI_EXPORT bool qt_applefontsmoothing_enabled;
70
71 class QSGTextDocumentWithImageResources : public QTextDocument {
72     Q_OBJECT
73
74 public:
75     QSGTextDocumentWithImageResources(QSGText *parent);
76     virtual ~QSGTextDocumentWithImageResources();
77
78     void setText(const QString &);
79     int resourcesLoading() const { return outstanding; }
80
81 protected:
82     QVariant loadResource(int type, const QUrl &name);
83
84 private slots:
85     void requestFinished();
86
87 private:
88     QHash<QUrl, QDeclarativePixmap *> m_resources;
89
90     int outstanding;
91     static QSet<QUrl> errors;
92 };
93
94 DEFINE_BOOL_CONFIG_OPTION(enableImageCache, QML_ENABLE_TEXT_IMAGE_CACHE);
95
96 QString QSGTextPrivate::elideChar = QString(0x2026);
97
98 QSGTextPrivate::QSGTextPrivate()
99 : color((QRgb)0), style(QSGText::Normal), hAlign(QSGText::AlignLeft),
100   vAlign(QSGText::AlignTop), elideMode(QSGText::ElideNone),
101   format(QSGText::AutoText), wrapMode(QSGText::NoWrap), lineHeight(1),
102   lineHeightMode(QSGText::ProportionalHeight), lineCount(1), maximumLineCount(INT_MAX),
103   maximumLineCountValid(false),
104   texture(0),
105   imageCacheDirty(false), updateOnComponentComplete(true),
106   richText(false), singleline(false), cacheAllTextAsImage(true), internalWidthUpdate(false),
107   requireImplicitWidth(false), truncated(false), hAlignImplicit(true), rightToLeftText(false),
108   layoutTextElided(false), richTextAsImage(false), textureImageCacheDirty(false), naturalWidth(0),
109   doc(0), layoutThread(0), nodeType(NodeIsNull)
110 {
111     cacheAllTextAsImage = enableImageCache();
112 }
113
114 void QSGTextPrivate::init()
115 {
116     Q_Q(QSGText);
117     q->setAcceptedMouseButtons(Qt::LeftButton);
118     q->setFlag(QSGItem::ItemHasContents);
119 }
120
121 QSGTextDocumentWithImageResources::QSGTextDocumentWithImageResources(QSGText *parent)
122 : QTextDocument(parent), outstanding(0)
123 {
124     setUndoRedoEnabled(false);
125 }
126
127 QSGTextDocumentWithImageResources::~QSGTextDocumentWithImageResources()
128 {
129     if (!m_resources.isEmpty())
130         qDeleteAll(m_resources);
131 }
132
133 QVariant QSGTextDocumentWithImageResources::loadResource(int type, const QUrl &name)
134 {
135     QDeclarativeContext *context = qmlContext(parent());
136     QUrl url = context->resolvedUrl(name);
137
138     if (type == QTextDocument::ImageResource) {
139         QHash<QUrl, QDeclarativePixmap *>::Iterator iter = m_resources.find(url);
140
141         if (iter == m_resources.end()) {
142             QDeclarativePixmap *p = new QDeclarativePixmap(context->engine(), url);
143             iter = m_resources.insert(name, p);
144
145             if (p->isLoading()) {
146                 p->connectFinished(this, SLOT(requestFinished()));
147                 outstanding++;
148             }
149         }
150
151         QDeclarativePixmap *p = *iter;
152         if (p->isReady()) {
153             return p->pixmap();
154         } else if (p->isError()) {
155             if (!errors.contains(url)) {
156                 errors.insert(url);
157                 qmlInfo(parent()) << p->error();
158             }
159         }
160     }
161
162     return QTextDocument::loadResource(type,url); // The *resolved* URL
163 }
164
165 void QSGTextDocumentWithImageResources::requestFinished()
166 {
167     outstanding--;
168     if (outstanding == 0) {
169         QSGText *textItem = static_cast<QSGText*>(parent());
170         QString text = textItem->text();
171 #ifndef QT_NO_TEXTHTMLPARSER
172         setHtml(text);
173 #else
174         setPlainText(text);
175 #endif
176         QSGTextPrivate *d = QSGTextPrivate::get(textItem);
177         d->updateLayout();
178     }
179 }
180
181 void QSGTextDocumentWithImageResources::setText(const QString &text)
182 {
183     if (!m_resources.isEmpty()) {
184         qDeleteAll(m_resources);
185         m_resources.clear();
186         outstanding = 0;
187     }
188
189 #ifndef QT_NO_TEXTHTMLPARSER
190     setHtml(text);
191 #else
192     setPlainText(text);
193 #endif
194 }
195
196 QSet<QUrl> QSGTextDocumentWithImageResources::errors;
197
198 QSGTextPrivate::~QSGTextPrivate()
199 {
200 }
201
202 qreal QSGTextPrivate::getImplicitWidth() const
203 {
204     if (!requireImplicitWidth) {
205         // We don't calculate implicitWidth unless it is required.
206         // We need to force a size update now to ensure implicitWidth is calculated
207         QSGTextPrivate *me = const_cast<QSGTextPrivate*>(this);
208         me->requireImplicitWidth = true;
209         me->updateSize();
210     }
211     return implicitWidth;
212 }
213
214 void QSGTextPrivate::updateLayout()
215 {
216     Q_Q(QSGText);
217     if (!q->isComponentComplete()) {
218         updateOnComponentComplete = true;
219         return;
220     }
221
222     layoutTextElided = false;
223     // Setup instance of QTextLayout for all cases other than richtext
224     if (!richText) {
225         layout.clearLayout();
226         layout.setFont(font);
227         if (format != QSGText::StyledText) {
228             QString tmp = text;
229             tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
230             singleline = !tmp.contains(QChar::LineSeparator);
231             if (singleline && !maximumLineCountValid && elideMode != QSGText::ElideNone && q->widthValid()) {
232                 QFontMetrics fm(font);
233                 tmp = fm.elidedText(tmp,(Qt::TextElideMode)elideMode,q->width());
234                 if (tmp != text) {
235                     layoutTextElided = true;
236                     if (!truncated) {
237                         truncated = true;
238                         emit q->truncatedChanged();
239                     }
240                 }
241             }
242             layout.setText(tmp);
243         } else {
244             singleline = false;
245             QDeclarativeStyledText::parse(text, layout);
246         }
247     } else {
248         ensureDoc();
249         QTextBlockFormat::LineHeightTypes type;
250         type = lineHeightMode == QSGText::FixedHeight ? QTextBlockFormat::FixedHeight : QTextBlockFormat::ProportionalHeight;
251         QTextBlockFormat blockFormat;
252         blockFormat.setLineHeight((lineHeightMode == QSGText::FixedHeight ? lineHeight : lineHeight * 100), type);
253         for (QTextBlock it = doc->begin(); it != doc->end(); it = it.next()) {
254             QTextCursor cursor(it);
255             cursor.setBlockFormat(blockFormat);
256         }
257     }
258
259     updateSize();
260 }
261
262 void QSGTextPrivate::updateSize()
263 {
264     Q_Q(QSGText);
265
266     if (!q->isComponentComplete()) {
267         updateOnComponentComplete = true;
268         return;
269     }
270
271     if (!requireImplicitWidth) {
272         emit q->implicitWidthChanged();
273         // if the implicitWidth is used, then updateSize() has already been called (recursively)
274         if (requireImplicitWidth)
275             return;
276     }
277
278     invalidateImageCache();
279
280     QFontMetrics fm(font);
281     if (text.isEmpty()) {
282         q->setImplicitWidth(0);
283         q->setImplicitHeight(fm.height());
284         paintedSize = QSize(0, fm.height());
285         emit q->paintedSizeChanged();
286         q->update();
287         return;
288     }
289
290     int dy = q->height();
291     QSize size(0, 0);
292
293     layoutThread = QThread::currentThread();
294
295     //setup instance of QTextLayout for all cases other than richtext
296     if (!richText) {
297         QRect textRect = setupTextLayout();
298         layedOutTextRect = textRect;
299         size = textRect.size();
300         dy -= size.height();
301     } else {
302         singleline = false; // richtext can't elide or be optimized for single-line case
303         ensureDoc();
304         doc->setDefaultFont(font);
305         QSGText::HAlignment horizontalAlignment = q->effectiveHAlign();
306         if (rightToLeftText) {
307             if (horizontalAlignment == QSGText::AlignLeft)
308                 horizontalAlignment = QSGText::AlignRight;
309             else if (horizontalAlignment == QSGText::AlignRight)
310                 horizontalAlignment = QSGText::AlignLeft;
311         }
312         QTextOption option;
313         option.setAlignment((Qt::Alignment)int(horizontalAlignment | vAlign));
314         option.setWrapMode(QTextOption::WrapMode(wrapMode));
315         doc->setDefaultTextOption(option);
316         if (requireImplicitWidth && q->widthValid()) {
317             doc->setTextWidth(-1);
318             naturalWidth = doc->idealWidth();
319         }
320         if (wrapMode != QSGText::NoWrap && q->widthValid())
321             doc->setTextWidth(q->width());
322         else
323             doc->setTextWidth(doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug)
324         dy -= (int)doc->size().height();
325         QSize dsize = doc->size().toSize();
326         layedOutTextRect = QRect(QPoint(0,0), dsize);
327         size = QSize(int(doc->idealWidth()),dsize.height());
328     }
329     int yoff = 0;
330
331     if (q->heightValid()) {
332         if (vAlign == QSGText::AlignBottom)
333             yoff = dy;
334         else if (vAlign == QSGText::AlignVCenter)
335             yoff = dy/2;
336     }
337     q->setBaselineOffset(fm.ascent() + yoff);
338
339     //### need to comfirm cost of always setting these for richText
340     internalWidthUpdate = true;
341     if (!q->widthValid())
342         q->setImplicitWidth(size.width());
343     else if (requireImplicitWidth)
344         q->setImplicitWidth(naturalWidth);
345     internalWidthUpdate = false;
346
347     q->setImplicitHeight(size.height());
348     if (paintedSize != size) {
349         paintedSize = size;
350         emit q->paintedSizeChanged();
351     }
352     q->update();
353 }
354
355 /*!
356     Lays out the QSGTextPrivate::layout QTextLayout in the constraints of the QSGText.
357
358     Returns the size of the final text.  This can be used to position the text vertically (the text is
359     already absolutely positioned horizontally).
360 */
361 QRect QSGTextPrivate::setupTextLayout()
362 {
363     // ### text layout handling should be profiled and optimized as needed
364     // what about QStackTextEngine engine(tmp, d->font.font()); QTextLayout textLayout(&engine);
365     Q_Q(QSGText);
366     layout.setCacheEnabled(true);
367
368     qreal lineWidth = 0;
369     int visibleCount = 0;
370
371     //set manual width
372     if (q->widthValid())
373         lineWidth = q->width();
374
375     QTextOption textOption = layout.textOption();
376     textOption.setAlignment(Qt::Alignment(q->effectiveHAlign()));
377     textOption.setWrapMode(QTextOption::WrapMode(wrapMode));
378     layout.setTextOption(textOption);
379
380     bool elideText = false;
381     bool truncate = false;
382
383     QFontMetrics fm(layout.font());
384     elidePos = QPointF();
385
386     if (requireImplicitWidth && q->widthValid()) {
387         // requires an extra layout
388         QString elidedText;
389         if (layoutTextElided) {
390             // We have provided elided text to the layout, but we must calculate unelided width.
391             elidedText = layout.text();
392             layout.setText(text);
393         }
394         layout.beginLayout();
395         forever {
396             QTextLine line = layout.createLine();
397             if (!line.isValid())
398                 break;
399         }
400         layout.endLayout();
401         QRectF br;
402         for (int i = 0; i < layout.lineCount(); ++i) {
403             QTextLine line = layout.lineAt(i);
404             br = br.united(line.naturalTextRect());
405         }
406         naturalWidth = br.width();
407         if (layoutTextElided)
408             layout.setText(elidedText);
409     }
410
411     if (maximumLineCountValid) {
412         layout.beginLayout();
413         if (!lineWidth)
414             lineWidth = INT_MAX;
415         int linesLeft = maximumLineCount;
416         int visibleTextLength = 0;
417         while (linesLeft > 0) {
418             QTextLine line = layout.createLine();
419             if (!line.isValid())
420                 break;
421
422             visibleCount++;
423             if (lineWidth)
424                 line.setLineWidth(lineWidth);
425             visibleTextLength += line.textLength();
426
427             if (--linesLeft == 0) {
428                 if (visibleTextLength < text.length()) {
429                     truncate = true;
430                     if (elideMode==QSGText::ElideRight && q->widthValid()) {
431                         qreal elideWidth = fm.width(elideChar);
432                         // Need to correct for alignment
433                         line.setLineWidth(lineWidth-elideWidth);
434                         if (layout.text().mid(line.textStart(), line.textLength()).isRightToLeft()) {
435                             line.setPosition(QPointF(line.position().x() + elideWidth, line.position().y()));
436                             elidePos.setX(line.naturalTextRect().left() - elideWidth);
437                         } else {
438                             elidePos.setX(line.naturalTextRect().right());
439                         }
440                         elideText = true;
441                     }
442                 }
443             }
444         }
445         layout.endLayout();
446
447         //Update truncated
448         if (truncated != truncate) {
449             truncated = truncate;
450             emit q->truncatedChanged();
451         }
452     } else {
453         layout.beginLayout();
454         forever {
455             QTextLine line = layout.createLine();
456             if (!line.isValid())
457                 break;
458             visibleCount++;
459             if (lineWidth)
460                 line.setLineWidth(lineWidth);
461         }
462         layout.endLayout();
463     }
464
465     qreal height = 0;
466     QRectF br;
467     for (int i = 0; i < layout.lineCount(); ++i) {
468         QTextLine line = layout.lineAt(i);
469         // set line spacing
470         line.setPosition(QPointF(line.position().x(), height));
471         if (elideText && i == layout.lineCount()-1) {
472             elidePos.setY(height + fm.ascent());
473             br = br.united(QRectF(elidePos, QSizeF(fm.width(elideChar), fm.ascent())));
474         }
475         br = br.united(line.naturalTextRect());
476         height += (lineHeightMode == QSGText::FixedHeight) ? lineHeight : line.height() * lineHeight;
477     }
478     br.setHeight(height);
479
480     if (!q->widthValid())
481         naturalWidth = br.width();
482
483     //Update the number of visible lines
484     if (lineCount != visibleCount) {
485         lineCount = visibleCount;
486         emit q->lineCountChanged();
487     }
488
489     return QRect(qRound(br.x()), qRound(br.y()), qCeil(br.width()), qCeil(br.height()));
490 }
491
492 /*!
493     Returns a painted version of the QSGTextPrivate::layout QTextLayout.
494     If \a drawStyle is true, the style color overrides all colors in the document.
495 */
496 QPixmap QSGTextPrivate::textLayoutImage(bool drawStyle)
497 {
498     QSize size = layedOutTextRect.size();
499
500     //paint text
501     QPixmap img(size);
502     if (!size.isEmpty()) {
503         img.fill(Qt::transparent);
504 #ifdef Q_WS_MAC
505         bool oldSmooth = qt_applefontsmoothing_enabled;
506         qt_applefontsmoothing_enabled = false;
507 #endif
508         QPainter p(&img);
509 #ifdef Q_WS_MAC
510         qt_applefontsmoothing_enabled = oldSmooth;
511 #endif
512         drawTextLayout(&p, QPointF(-layedOutTextRect.x(),0), drawStyle);
513     }
514     return img;
515 }
516
517 /*!
518     Paints the QSGTextPrivate::layout QTextLayout into \a painter at \a pos.  If
519     \a drawStyle is true, the style color overrides all colors in the document.
520 */
521 void QSGTextPrivate::drawTextLayout(QPainter *painter, const QPointF &pos, bool drawStyle)
522 {
523     if (drawStyle)
524         painter->setPen(styleColor);
525     else
526         painter->setPen(color);
527     painter->setFont(font);
528     layout.draw(painter, pos);
529     if (!elidePos.isNull())
530         painter->drawText(pos + elidePos, elideChar);
531 }
532
533 /*!
534     Returns a painted version of the QSGTextPrivate::doc QTextDocument.
535     If \a drawStyle is true, the style color overrides all colors in the document.
536 */
537 QPixmap QSGTextPrivate::textDocumentImage(bool drawStyle)
538 {
539     QSize size = doc->size().toSize();
540
541     //paint text
542     QPixmap img(size);
543     img.fill(Qt::transparent);
544 #ifdef Q_WS_MAC
545     bool oldSmooth = qt_applefontsmoothing_enabled;
546     qt_applefontsmoothing_enabled = false;
547 #endif
548     QPainter p(&img);
549 #ifdef Q_WS_MAC
550     qt_applefontsmoothing_enabled = oldSmooth;
551 #endif
552
553     QAbstractTextDocumentLayout::PaintContext context;
554
555     QTextOption oldOption(doc->defaultTextOption());
556     if (drawStyle) {
557         context.palette.setColor(QPalette::Text, styleColor);
558         QTextOption colorOption(doc->defaultTextOption());
559         colorOption.setFlags(QTextOption::SuppressColors);
560         doc->setDefaultTextOption(colorOption);
561     } else {
562         context.palette.setColor(QPalette::Text, color);
563     }
564     doc->documentLayout()->draw(&p, context);
565     if (drawStyle)
566         doc->setDefaultTextOption(oldOption);
567     return img;
568 }
569
570 /*!
571     Mark the image cache as dirty.
572 */
573 void QSGTextPrivate::invalidateImageCache()
574 {
575     Q_Q(QSGText);
576
577     if(richTextAsImage || cacheAllTextAsImage || (!QSGDistanceFieldGlyphCache::distanceFieldEnabled() && style != QSGText::Normal)){//If actually using the image cache
578         if (imageCacheDirty)
579             return;
580
581         imageCacheDirty = true;
582
583         if (q->isComponentComplete())
584             QCoreApplication::postEvent(q, new QEvent(QEvent::User));
585     } else if (q->isComponentComplete())
586         q->update();
587 }
588
589 /*!
590     Tests if the image cache is dirty, and repaints it if it is.
591 */
592 void QSGTextPrivate::checkImageCache()
593 {
594     Q_Q(QSGText);
595
596     if (!imageCacheDirty)
597         return;
598
599     if (text.isEmpty()) {
600
601         imageCache = QPixmap();
602
603     } else {
604
605         QPixmap textImage;
606         QPixmap styledImage;
607
608         if (richText) {
609             textImage = textDocumentImage(false);
610             if (style != QSGText::Normal)
611                 styledImage = textDocumentImage(true); //### should use styleColor
612         } else {
613             textImage = textLayoutImage(false);
614             if (style != QSGText::Normal)
615                 styledImage = textLayoutImage(true); //### should use styleColor
616         }
617
618         switch (style) {
619         case QSGText::Outline:
620             imageCache = drawOutline(textImage, styledImage);
621             break;
622         case QSGText::Sunken:
623             imageCache = drawOutline(textImage, styledImage, -1);
624             break;
625         case QSGText::Raised:
626             imageCache = drawOutline(textImage, styledImage, 1);
627             break;
628         default:
629             imageCache = textImage;
630             break;
631         }
632
633     }
634
635     imageCacheDirty = false;
636     textureImageCacheDirty = true;
637     q->update();
638 }
639
640 /*!
641     Ensures the QSGTextPrivate::doc variable is set to a valid text document
642 */
643 void QSGTextPrivate::ensureDoc()
644 {
645     if (!doc) {
646         Q_Q(QSGText);
647         doc = new QSGTextDocumentWithImageResources(q);
648         doc->setDocumentMargin(0);
649     }
650 }
651
652 /*!
653     Draw \a styleSource as an outline around \a source and return the new image.
654 */
655 QPixmap QSGTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource)
656 {
657     QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
658     img.fill(Qt::transparent);
659
660     QPainter ppm(&img);
661
662     QPoint pos(0, 0);
663     pos += QPoint(-1, 0);
664     ppm.drawPixmap(pos, styleSource);
665     pos += QPoint(2, 0);
666     ppm.drawPixmap(pos, styleSource);
667     pos += QPoint(-1, -1);
668     ppm.drawPixmap(pos, styleSource);
669     pos += QPoint(0, 2);
670     ppm.drawPixmap(pos, styleSource);
671
672     pos += QPoint(0, -1);
673     ppm.drawPixmap(pos, source);
674     ppm.end();
675
676     return img;
677 }
678
679 /*!
680     Draw \a styleSource below \a source at \a yOffset and return the new image.
681 */
682 QPixmap QSGTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource, int yOffset)
683 {
684     QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
685     img.fill(Qt::transparent);
686
687     QPainter ppm(&img);
688
689     ppm.drawPixmap(QPoint(0, yOffset), styleSource);
690     ppm.drawPixmap(0, 0, source);
691
692     ppm.end();
693
694     return img;
695 }
696
697 /*!
698     \qmlclass Text QSGText
699     \inqmlmodule QtQuick 2
700     \ingroup qml-basic-visual-elements
701     \brief The Text item allows you to add formatted text to a scene.
702     \inherits Item
703
704     Text items can display both plain and rich text. For example, red text with
705     a specific font and size can be defined like this:
706
707     \qml
708     Text {
709         text: "Hello World!"
710         font.family: "Helvetica"
711         font.pointSize: 24
712         color: "red"
713     }
714     \endqml
715
716     Rich text is defined using HTML-style markup:
717
718     \qml
719     Text {
720         text: "<b>Hello</b> <i>World!</i>"
721     }
722     \endqml
723
724     \image declarative-text.png
725
726     If height and width are not explicitly set, Text will attempt to determine how
727     much room is needed and set it accordingly. Unless \l wrapMode is set, it will always
728     prefer width to height (all text will be placed on a single line).
729
730     The \l elide property can alternatively be used to fit a single line of
731     plain text to a set width.
732
733     Note that the \l{Supported HTML Subset} is limited. Also, if the text contains
734     HTML img tags that load remote images, the text is reloaded.
735
736     Text provides read-only text. For editable text, see \l TextEdit.
737
738     \sa {declarative/text/fonts}{Fonts example}
739 */
740 QSGText::QSGText(QSGItem *parent)
741 : QSGImplicitSizeItem(*(new QSGTextPrivate), parent)
742 {
743     Q_D(QSGText);
744     d->init();
745 }
746
747 QSGText::~QSGText()
748 {
749 }
750
751 /*!
752   \qmlproperty bool QtQuick2::Text::clip
753   This property holds whether the text is clipped.
754
755   Note that if the text does not fit in the bounding rectangle it will be abruptly chopped.
756
757   If you want to display potentially long text in a limited space, you probably want to use \c elide instead.
758 */
759
760 /*!
761     \qmlproperty bool QtQuick2::Text::smooth
762
763     This property holds whether the text is smoothly scaled or transformed.
764
765     Smooth filtering gives better visual quality, but is slower.  If
766     the item is displayed at its natural size, this property has no visual or
767     performance effect.
768
769     \note Generally scaling artifacts are only visible if the item is stationary on
770     the screen.  A common pattern when animating an item is to disable smooth
771     filtering at the beginning of the animation and reenable it at the conclusion.
772 */
773
774 /*!
775     \qmlsignal QtQuick2::Text::onLinkActivated(string link)
776
777     This handler is called when the user clicks on a link embedded in the text.
778     The link must be in rich text or HTML format and the
779     \a link string provides access to the particular link.
780
781     \snippet doc/src/snippets/declarative/text/onLinkActivated.qml 0
782
783     The example code will display the text
784     "The main website is at \l{http://qt.nokia.com}{Nokia Qt DF}."
785
786     Clicking on the highlighted link will output
787     \tt{http://qt.nokia.com link activated} to the console.
788 */
789
790 /*!
791     \qmlproperty string QtQuick2::Text::font.family
792
793     Sets the family name of the font.
794
795     The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
796     If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
797     If the family isn't available a family will be set using the font matching algorithm.
798 */
799
800 /*!
801     \qmlproperty bool QtQuick2::Text::font.bold
802
803     Sets whether the font weight is bold.
804 */
805
806 /*!
807     \qmlproperty enumeration QtQuick2::Text::font.weight
808
809     Sets the font's weight.
810
811     The weight can be one of:
812     \list
813     \o Font.Light
814     \o Font.Normal - the default
815     \o Font.DemiBold
816     \o Font.Bold
817     \o Font.Black
818     \endlist
819
820     \qml
821     Text { text: "Hello"; font.weight: Font.DemiBold }
822     \endqml
823 */
824
825 /*!
826     \qmlproperty bool QtQuick2::Text::font.italic
827
828     Sets whether the font has an italic style.
829 */
830
831 /*!
832     \qmlproperty bool QtQuick2::Text::font.underline
833
834     Sets whether the text is underlined.
835 */
836
837 /*!
838     \qmlproperty bool QtQuick2::Text::font.strikeout
839
840     Sets whether the font has a strikeout style.
841 */
842
843 /*!
844     \qmlproperty real QtQuick2::Text::font.pointSize
845
846     Sets the font size in points. The point size must be greater than zero.
847 */
848
849 /*!
850     \qmlproperty int QtQuick2::Text::font.pixelSize
851
852     Sets the font size in pixels.
853
854     Using this function makes the font device dependent.
855     Use \c pointSize to set the size of the font in a device independent manner.
856 */
857
858 /*!
859     \qmlproperty real QtQuick2::Text::font.letterSpacing
860
861     Sets the letter spacing for the font.
862
863     Letter spacing changes the default spacing between individual letters in the font.
864     A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
865 */
866
867 /*!
868     \qmlproperty real QtQuick2::Text::font.wordSpacing
869
870     Sets the word spacing for the font.
871
872     Word spacing changes the default spacing between individual words.
873     A positive value increases the word spacing by a corresponding amount of pixels,
874     while a negative value decreases the inter-word spacing accordingly.
875 */
876
877 /*!
878     \qmlproperty enumeration QtQuick2::Text::font.capitalization
879
880     Sets the capitalization for the text.
881
882     \list
883     \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
884     \o Font.AllUppercase - This alters the text to be rendered in all uppercase type.
885     \o Font.AllLowercase         - This alters the text to be rendered in all lowercase type.
886     \o Font.SmallCaps - This alters the text to be rendered in small-caps type.
887     \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
888     \endlist
889
890     \qml
891     Text { text: "Hello"; font.capitalization: Font.AllLowercase }
892     \endqml
893 */
894 QFont QSGText::font() const
895 {
896     Q_D(const QSGText);
897     return d->sourceFont;
898 }
899
900 void QSGText::setFont(const QFont &font)
901 {
902     Q_D(QSGText);
903     if (d->sourceFont == font)
904         return;
905
906     d->sourceFont = font;
907     QFont oldFont = d->font;
908     d->font = font;
909     if (QSGDistanceFieldGlyphCache::distanceFieldEnabled())
910         d->font.setHintingPreference(QFont::PreferNoHinting);
911
912     if (d->font.pointSizeF() != -1) {
913         // 0.5pt resolution
914         qreal size = qRound(d->font.pointSizeF()*2.0);
915         d->font.setPointSizeF(size/2.0);
916     }
917
918     if (oldFont != d->font)
919         d->updateLayout();
920
921     emit fontChanged(d->sourceFont);
922 }
923
924 /*!
925     \qmlproperty string QtQuick2::Text::text
926
927     The text to display. Text supports both plain and rich text strings.
928
929     The item will try to automatically determine whether the text should
930     be treated as rich text. This determination is made using Qt::mightBeRichText().
931 */
932 QString QSGText::text() const
933 {
934     Q_D(const QSGText);
935     return d->text;
936 }
937
938 void QSGText::setText(const QString &n)
939 {
940     Q_D(QSGText);
941     if (d->text == n)
942         return;
943
944     d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(n));
945     d->text = n;
946     if (isComponentComplete()) {
947         if (d->richText) {
948             d->ensureDoc();
949             d->doc->setText(n);
950             d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
951             d->richTextAsImage = QSGTextNode::isComplexRichText(d->doc);
952         } else {
953             d->rightToLeftText = d->text.isRightToLeft();
954         }
955         d->determineHorizontalAlignment();
956     }
957     d->updateLayout();
958     emit textChanged(d->text);
959 }
960
961 /*!
962     \qmlproperty color QtQuick2::Text::color
963
964     The text color.
965
966     An example of green text defined using hexadecimal notation:
967     \qml
968     Text {
969         color: "#00FF00"
970         text: "green text"
971     }
972     \endqml
973
974     An example of steel blue text defined using an SVG color name:
975     \qml
976     Text {
977         color: "steelblue"
978         text: "blue text"
979     }
980     \endqml
981 */
982 QColor QSGText::color() const
983 {
984     Q_D(const QSGText);
985     return d->color;
986 }
987
988 void QSGText::setColor(const QColor &color)
989 {
990     Q_D(QSGText);
991     if (d->color == color)
992         return;
993
994     d->color = color;
995     d->invalidateImageCache();
996     emit colorChanged(d->color);
997 }
998 /*!
999     \qmlproperty enumeration QtQuick2::Text::style
1000
1001     Set an additional text style.
1002
1003     Supported text styles are:
1004     \list
1005     \o Text.Normal - the default
1006     \o Text.Outline
1007     \o Text.Raised
1008     \o Text.Sunken
1009     \endlist
1010
1011     \qml
1012     Row {
1013         Text { font.pointSize: 24; text: "Normal" }
1014         Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" }
1015         Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" }
1016         Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" }
1017     }
1018     \endqml
1019
1020     \image declarative-textstyle.png
1021 */
1022 QSGText::TextStyle QSGText::style() const
1023 {
1024     Q_D(const QSGText);
1025     return d->style;
1026 }
1027
1028 void QSGText::setStyle(QSGText::TextStyle style)
1029 {
1030     Q_D(QSGText);
1031     if (d->style == style)
1032         return;
1033
1034     // changing to/from Normal requires the boundingRect() to change
1035     if (isComponentComplete() && (d->style == Normal || style == Normal))
1036         update();
1037     d->style = style;
1038     d->invalidateImageCache();
1039     emit styleChanged(d->style);
1040 }
1041
1042 /*!
1043     \qmlproperty color QtQuick2::Text::styleColor
1044
1045     Defines the secondary color used by text styles.
1046
1047     \c styleColor is used as the outline color for outlined text, and as the
1048     shadow color for raised or sunken text. If no style has been set, it is not
1049     used at all.
1050
1051     \qml
1052     Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" }
1053     \endqml
1054
1055     \sa style
1056  */
1057 QColor QSGText::styleColor() const
1058 {
1059     Q_D(const QSGText);
1060     return d->styleColor;
1061 }
1062
1063 void QSGText::setStyleColor(const QColor &color)
1064 {
1065     Q_D(QSGText);
1066     if (d->styleColor == color)
1067         return;
1068
1069     d->styleColor = color;
1070     d->invalidateImageCache();
1071     emit styleColorChanged(d->styleColor);
1072 }
1073
1074 /*!
1075     \qmlproperty enumeration QtQuick2::Text::horizontalAlignment
1076     \qmlproperty enumeration QtQuick2::Text::verticalAlignment
1077     \qmlproperty enumeration QtQuick2::Text::effectiveHorizontalAlignment
1078
1079     Sets the horizontal and vertical alignment of the text within the Text items
1080     width and height. By default, the text is vertically aligned to the top. Horizontal
1081     alignment follows the natural alignment of the text, for example text that is read
1082     from left to right will be aligned to the left.
1083
1084     The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight, \c Text.AlignHCenter and
1085     \c Text.AlignJustify.  The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom
1086     and \c Text.AlignVCenter.
1087
1088     Note that for a single line of text, the size of the text is the area of the text. In this common case,
1089     all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will
1090     need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to
1091     that of the parent.
1092
1093     When using the attached property LayoutMirroring::enabled to mirror application
1094     layouts, the horizontal alignment of text will also be mirrored. However, the property
1095     \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
1096     of Text, use the read-only property \c effectiveHorizontalAlignment.
1097 */
1098 QSGText::HAlignment QSGText::hAlign() const
1099 {
1100     Q_D(const QSGText);
1101     return d->hAlign;
1102 }
1103
1104 void QSGText::setHAlign(HAlignment align)
1105 {
1106     Q_D(QSGText);
1107     bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
1108     d->hAlignImplicit = false;
1109     if (d->setHAlign(align, forceAlign) && isComponentComplete())
1110         d->updateLayout();
1111 }
1112
1113 void QSGText::resetHAlign()
1114 {
1115     Q_D(QSGText);
1116     d->hAlignImplicit = true;
1117     if (d->determineHorizontalAlignment() && isComponentComplete())
1118         d->updateLayout();
1119 }
1120
1121 QSGText::HAlignment QSGText::effectiveHAlign() const
1122 {
1123     Q_D(const QSGText);
1124     QSGText::HAlignment effectiveAlignment = d->hAlign;
1125     if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
1126         switch (d->hAlign) {
1127         case QSGText::AlignLeft:
1128             effectiveAlignment = QSGText::AlignRight;
1129             break;
1130         case QSGText::AlignRight:
1131             effectiveAlignment = QSGText::AlignLeft;
1132             break;
1133         default:
1134             break;
1135         }
1136     }
1137     return effectiveAlignment;
1138 }
1139
1140 bool QSGTextPrivate::setHAlign(QSGText::HAlignment alignment, bool forceAlign)
1141 {
1142     Q_Q(QSGText);
1143     if (hAlign != alignment || forceAlign) {
1144         QSGText::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
1145         hAlign = alignment;
1146
1147         emit q->horizontalAlignmentChanged(hAlign);
1148         if (oldEffectiveHAlign != q->effectiveHAlign())
1149             emit q->effectiveHorizontalAlignmentChanged();
1150         return true;
1151     }
1152     return false;
1153 }
1154
1155 bool QSGTextPrivate::determineHorizontalAlignment()
1156 {
1157     Q_Q(QSGText);
1158     if (hAlignImplicit && q->isComponentComplete()) {
1159         bool alignToRight = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText;
1160         return setHAlign(alignToRight ? QSGText::AlignRight : QSGText::AlignLeft);
1161     }
1162     return false;
1163 }
1164
1165 void QSGTextPrivate::mirrorChange()
1166 {
1167     Q_Q(QSGText);
1168     if (q->isComponentComplete()) {
1169         if (!hAlignImplicit && (hAlign == QSGText::AlignRight || hAlign == QSGText::AlignLeft)) {
1170             updateLayout();
1171             emit q->effectiveHorizontalAlignmentChanged();
1172         }
1173     }
1174 }
1175
1176 QTextDocument *QSGTextPrivate::textDocument()
1177 {
1178     return doc;
1179 }
1180
1181 QSGText::VAlignment QSGText::vAlign() const
1182 {
1183     Q_D(const QSGText);
1184     return d->vAlign;
1185 }
1186
1187 void QSGText::setVAlign(VAlignment align)
1188 {
1189     Q_D(QSGText);
1190     if (d->vAlign == align)
1191         return;
1192
1193     d->vAlign = align;
1194     emit verticalAlignmentChanged(align);
1195 }
1196
1197 /*!
1198     \qmlproperty enumeration QtQuick2::Text::wrapMode
1199
1200     Set this property to wrap the text to the Text item's width.  The text will only
1201     wrap if an explicit width has been set.  wrapMode can be one of:
1202
1203     \list
1204     \o Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l paintedWidth will exceed a set width.
1205     \o Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l paintedWidth will exceed a set width.
1206     \o Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
1207     \o Text.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word.
1208     \endlist
1209 */
1210 QSGText::WrapMode QSGText::wrapMode() const
1211 {
1212     Q_D(const QSGText);
1213     return d->wrapMode;
1214 }
1215
1216 void QSGText::setWrapMode(WrapMode mode)
1217 {
1218     Q_D(QSGText);
1219     if (mode == d->wrapMode)
1220         return;
1221
1222     d->wrapMode = mode;
1223     d->updateLayout();
1224
1225     emit wrapModeChanged();
1226 }
1227
1228 /*!
1229     \qmlproperty int QtQuick2::Text::lineCount
1230
1231     Returns the number of lines visible in the text item.
1232
1233     This property is not supported for rich text.
1234
1235     \sa maximumLineCount
1236 */
1237 int QSGText::lineCount() const
1238 {
1239     Q_D(const QSGText);
1240     return d->lineCount;
1241 }
1242
1243 /*!
1244     \qmlproperty bool QtQuick2::Text::truncated
1245
1246     Returns true if the text has been truncated due to \l maximumLineCount
1247     or \l elide.
1248
1249     This property is not supported for rich text.
1250
1251     \sa maximumLineCount, elide
1252 */
1253 bool QSGText::truncated() const
1254 {
1255     Q_D(const QSGText);
1256     return d->truncated;
1257 }
1258
1259 /*!
1260     \qmlproperty int QtQuick2::Text::maximumLineCount
1261
1262     Set this property to limit the number of lines that the text item will show.
1263     If elide is set to Text.ElideRight, the text will be elided appropriately.
1264     By default, this is the value of the largest possible integer.
1265
1266     This property is not supported for rich text.
1267
1268     \sa lineCount, elide
1269 */
1270 int QSGText::maximumLineCount() const
1271 {
1272     Q_D(const QSGText);
1273     return d->maximumLineCount;
1274 }
1275
1276 void QSGText::setMaximumLineCount(int lines)
1277 {
1278     Q_D(QSGText);
1279
1280     d->maximumLineCountValid = lines==INT_MAX ? false : true;
1281     if (d->maximumLineCount != lines) {
1282         d->maximumLineCount = lines;
1283         d->updateLayout();
1284         emit maximumLineCountChanged();
1285     }
1286 }
1287
1288 void QSGText::resetMaximumLineCount()
1289 {
1290     Q_D(QSGText);
1291     setMaximumLineCount(INT_MAX);
1292     d->elidePos = QPointF();
1293     if (d->truncated != false) {
1294         d->truncated = false;
1295         emit truncatedChanged();
1296     }
1297 }
1298
1299 /*!
1300     \qmlproperty enumeration QtQuick2::Text::textFormat
1301
1302     The way the text property should be displayed.
1303
1304     Supported text formats are:
1305
1306     \list
1307     \o Text.AutoText (default)
1308     \o Text.PlainText
1309     \o Text.RichText
1310     \o Text.StyledText
1311     \endlist
1312
1313     If the text format is \c Text.AutoText the text element
1314     will automatically determine whether the text should be treated as
1315     rich text.  This determination is made using Qt::mightBeRichText().
1316
1317     Text.StyledText is an optimized format supporting some basic text
1318     styling markup, in the style of html 3.2:
1319
1320     \code
1321     <font size="4" color="#ff0000">font size and color</font>
1322     <b>bold</b>
1323     <i>italic</i>
1324     <br>
1325     &gt; &lt; &amp;
1326     \endcode
1327
1328     \c Text.StyledText parser is strict, requiring tags to be correctly nested.
1329
1330     \table
1331     \row
1332     \o
1333     \qml
1334 Column {
1335     Text {
1336         font.pointSize: 24
1337         text: "<b>Hello</b> <i>World!</i>"
1338     }
1339     Text {
1340         font.pointSize: 24
1341         textFormat: Text.RichText
1342         text: "<b>Hello</b> <i>World!</i>"
1343     }
1344     Text {
1345         font.pointSize: 24
1346         textFormat: Text.PlainText
1347         text: "<b>Hello</b> <i>World!</i>"
1348     }
1349 }
1350     \endqml
1351     \o \image declarative-textformat.png
1352     \endtable
1353 */
1354 QSGText::TextFormat QSGText::textFormat() const
1355 {
1356     Q_D(const QSGText);
1357     return d->format;
1358 }
1359
1360 void QSGText::setTextFormat(TextFormat format)
1361 {
1362     Q_D(QSGText);
1363     if (format == d->format)
1364         return;
1365     d->format = format;
1366     bool wasRich = d->richText;
1367     d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text));
1368
1369     if (!wasRich && d->richText && isComponentComplete()) {
1370         d->ensureDoc();
1371         d->doc->setText(d->text);
1372         d->richTextAsImage = QSGTextNode::isComplexRichText(d->doc);
1373     }
1374
1375     d->updateLayout();
1376
1377     emit textFormatChanged(d->format);
1378 }
1379
1380 /*!
1381     \qmlproperty enumeration QtQuick2::Text::elide
1382
1383     Set this property to elide parts of the text fit to the Text item's width.
1384     The text will only elide if an explicit width has been set.
1385
1386     This property cannot be used with rich text.
1387
1388     Eliding can be:
1389     \list
1390     \o Text.ElideNone  - the default
1391     \o Text.ElideLeft
1392     \o Text.ElideMiddle
1393     \o Text.ElideRight
1394     \endlist
1395
1396     If this property is set to Text.ElideRight, it can be used with multiline
1397     text. The text will only elide if maximumLineCount has been set.
1398
1399     If the text is a multi-length string, and the mode is not \c Text.ElideNone,
1400     the first string that fits will be used, otherwise the last will be elided.
1401
1402     Multi-length strings are ordered from longest to shortest, separated by the
1403     Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}).
1404 */
1405 QSGText::TextElideMode QSGText::elideMode() const
1406 {
1407     Q_D(const QSGText);
1408     return d->elideMode;
1409 }
1410
1411 void QSGText::setElideMode(QSGText::TextElideMode mode)
1412 {
1413     Q_D(QSGText);
1414     if (mode == d->elideMode)
1415         return;
1416
1417     d->elideMode = mode;
1418     d->updateLayout();
1419
1420     emit elideModeChanged(d->elideMode);
1421 }
1422
1423 /*! \internal */
1424 QRectF QSGText::boundingRect() const
1425 {
1426     Q_D(const QSGText);
1427
1428     QRect rect = d->layedOutTextRect;
1429     if (d->style != Normal)
1430         rect.adjust(-1, 0, 1, 2);
1431
1432     // Could include font max left/right bearings to either side of rectangle.
1433
1434     int h = height();
1435     switch (d->vAlign) {
1436     case AlignTop:
1437         break;
1438     case AlignBottom:
1439         rect.moveTop(h - rect.height());
1440         break;
1441     case AlignVCenter:
1442         rect.moveTop((h - rect.height()) / 2);
1443         break;
1444     }
1445
1446     return QRectF(rect);
1447 }
1448
1449 /*! \internal */
1450 void QSGText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
1451 {
1452     Q_D(QSGText);
1453     if ((!d->internalWidthUpdate && newGeometry.width() != oldGeometry.width())
1454             && (d->wrapMode != QSGText::NoWrap
1455                 || d->elideMode != QSGText::ElideNone
1456                 || d->hAlign != QSGText::AlignLeft)) {
1457         if ((d->singleline || d->maximumLineCountValid) && d->elideMode != QSGText::ElideNone && widthValid()) {
1458             // We need to re-elide
1459             d->updateLayout();
1460         } else {
1461             // We just need to re-layout
1462             d->updateSize();
1463         }
1464     }
1465
1466     QSGItem::geometryChanged(newGeometry, oldGeometry);
1467 }
1468
1469 QSGNode *QSGText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
1470 {
1471     Q_UNUSED(data);
1472     Q_D(QSGText);
1473
1474     if (d->text.isEmpty()) {
1475         delete oldNode;
1476         return 0;
1477     }
1478
1479     QRectF bounds = boundingRect();
1480
1481     // We need to make sure the layout is done in the current thread
1482     if (d->layoutThread != QThread::currentThread())
1483         d->updateLayout();
1484
1485     // XXX todo - some styled text can be done by the QSGTextNode
1486     if (d->richTextAsImage || d->cacheAllTextAsImage || (!QSGDistanceFieldGlyphCache::distanceFieldEnabled() && d->style != Normal)) {
1487         bool wasDirty = d->textureImageCacheDirty;
1488         d->textureImageCacheDirty = false;
1489
1490         if (d->imageCache.isNull()) {
1491             delete oldNode;
1492             return 0;
1493         }
1494
1495         QSGImageNode *node = 0;
1496         if (!oldNode || d->nodeType != QSGTextPrivate::NodeIsTexture) {
1497             delete oldNode;
1498             node = QSGItemPrivate::get(this)->sceneGraphContext()->createImageNode();
1499             d->texture = new QSGPlainTexture();
1500             wasDirty = true;
1501             d->nodeType = QSGTextPrivate::NodeIsTexture;
1502         } else {
1503             node = static_cast<QSGImageNode *>(oldNode);
1504             Q_ASSERT(d->texture);
1505         }
1506
1507         if (wasDirty) {
1508             qobject_cast<QSGPlainTexture *>(d->texture)->setImage(d->imageCache.toImage());
1509             node->setTexture(0);
1510             node->setTexture(d->texture);
1511         }
1512
1513         node->setTargetRect(QRectF(bounds.x(), bounds.y(), d->imageCache.width(), d->imageCache.height()));
1514         node->setSourceRect(QRectF(0, 0, 1, 1));
1515         node->setHorizontalWrapMode(QSGTexture::ClampToEdge);
1516         node->setVerticalWrapMode(QSGTexture::ClampToEdge);
1517         node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that..
1518         node->update();
1519
1520         return node;
1521
1522     } else {
1523         QSGTextNode *node = 0;
1524         if (!oldNode || d->nodeType != QSGTextPrivate::NodeIsText) {
1525             delete oldNode;
1526             node = new QSGTextNode(QSGItemPrivate::get(this)->sceneGraphContext());
1527             d->nodeType = QSGTextPrivate::NodeIsText;
1528         } else {
1529             node = static_cast<QSGTextNode *>(oldNode);
1530         }
1531
1532         node->deleteContent();
1533         node->setMatrix(QMatrix4x4());
1534
1535         if (d->richText) {
1536
1537             d->ensureDoc();
1538             node->addTextDocument(bounds.topLeft(), d->doc, QColor(), d->style, d->styleColor);
1539
1540         } else {
1541             node->addTextLayout(QPoint(0, bounds.y()), &d->layout, d->color, d->style, d->styleColor);
1542         }
1543
1544         return node;
1545     }
1546 }
1547
1548 bool QSGText::event(QEvent *e)
1549 {
1550     Q_D(QSGText);
1551     if (e->type() == QEvent::User) {
1552         d->checkImageCache();
1553         return true;
1554     } else {
1555         return QSGImplicitSizeItem::event(e);
1556     }
1557 }
1558
1559 /*!
1560     \qmlproperty real QtQuick2::Text::paintedWidth
1561
1562     Returns the width of the text, including width past the width
1563     which is covered due to insufficient wrapping if WrapMode is set.
1564 */
1565 qreal QSGText::paintedWidth() const
1566 {
1567     Q_D(const QSGText);
1568     return d->paintedSize.width();
1569 }
1570
1571 /*!
1572     \qmlproperty real QtQuick2::Text::paintedHeight
1573
1574     Returns the height of the text, including height past the height
1575     which is covered due to there being more text than fits in the set height.
1576 */
1577 qreal QSGText::paintedHeight() const
1578 {
1579     Q_D(const QSGText);
1580     return d->paintedSize.height();
1581 }
1582
1583 /*!
1584     \qmlproperty real QtQuick2::Text::lineHeight
1585
1586     Sets the line height for the text.
1587     The value can be in pixels or a multiplier depending on lineHeightMode.
1588
1589     The default value is a multiplier of 1.0.
1590     The line height must be a positive value.
1591 */
1592 qreal QSGText::lineHeight() const
1593 {
1594     Q_D(const QSGText);
1595     return d->lineHeight;
1596 }
1597
1598 void QSGText::setLineHeight(qreal lineHeight)
1599 {
1600     Q_D(QSGText);
1601
1602     if ((d->lineHeight == lineHeight) || (lineHeight < 0.0))
1603         return;
1604
1605     d->lineHeight = lineHeight;
1606     d->updateLayout();
1607     emit lineHeightChanged(lineHeight);
1608 }
1609
1610 /*!
1611     \qmlproperty enumeration QtQuick2::Text::lineHeightMode
1612
1613     This property determines how the line height is specified.
1614     The possible values are:
1615
1616     \list
1617     \o Text.ProportionalHeight (default) - this sets the spacing proportional to the
1618        line (as a multiplier). For example, set to 2 for double spacing.
1619     \o Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
1620     \endlist
1621 */
1622 QSGText::LineHeightMode QSGText::lineHeightMode() const
1623 {
1624     Q_D(const QSGText);
1625     return d->lineHeightMode;
1626 }
1627
1628 void QSGText::setLineHeightMode(LineHeightMode mode)
1629 {
1630     Q_D(QSGText);
1631     if (mode == d->lineHeightMode)
1632         return;
1633
1634     d->lineHeightMode = mode;
1635     d->updateLayout();
1636
1637     emit lineHeightModeChanged(mode);
1638 }
1639
1640 /*!
1641     Returns the number of resources (images) that are being loaded asynchronously.
1642 */
1643 int QSGText::resourcesLoading() const
1644 {
1645     Q_D(const QSGText);
1646     return d->doc ? d->doc->resourcesLoading() : 0;
1647 }
1648
1649 /*! \internal */
1650 void QSGText::componentComplete()
1651 {
1652     Q_D(QSGText);
1653     QSGItem::componentComplete();
1654     if (d->updateOnComponentComplete) {
1655         d->updateOnComponentComplete = false;
1656         if (d->richText) {
1657             d->ensureDoc();
1658             d->doc->setText(d->text);
1659             d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
1660             d->richTextAsImage = QSGTextNode::isComplexRichText(d->doc);
1661         } else {
1662             d->rightToLeftText = d->text.isRightToLeft();
1663         }
1664         d->determineHorizontalAlignment();
1665         d->updateLayout();
1666     }
1667 }
1668
1669 /*!  \internal */
1670 void QSGText::mousePressEvent(QGraphicsSceneMouseEvent *event)
1671 {
1672     Q_D(QSGText);
1673
1674     if (!d->richText || !d->doc || d->doc->documentLayout()->anchorAt(event->pos()).isEmpty()) {
1675         event->setAccepted(false);
1676         d->activeLink.clear();
1677     } else {
1678         d->activeLink = d->doc->documentLayout()->anchorAt(event->pos());
1679     }
1680
1681     // ### may malfunction if two of the same links are clicked & dragged onto each other)
1682
1683     if (!event->isAccepted())
1684         QSGItem::mousePressEvent(event);
1685
1686 }
1687
1688 /*! \internal */
1689 void QSGText::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
1690 {
1691     Q_D(QSGText);
1692
1693         // ### confirm the link, and send a signal out
1694     if (d->richText && d->doc && d->activeLink == d->doc->documentLayout()->anchorAt(event->pos()))
1695         emit linkActivated(d->activeLink);
1696     else
1697         event->setAccepted(false);
1698
1699     if (!event->isAccepted())
1700         QSGItem::mouseReleaseEvent(event);
1701 }
1702
1703 QT_END_NAMESPACE
1704
1705 #include "qsgtext.moc"