Fix updating text color in Text element
[profile/ivi/qtdeclarative.git] / src / quick / items / qquicktext.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquicktext_p.h"
43 #include "qquicktext_p_p.h"
44
45 #include <QtQuick/private/qsgcontext_p.h>
46 #include <private/qsgadaptationlayer_p.h>
47 #include "qquicktextnode_p.h"
48 #include "qquickimage_p_p.h"
49 #include <QtQuick/private/qsgtexture_p.h>
50
51 #include <QtDeclarative/qdeclarativeinfo.h>
52 #include <QtGui/qevent.h>
53 #include <QtGui/qabstracttextdocumentlayout.h>
54 #include <QtGui/qpainter.h>
55 #include <QtGui/qtextdocument.h>
56 #include <QtGui/qtextobject.h>
57 #include <QtGui/qtextcursor.h>
58 #include <QtGui/qguiapplication.h>
59 #include <QtGui/qinputpanel.h>
60
61 #include <private/qtextengine_p.h>
62 #include <private/qdeclarativestyledtext_p.h>
63 #include <QtQuick/private/qdeclarativepixmapcache_p.h>
64
65 #include <qmath.h>
66 #include <limits.h>
67
68 QT_BEGIN_NAMESPACE
69
70 extern Q_GUI_EXPORT bool qt_applefontsmoothing_enabled;
71
72 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
73 DEFINE_BOOL_CONFIG_OPTION(enableImageCache, QML_ENABLE_TEXT_IMAGE_CACHE);
74
75 const QChar QQuickTextPrivate::elideChar = QChar(0x2026);
76
77 QQuickTextPrivate::QQuickTextPrivate()
78 : color((QRgb)0), style(QQuickText::Normal), hAlign(QQuickText::AlignLeft),
79   vAlign(QQuickText::AlignTop), elideMode(QQuickText::ElideNone),
80   format(QQuickText::AutoText), wrapMode(QQuickText::NoWrap), lineHeight(1),
81   lineHeightMode(QQuickText::ProportionalHeight), lineCount(1), maximumLineCount(INT_MAX),
82   maximumLineCountValid(false), fontSizeMode(QQuickText::FixedSize), minimumPixelSize(12),
83   minimumPointSize(12), imageCache(0), texture(0),
84   imageCacheDirty(false), updateOnComponentComplete(true),
85   richText(false), styledText(false), singleline(false), cacheAllTextAsImage(true),
86   disableDistanceField(false), internalWidthUpdate(false),
87   requireImplicitWidth(false), truncated(false), hAlignImplicit(true), rightToLeftText(false),
88   layoutTextElided(false), richTextAsImage(false), textureImageCacheDirty(false), textHasChanged(true),
89   needToUpdateLayout(false), naturalWidth(0), doc(0), elideLayout(0), textLine(0), nodeType(NodeIsNull),
90   updateType(UpdatePaintNode), nbActiveDownloads(0)
91
92 #if defined(Q_OS_MAC)
93 , layoutThread(0), paintingThread(0)
94 #endif
95 {
96     cacheAllTextAsImage = enableImageCache();
97     disableDistanceField = qmlDisableDistanceField();
98 }
99
100 void QQuickTextPrivate::init()
101 {
102     Q_Q(QQuickText);
103     q->setAcceptedMouseButtons(Qt::LeftButton);
104     q->setFlag(QQuickItem::ItemHasContents);
105 }
106
107 QQuickTextDocumentWithImageResources::QQuickTextDocumentWithImageResources(QQuickItem *parent)
108 : QTextDocument(parent), outstanding(0)
109 {
110     setUndoRedoEnabled(false);
111     documentLayout()->registerHandler(QTextFormat::ImageObject, this);
112 }
113
114 QQuickTextDocumentWithImageResources::~QQuickTextDocumentWithImageResources()
115 {
116     if (!m_resources.isEmpty())
117         qDeleteAll(m_resources);
118 }
119
120 QVariant QQuickTextDocumentWithImageResources::loadResource(int type, const QUrl &name)
121 {
122     QDeclarativeContext *context = qmlContext(parent());
123     QUrl url = m_baseUrl.resolved(name);
124
125     if (type == QTextDocument::ImageResource) {
126         QDeclarativePixmap *p = loadPixmap(context, url);
127         return p->image();
128     }
129
130     return QTextDocument::loadResource(type,url); // The *resolved* URL
131 }
132
133 void QQuickTextDocumentWithImageResources::requestFinished()
134 {
135     outstanding--;
136     if (outstanding == 0) {
137         markContentsDirty(0, characterCount());
138         emit imagesLoaded();
139     }
140 }
141
142 void QQuickTextDocumentWithImageResources::clear()
143 {
144     clearResources();
145
146     QTextDocument::clear();
147 }
148
149
150 QSizeF QQuickTextDocumentWithImageResources::intrinsicSize(
151         QTextDocument *, int, const QTextFormat &format)
152 {
153     if (format.isImageFormat()) {
154         QTextImageFormat imageFormat = format.toImageFormat();
155
156         const bool hasWidth = imageFormat.hasProperty(QTextFormat::ImageWidth);
157         const int width = qRound(imageFormat.width());
158         const bool hasHeight = imageFormat.hasProperty(QTextFormat::ImageHeight);
159         const int height = qRound(imageFormat.height());
160
161         QSizeF size(width, height);
162         if (!hasWidth || !hasHeight) {
163             QDeclarativeContext *context = qmlContext(parent());
164             QUrl url = m_baseUrl.resolved(QUrl(imageFormat.name()));
165
166             QDeclarativePixmap *p = loadPixmap(context, url);
167             if (!p->isReady()) {
168                 if (!hasWidth)
169                     size.setWidth(16);
170                 if (!hasHeight)
171                     size.setHeight(16);
172                 return size;
173             }
174             QSize implicitSize = p->implicitSize();
175
176             if (!hasWidth) {
177                 if (!hasHeight)
178                     size.setWidth(implicitSize.width());
179                 else
180                     size.setWidth(qRound(height * (implicitSize.width() / (qreal) implicitSize.height())));
181             }
182             if (!hasHeight) {
183                 if (!hasWidth)
184                     size.setHeight(implicitSize.height());
185                 else
186                     size.setHeight(qRound(width * (implicitSize.height() / (qreal) implicitSize.width())));
187             }
188         }
189         return size;
190     }
191     return QSizeF();
192 }
193
194 void QQuickTextDocumentWithImageResources::drawObject(
195         QPainter *, const QRectF &, QTextDocument *, int, const QTextFormat &)
196 {
197 }
198
199 QImage QQuickTextDocumentWithImageResources::image(const QTextImageFormat &format)
200 {
201     QDeclarativeContext *context = qmlContext(parent());
202     QUrl url = m_baseUrl.resolved(QUrl(format.name()));
203
204     QDeclarativePixmap *p = loadPixmap(context, url);
205     return p->image();
206 }
207
208 void QQuickTextDocumentWithImageResources::setBaseUrl(const QUrl &url, bool clear)
209 {
210     m_baseUrl = url;
211     if (clear) {
212         clearResources();
213         markContentsDirty(0, characterCount());
214     }
215 }
216
217 QDeclarativePixmap *QQuickTextDocumentWithImageResources::loadPixmap(
218         QDeclarativeContext *context, const QUrl &url)
219 {
220
221     QHash<QUrl, QDeclarativePixmap *>::Iterator iter = m_resources.find(url);
222
223     if (iter == m_resources.end()) {
224         QDeclarativePixmap *p = new QDeclarativePixmap(context->engine(), url);
225         iter = m_resources.insert(url, p);
226
227         if (p->isLoading()) {
228             p->connectFinished(this, SLOT(requestFinished()));
229             outstanding++;
230         }
231     }
232
233     QDeclarativePixmap *p = *iter;
234     if (p->isError()) {
235         if (!errors.contains(url)) {
236             errors.insert(url);
237             qmlInfo(parent()) << p->error();
238         }
239     }
240     return p;
241 }
242
243 void QQuickTextDocumentWithImageResources::clearResources()
244 {
245     foreach (QDeclarativePixmap *pixmap, m_resources)
246         pixmap->clear(this);
247     qDeleteAll(m_resources);
248     m_resources.clear();
249     outstanding = 0;
250 }
251
252 void QQuickTextDocumentWithImageResources::setText(const QString &text)
253 {
254     clearResources();
255
256 #ifndef QT_NO_TEXTHTMLPARSER
257     setHtml(text);
258 #else
259     setPlainText(text);
260 #endif
261 }
262
263 QSet<QUrl> QQuickTextDocumentWithImageResources::errors;
264
265 QQuickTextPrivate::~QQuickTextPrivate()
266 {
267     delete elideLayout;
268     delete textLine; textLine = 0;
269     delete imageCache;
270     qDeleteAll(imgTags);
271     imgTags.clear();
272 }
273
274 qreal QQuickTextPrivate::getImplicitWidth() const
275 {
276     if (!requireImplicitWidth) {
277         // We don't calculate implicitWidth unless it is required.
278         // We need to force a size update now to ensure implicitWidth is calculated
279         QQuickTextPrivate *me = const_cast<QQuickTextPrivate*>(this);
280         me->requireImplicitWidth = true;
281         me->updateSize();
282     }
283     return implicitWidth;
284 }
285
286 void QQuickText::q_imagesLoaded()
287 {
288     Q_D(QQuickText);
289     d->updateLayout();
290 }
291
292 void QQuickTextPrivate::updateLayout()
293 {
294     Q_Q(QQuickText);
295     if (!q->isComponentComplete()) {
296         updateOnComponentComplete = true;
297         return;
298     }
299     updateOnComponentComplete = false;
300     layoutTextElided = false;
301
302     if (!visibleImgTags.isEmpty())
303         visibleImgTags.clear();
304     needToUpdateLayout = false;
305
306     // Setup instance of QTextLayout for all cases other than richtext
307     if (!richText) {
308         if (textHasChanged) {
309             if (styledText && !text.isEmpty()) {
310                 QDeclarativeStyledText::parse(text, layout, imgTags, qmlContext(q), !maximumLineCountValid);
311             } else {
312                 layout.clearAdditionalFormats();
313                 QString tmp = text;
314                 tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
315                 layout.setText(tmp);
316             }
317             textHasChanged = false;
318         }
319     } else {
320         ensureDoc();
321         QTextBlockFormat::LineHeightTypes type;
322         type = lineHeightMode == QQuickText::FixedHeight ? QTextBlockFormat::FixedHeight : QTextBlockFormat::ProportionalHeight;
323         QTextBlockFormat blockFormat;
324         blockFormat.setLineHeight((lineHeightMode == QQuickText::FixedHeight ? lineHeight : lineHeight * 100), type);
325         for (QTextBlock it = doc->begin(); it != doc->end(); it = it.next()) {
326             QTextCursor cursor(it);
327             cursor.mergeBlockFormat(blockFormat);
328         }
329     }
330
331     updateSize();
332
333     if (needToUpdateLayout) {
334         needToUpdateLayout = false;
335         textHasChanged = true;
336         updateLayout();
337     }
338 }
339
340 void QQuickText::imageDownloadFinished()
341 {
342     Q_D(QQuickText);
343
344     (d->nbActiveDownloads)--;
345
346     // when all the remote images have been downloaded,
347     // if one of the sizes was not specified at parsing time
348     // we use the implicit size from pixmapcache and re-layout.
349
350     if (d->nbActiveDownloads == 0) {
351         bool needToUpdateLayout = false;
352         foreach (QDeclarativeStyledTextImgTag *img, d->visibleImgTags) {
353             if (!img->size.isValid()) {
354                 img->size = img->pix->implicitSize();
355                 needToUpdateLayout = true;
356             }
357         }
358
359         if (needToUpdateLayout) {
360             d->textHasChanged = true;
361             d->updateLayout();
362         } else {
363             d->updateType = QQuickTextPrivate::UpdatePaintNode;
364             update();
365         }
366     }
367 }
368
369 void QQuickTextPrivate::updateSize()
370 {
371     Q_Q(QQuickText);
372
373     if (!q->isComponentComplete()) {
374         updateOnComponentComplete = true;
375         return;
376     }
377
378     if (!requireImplicitWidth) {
379         emit q->implicitWidthChanged();
380         // if the implicitWidth is used, then updateSize() has already been called (recursively)
381         if (requireImplicitWidth)
382             return;
383     }
384
385     invalidateImageCache();
386
387     QFontMetrics fm(font);
388     if (text.isEmpty()) {
389         qreal fontHeight = fm.height();
390         q->setImplicitSize(0, fontHeight);
391         paintedSize = QSize(0, fontHeight);
392         emit q->paintedSizeChanged();
393         updateType = UpdatePaintNode;
394         q->update();
395         return;
396     }
397
398     int dy = q->height();
399     QSize size(0, 0);
400
401 #if defined(Q_OS_MAC)
402     layoutThread = QThread::currentThread();
403 #endif
404
405     //setup instance of QTextLayout for all cases other than richtext
406     if (!richText) {
407         QRect textRect = setupTextLayout();
408         layedOutTextRect = textRect;
409         size = textRect.size();
410         dy -= size.height();
411     } else {
412         singleline = false; // richtext can't elide or be optimized for single-line case
413         ensureDoc();
414         doc->setDefaultFont(font);
415         QQuickText::HAlignment horizontalAlignment = q->effectiveHAlign();
416         if (rightToLeftText) {
417             if (horizontalAlignment == QQuickText::AlignLeft)
418                 horizontalAlignment = QQuickText::AlignRight;
419             else if (horizontalAlignment == QQuickText::AlignRight)
420                 horizontalAlignment = QQuickText::AlignLeft;
421         }
422         QTextOption option;
423         option.setAlignment((Qt::Alignment)int(horizontalAlignment | vAlign));
424         option.setWrapMode(QTextOption::WrapMode(wrapMode));
425         if (!cacheAllTextAsImage && !richTextAsImage && !disableDistanceField)
426             option.setUseDesignMetrics(true);
427         doc->setDefaultTextOption(option);
428         if (requireImplicitWidth && q->widthValid()) {
429             doc->setTextWidth(-1);
430             naturalWidth = doc->idealWidth();
431         }
432         if (wrapMode != QQuickText::NoWrap && q->widthValid())
433             doc->setTextWidth(q->width());
434         else
435             doc->setTextWidth(doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug)
436         dy -= (int)doc->size().height();
437         QSize dsize = doc->size().toSize();
438         layedOutTextRect = QRect(QPoint(0,0), dsize);
439         size = QSize(int(doc->idealWidth()),dsize.height());
440     }
441     int yoff = 0;
442
443     if (q->heightValid()) {
444         if (vAlign == QQuickText::AlignBottom)
445             yoff = dy;
446         else if (vAlign == QQuickText::AlignVCenter)
447             yoff = dy/2;
448     }
449     q->setBaselineOffset(fm.ascent() + yoff);
450
451     //### need to comfirm cost of always setting these for richText
452     internalWidthUpdate = true;
453     qreal iWidth = -1;
454     if (!q->widthValid())
455         iWidth = size.width();
456     else if (requireImplicitWidth)
457         iWidth = naturalWidth;
458     if (iWidth > -1)
459         q->setImplicitSize(iWidth, size.height());
460     internalWidthUpdate = false;
461
462     if (iWidth == -1)
463         q->setImplicitHeight(size.height());
464     if (paintedSize != size) {
465         paintedSize = size;
466         emit q->paintedSizeChanged();
467     }
468     updateType = UpdatePaintNode;
469     q->update();
470 }
471
472 QQuickTextLine::QQuickTextLine()
473     : QObject(), m_line(0), m_height(0)
474 {
475 }
476
477 void QQuickTextLine::setLine(QTextLine *line)
478 {
479     m_line = line;
480 }
481
482 void QQuickTextLine::setLineOffset(int offset)
483 {
484     m_lineOffset = offset;
485 }
486
487 int QQuickTextLine::number() const
488 {
489     if (m_line)
490         return m_line->lineNumber() + m_lineOffset;
491     return 0;
492 }
493
494 qreal QQuickTextLine::width() const
495 {
496     if (m_line)
497         return m_line->width();
498     return 0;
499 }
500
501 void QQuickTextLine::setWidth(qreal width)
502 {
503     if (m_line)
504         m_line->setLineWidth(width);
505 }
506
507 qreal QQuickTextLine::height() const
508 {
509     if (m_height)
510         return m_height;
511     if (m_line)
512         return m_line->height();
513     return 0;
514 }
515
516 void QQuickTextLine::setHeight(qreal height)
517 {
518     if (m_line)
519         m_line->setPosition(QPointF(m_line->x(), m_line->y() - m_line->height() + height));
520     m_height = height;
521 }
522
523 qreal QQuickTextLine::x() const
524 {
525     if (m_line)
526         return m_line->x();
527     return 0;
528 }
529
530 void QQuickTextLine::setX(qreal x)
531 {
532     if (m_line)
533         m_line->setPosition(QPointF(x, m_line->y()));
534 }
535
536 qreal QQuickTextLine::y() const
537 {
538     if (m_line)
539         return m_line->y();
540     return 0;
541 }
542
543 void QQuickTextLine::setY(qreal y)
544 {
545     if (m_line)
546         m_line->setPosition(QPointF(m_line->x(), y));
547 }
548
549 void QQuickText::doLayout()
550 {
551     Q_D(QQuickText);
552     d->updateSize();
553 }
554
555 bool QQuickTextPrivate::isLineLaidOutConnected()
556 {
557     static int idx = this->signalIndex("lineLaidOut(QQuickTextLine*)");
558     return this->isSignalConnected(idx);
559 }
560
561 void QQuickTextPrivate::setupCustomLineGeometry(QTextLine &line, qreal &height, int lineOffset)
562 {
563     Q_Q(QQuickText);
564
565 #if defined(Q_OS_MAC)
566     if (QThread::currentThread() != paintingThread) {
567         if (!line.lineNumber())
568             linesRects.clear();
569 #endif
570
571         if (!textLine)
572             textLine = new QQuickTextLine;
573         textLine->setLine(&line);
574         textLine->setY(height);
575         textLine->setHeight(0);
576         textLine->setLineOffset(lineOffset);
577
578         // use the text item's width by default if it has one and wrap is on
579         if (q->widthValid() && q->wrapMode() != QQuickText::NoWrap)
580             textLine->setWidth(q->width());
581         else
582             textLine->setWidth(INT_MAX);
583         if (lineHeight != 1.0)
584             textLine->setHeight((lineHeightMode == QQuickText::FixedHeight) ? lineHeight : line.height() * lineHeight);
585
586         emit q->lineLaidOut(textLine);
587
588         height += textLine->height();
589
590 #if defined(Q_OS_MAC)
591         linesRects << QRectF(textLine->x(), textLine->y(), textLine->width(), textLine->height());
592
593     } else {
594         if (line.lineNumber() < linesRects.count()) {
595             QRectF r = linesRects.at(line.lineNumber());
596             line.setLineWidth(r.width());
597             line.setPosition(r.topLeft());
598         }
599     }
600 #endif
601 }
602
603 /*!
604     Lays out the QQuickTextPrivate::layout QTextLayout in the constraints of the QQuickText.
605
606     Returns the size of the final text.  This can be used to position the text vertically (the text is
607     already absolutely positioned horizontally).
608 */
609
610 QRect QQuickTextPrivate::setupTextLayout()
611 {
612     Q_Q(QQuickText);
613     layout.setCacheEnabled(true);
614
615     QTextOption textOption = layout.textOption();
616     textOption.setAlignment(Qt::Alignment(q->effectiveHAlign()));
617     textOption.setWrapMode(QTextOption::WrapMode(wrapMode));
618     if (!cacheAllTextAsImage && !richTextAsImage && !disableDistanceField)
619         textOption.setUseDesignMetrics(true);
620     layout.setTextOption(textOption);
621     layout.setFont(font);
622
623     if ((q->widthValid() && q->width() <= 0. && elideMode != QQuickText::ElideNone)
624             || (q->heightValid() && q->height() <= 0. && wrapMode != QQuickText::NoWrap && elideMode == QQuickText::ElideRight)) {
625         // we are elided and we have a zero width or height
626         if (!truncated) {
627             truncated = true;
628             emit q->truncatedChanged();
629         }
630         if (lineCount) {
631             lineCount = 0;
632             emit q->lineCountChanged();
633         }
634
635         if (requireImplicitWidth) {
636             // Layout to determine the implicit width.
637             layout.beginLayout();
638
639             for (int i = 0; i < maximumLineCount; ++i) {
640                 QTextLine line = layout.createLine();
641                 if (!line.isValid())
642                     break;
643             }
644             layout.endLayout();
645             naturalWidth = layout.maximumWidth();
646             layout.clearLayout();
647         }
648
649         QFontMetrics fm(font);
650         qreal height = (lineHeightMode == QQuickText::FixedHeight) ? lineHeight : fm.height() * lineHeight;
651         return QRect(0, 0, 0, height);
652     }
653
654     const int lineWidth = q->widthValid() ? q->width() : INT_MAX;
655     const bool customLayout = isLineLaidOutConnected();
656     const bool wasTruncated = truncated;
657
658     const bool singlelineElide = !styledText && elideMode != QQuickText::ElideNone && q->widthValid();
659     const bool multilineElide = !styledText
660             && elideMode == QQuickText::ElideRight
661             && q->widthValid()
662             && (q->heightValid() || maximumLineCountValid);
663     const bool canWrap = wrapMode != QQuickText::NoWrap && q->widthValid();
664
665     const bool horizontalFit = fontSizeMode & QQuickText::HorizontalFit && q->widthValid();
666     const bool verticalFit = fontSizeMode & QQuickText::VerticalFit
667             && (q->heightValid() || (maximumLineCountValid && canWrap));
668     const bool pixelSize = font.pixelSize() != -1;
669     const QString layoutText = layout.text();
670
671     int largeFont = pixelSize ? font.pixelSize() : font.pointSize();
672     int smallFont = fontSizeMode != QQuickText::FixedSize
673             ? qMin(pixelSize ? minimumPixelSize : minimumPointSize, largeFont)
674             : largeFont;
675     int scaledFontSize = largeFont;
676
677     QRectF br;
678
679     QFont scaledFont = font;
680
681     QTextLine line;
682     int visibleCount = 0;
683     bool elide;
684     qreal height = 0;
685     QString elideText;
686     bool once = true;
687
688     naturalWidth = 0;
689
690     do {
691         if (!once) {
692             if (pixelSize)
693                 scaledFont.setPixelSize(scaledFontSize);
694             else
695                 scaledFont.setPointSize(scaledFontSize);
696             layout.setFont(scaledFont);
697         }
698         layout.beginLayout();
699
700         bool wrapped = false;
701         bool truncateHeight = false;
702         truncated = false;
703         elide = false;
704         int characterCount = 0;
705         int unwrappedLineCount = 1;
706         height = 0;
707         br = QRectF();
708         line = layout.createLine();
709         for (visibleCount = 1; ; ++visibleCount) {
710             qreal preLayoutHeight = height;
711
712             if (customLayout) {
713                 setupCustomLineGeometry(line, height);
714             } else {
715                 setLineGeometry(line, lineWidth, height);
716             }
717
718             if (multilineElide && height > q->height() && visibleCount > 1) {
719                 truncated = true;
720                 truncateHeight = true;
721                 height = preLayoutHeight;
722
723                 elide = true;
724                 characterCount = line.textStart() + line.textLength();
725
726                 QTextLine previousLine = layout.lineAt(visibleCount - 2);
727                 elideText = layoutText.mid(previousLine.textStart(), previousLine.textLength());
728                 if (layoutText.at(line.textStart() - 1) != QChar::LineSeparator) {
729                     line.setLineWidth(INT_MAX);
730                     elideText += layoutText.mid(line.textStart(), line.textLength());
731                 } else {
732                     elideText[elideText.length() - 1] = elideChar;
733                 }
734                 line.setLineWidth(0);
735                 line.setPosition(QPointF(FLT_MAX, FLT_MAX));
736                 line = previousLine;
737                 --visibleCount;
738                 height -= (lineHeightMode == QQuickText::FixedHeight) ? lineHeight : previousLine.height() * lineHeight;
739                 break;
740             }
741
742             QTextLine nextLine = layout.createLine();
743             if (!nextLine.isValid()) {
744                 characterCount = line.textStart() + line.textLength();
745                 if (singlelineElide && visibleCount == 1 && line.naturalTextWidth() > lineWidth) {
746                     truncated = true;
747                     height = preLayoutHeight;
748                     elide = true;
749                     elideText = layoutText.mid(line.textStart(), line.textLength());
750                 } else {
751                     br = br.united(line.naturalTextRect());
752                 }
753                 break;
754             } else {
755                 const bool wrappedLine = layoutText.at(nextLine.textStart() - 1) != QChar::LineSeparator;
756                 wrapped |= wrappedLine;
757
758                 if (!wrappedLine)
759                     ++unwrappedLineCount;
760
761                 if (visibleCount == maximumLineCount) {
762                     truncated = true;
763                     characterCount = nextLine.textStart() + nextLine.textLength();
764
765                     if (multilineElide) {
766                         height = preLayoutHeight;
767                         elide = true;
768                         elideText = layoutText.mid(line.textStart(), line.textLength());
769                         if (wrappedLine) {
770                             nextLine.setLineWidth(INT_MAX);
771                             elideText += layoutText.mid(nextLine.textStart(), nextLine.textLength());
772                         } else {
773                             elideText[elideText.length() - 1] = elideChar;
774                         }
775                         elideText += elideChar;
776                     } else {
777                         br = br.united(line.naturalTextRect());
778                     }
779                     nextLine.setLineWidth(0);
780                     nextLine.setPosition(QPointF(FLT_MAX, FLT_MAX));
781                     break;
782                 }
783             }
784             br = br.united(line.naturalTextRect());
785             line = nextLine;
786         }
787
788         layout.endLayout();
789
790         if (once) {
791             naturalWidth = layout.maximumWidth();
792             once = false;
793
794             if (requireImplicitWidth
795                     && characterCount < layoutText.length()
796                     && unwrappedLineCount < maximumLineCount) {
797                 // Use a new layout to get the maximum width for the remaining text.  Using a
798                 // different layout excludes the truncated text from rendering.
799                 QTextLayout widthLayout(layoutText.mid(characterCount), scaledFont);
800                 widthLayout.setTextOption(layout.textOption());
801
802                 for (; unwrappedLineCount <= maximumLineCount; ++unwrappedLineCount) {
803                     QTextLine line = widthLayout.createLine();
804                     if (!line.isValid())
805                         break;
806                 }
807                 naturalWidth = qMax(naturalWidth, widthLayout.maximumWidth());
808             }
809         }
810
811         QRectF unelidedRect = br.united(line.naturalTextRect());
812
813         if (horizontalFit) {
814             if (unelidedRect.width() > lineWidth || (!verticalFit && wrapped)) {
815                 largeFont = scaledFontSize - 1;
816                 scaledFontSize = (smallFont + largeFont) / 2;
817                 if (smallFont > largeFont)
818                     break;
819                 continue;
820             } else if (!verticalFit) {
821                 smallFont = scaledFontSize;
822                 scaledFontSize = (smallFont + largeFont + 1) / 2;
823                 if (smallFont == largeFont)
824                     break;
825             }
826         }
827
828         if (verticalFit) {
829             if (truncateHeight || (q->heightValid() && unelidedRect.height() > q->height())) {
830                 largeFont = scaledFontSize - 1;
831                 scaledFontSize = (smallFont + largeFont + 1) / 2;
832                 if (smallFont > largeFont)
833                     break;
834             } else {
835                 smallFont = scaledFontSize;
836                 scaledFontSize = (smallFont + largeFont + 1) / 2;
837                 if (smallFont == largeFont)
838                     break;
839             }
840         }
841     } while (horizontalFit || verticalFit);
842
843     if (elide) {
844         if (!elideLayout)
845             elideLayout = new QTextLayout;
846         elideLayout->setFont(layout.font());
847         elideLayout->setTextOption(layout.textOption());
848         elideLayout->setText(elideText);
849         elideLayout->setText(elideLayout->engine()->elidedText(Qt::TextElideMode(elideMode), lineWidth));
850         elideLayout->beginLayout();
851
852         QTextLine elidedLine = elideLayout->createLine();
853         elidedLine.setPosition(QPointF(0, height));
854         if (customLayout) {
855             setupCustomLineGeometry(elidedLine, height, line.lineNumber());
856         } else {
857             setLineGeometry(elidedLine, lineWidth, height);
858         }
859         elideLayout->endLayout();
860
861         br = br.united(elidedLine.naturalTextRect());
862
863         if (visibleCount > 1)
864             line.setPosition(QPointF(FLT_MAX, FLT_MAX));
865         else
866             layout.clearLayout();
867     } else {
868         delete elideLayout;
869         elideLayout = 0;
870     }
871
872     if (!customLayout)
873         br.setHeight(height);
874
875     //Update the number of visible lines
876     if (lineCount != visibleCount) {
877         lineCount = visibleCount;
878         emit q->lineCountChanged();
879     }
880
881     if (truncated != wasTruncated)
882         emit q->truncatedChanged();
883
884     return QRect(qRound(br.x()), qRound(br.y()), qCeil(br.width()), qCeil(br.height()));
885 }
886
887 void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal &height)
888 {
889     Q_Q(QQuickText);
890     line.setLineWidth(lineWidth);
891
892     if (imgTags.isEmpty()) {
893         line.setPosition(QPointF(line.position().x(), height));
894         height += (lineHeightMode == QQuickText::FixedHeight) ? lineHeight : line.height() * lineHeight;
895         return;
896     }
897
898     qreal textTop = 0;
899     qreal textHeight = line.height();
900     qreal totalLineHeight = textHeight;
901
902     QList<QDeclarativeStyledTextImgTag *> imagesInLine;
903
904     foreach (QDeclarativeStyledTextImgTag *image, imgTags) {
905         if (image->position >= line.textStart() &&
906             image->position < line.textStart() + line.textLength()) {
907
908             if (!image->pix) {
909                 QUrl url = qmlContext(q)->resolvedUrl(image->url);
910                 image->pix = new QDeclarativePixmap(qmlEngine(q), url, image->size);
911                 if (image->pix->isLoading()) {
912                     image->pix->connectFinished(q, SLOT(imageDownloadFinished()));
913                     nbActiveDownloads++;
914                 } else if (image->pix->isReady()) {
915                     if (!image->size.isValid()) {
916                         image->size = image->pix->implicitSize();
917                         // if the size of the image was not explicitly set, we need to
918                         // call updateLayout() once again.
919                         needToUpdateLayout = true;
920                     }
921                 } else if (image->pix->isError()) {
922                     qmlInfo(q) << image->pix->error();
923                 }
924             }
925
926             qreal ih = qreal(image->size.height());
927             if (image->align == QDeclarativeStyledTextImgTag::Top)
928                 image->pos.setY(0);
929             else if (image->align == QDeclarativeStyledTextImgTag::Middle)
930                 image->pos.setY((textHeight / 2.0) - (ih / 2.0));
931             else
932                 image->pos.setY(textHeight - ih);
933             imagesInLine << image;
934             textTop = qMax(textTop, qAbs(image->pos.y()));
935         }
936     }
937
938     foreach (QDeclarativeStyledTextImgTag *image, imagesInLine) {
939         totalLineHeight = qMax(totalLineHeight, textTop + image->pos.y() + image->size.height());
940         image->pos.setX(line.cursorToX(image->position));
941         image->pos.setY(image->pos.y() + height + textTop);
942         visibleImgTags << image;
943     }
944
945     line.setPosition(QPointF(line.position().x(), height + textTop));
946     height += (lineHeightMode == QQuickText::FixedHeight) ? lineHeight : totalLineHeight * lineHeight;
947 }
948
949 /*!
950     Returns a painted version of the QQuickTextPrivate::layout QTextLayout.
951     If \a drawStyle is true, the style color overrides all colors in the document.
952 */
953 QPixmap QQuickTextPrivate::textLayoutImage(bool drawStyle)
954 {
955     QSize size = layedOutTextRect.size();
956
957     //paint text
958     QPixmap img(size);
959     if (!size.isEmpty()) {
960         img.fill(Qt::transparent);
961 /*#ifdef Q_OS_MAC // Fails on CocoaX64
962         bool oldSmooth = qt_applefontsmoothing_enabled;
963         qt_applefontsmoothing_enabled = false;
964 #endif*/
965         QPainter p(&img);
966 /*#ifdef Q_OS_MAC // Fails on CocoaX64
967         qt_applefontsmoothing_enabled = oldSmooth;
968 #endif*/
969         drawTextLayout(&p, QPointF(-layedOutTextRect.x(),0), drawStyle);
970     }
971     return img;
972 }
973
974 /*!
975     Paints the QQuickTextPrivate::layout QTextLayout into \a painter at \a pos.  If
976     \a drawStyle is true, the style color overrides all colors in the document.
977 */
978 void QQuickTextPrivate::drawTextLayout(QPainter *painter, const QPointF &pos, bool drawStyle)
979 {
980     if (drawStyle)
981         painter->setPen(styleColor);
982     else
983         painter->setPen(color);
984     painter->setFont(font);
985     layout.draw(painter, pos);
986     if (!elidePos.isNull())
987         painter->drawText(pos + elidePos, elideChar);
988 }
989
990 /*!
991     Returns a painted version of the QQuickTextPrivate::doc QTextDocument.
992     If \a drawStyle is true, the style color overrides all colors in the document.
993 */
994 QPixmap QQuickTextPrivate::textDocumentImage(bool drawStyle)
995 {
996     QSize size = doc->size().toSize();
997
998     //paint text
999     QPixmap img(size);
1000     img.fill(Qt::transparent);
1001 /*#ifdef Q_OS_MAC // Fails on CocoaX64
1002     bool oldSmooth = qt_applefontsmoothing_enabled;
1003     qt_applefontsmoothing_enabled = false;
1004 #endif*/
1005     QPainter p(&img);
1006 /*#ifdef Q_OS_MAC // Fails on CocoaX64
1007     qt_applefontsmoothing_enabled = oldSmooth;
1008 #endif*/
1009
1010     QAbstractTextDocumentLayout::PaintContext context;
1011
1012     QTextOption oldOption(doc->defaultTextOption());
1013     if (drawStyle) {
1014         context.palette.setColor(QPalette::Text, styleColor);
1015         QTextOption colorOption(doc->defaultTextOption());
1016         colorOption.setFlags(QTextOption::SuppressColors);
1017         doc->setDefaultTextOption(colorOption);
1018     } else {
1019         context.palette.setColor(QPalette::Text, color);
1020     }
1021     doc->documentLayout()->draw(&p, context);
1022     if (drawStyle)
1023         doc->setDefaultTextOption(oldOption);
1024     return img;
1025 }
1026
1027
1028 void QQuickTextPrivate::markDirty()
1029 {
1030     Q_Q(QQuickText);
1031     if (!invalidateImageCache() && q->isComponentComplete()) {
1032        updateType = UpdatePaintNode;
1033        q->update();
1034     }
1035 }
1036
1037 /*!
1038     Mark the image cache as dirty.
1039 */
1040 bool QQuickTextPrivate::invalidateImageCache()
1041 {
1042     Q_Q(QQuickText);
1043
1044     if (richTextAsImage || cacheAllTextAsImage || (disableDistanceField && style != QQuickText::Normal)) { // If actually using the image cache
1045         if (imageCacheDirty)
1046             return true;
1047
1048         imageCacheDirty = true;
1049
1050         if (q->isComponentComplete())
1051             QCoreApplication::postEvent(q, new QEvent(QEvent::User));
1052         return true;
1053     }
1054
1055     return false;
1056 }
1057
1058 /*!
1059     Tests if the image cache is dirty, and repaints it if it is.
1060 */
1061 void QQuickTextPrivate::checkImageCache()
1062 {
1063     Q_Q(QQuickText);
1064
1065     if (!imageCacheDirty)
1066         return;
1067
1068     if (text.isEmpty()) {
1069
1070         delete imageCache;
1071         imageCache = 0;
1072
1073     } else {
1074
1075         QPixmap textImage;
1076         QPixmap styledImage;
1077
1078         if (richText) {
1079             textImage = textDocumentImage(false);
1080             if (style != QQuickText::Normal)
1081                 styledImage = textDocumentImage(true); //### should use styleColor
1082         } else {
1083             textImage = textLayoutImage(false);
1084             if (style != QQuickText::Normal)
1085                 styledImage = textLayoutImage(true); //### should use styleColor
1086         }
1087
1088         delete imageCache;
1089         switch (style) {
1090         case QQuickText::Outline:
1091             imageCache = new QPixmap(drawOutline(textImage, styledImage));
1092             break;
1093         case QQuickText::Sunken:
1094             imageCache = new QPixmap(drawOutline(textImage, styledImage, -1));
1095             break;
1096         case QQuickText::Raised:
1097             imageCache = new QPixmap(drawOutline(textImage, styledImage, 1));
1098             break;
1099         default:
1100             imageCache = new QPixmap(textImage);
1101             break;
1102         }
1103
1104     }
1105
1106     imageCacheDirty = false;
1107     textureImageCacheDirty = true;
1108     updateType = UpdatePaintNode;
1109     q->update();
1110 }
1111
1112 /*!
1113     Ensures the QQuickTextPrivate::doc variable is set to a valid text document
1114 */
1115 void QQuickTextPrivate::ensureDoc()
1116 {
1117     if (!doc) {
1118         Q_Q(QQuickText);
1119         doc = new QQuickTextDocumentWithImageResources(q);
1120         doc->setDocumentMargin(0);
1121         doc->setBaseUrl(q->baseUrl());
1122         FAST_CONNECT(doc, SIGNAL(imagesLoaded()), q, SLOT(q_imagesLoaded()));
1123     }
1124 }
1125
1126 /*!
1127     Draw \a styleSource as an outline around \a source and return the new image.
1128 */
1129 QPixmap QQuickTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource)
1130 {
1131     QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
1132     img.fill(Qt::transparent);
1133
1134     QPainter ppm(&img);
1135
1136     QPoint pos(0, 0);
1137     pos += QPoint(-1, 0);
1138     ppm.drawPixmap(pos, styleSource);
1139     pos += QPoint(2, 0);
1140     ppm.drawPixmap(pos, styleSource);
1141     pos += QPoint(-1, -1);
1142     ppm.drawPixmap(pos, styleSource);
1143     pos += QPoint(0, 2);
1144     ppm.drawPixmap(pos, styleSource);
1145
1146     pos += QPoint(0, -1);
1147     ppm.drawPixmap(pos, source);
1148     ppm.end();
1149
1150     return img;
1151 }
1152
1153 /*!
1154     Draw \a styleSource below \a source at \a yOffset and return the new image.
1155 */
1156 QPixmap QQuickTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource, int yOffset)
1157 {
1158     QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
1159     img.fill(Qt::transparent);
1160
1161     QPainter ppm(&img);
1162
1163     ppm.drawPixmap(QPoint(0, yOffset), styleSource);
1164     ppm.drawPixmap(0, 0, source);
1165
1166     ppm.end();
1167
1168     return img;
1169 }
1170
1171 /*!
1172     \qmlclass Text QQuickText
1173     \inqmlmodule QtQuick 2
1174     \ingroup qml-basic-visual-elements
1175     \brief The Text item allows you to add formatted text to a scene.
1176     \inherits Item
1177
1178     Text items can display both plain and rich text. For example, red text with
1179     a specific font and size can be defined like this:
1180
1181     \qml
1182     Text {
1183         text: "Hello World!"
1184         font.family: "Helvetica"
1185         font.pointSize: 24
1186         color: "red"
1187     }
1188     \endqml
1189
1190     Rich text is defined using HTML-style markup:
1191
1192     \qml
1193     Text {
1194         text: "<b>Hello</b> <i>World!</i>"
1195     }
1196     \endqml
1197
1198     \image declarative-text.png
1199
1200     If height and width are not explicitly set, Text will attempt to determine how
1201     much room is needed and set it accordingly. Unless \l wrapMode is set, it will always
1202     prefer width to height (all text will be placed on a single line).
1203
1204     The \l elide property can alternatively be used to fit a single line of
1205     plain text to a set width.
1206
1207     Note that the \l{Supported HTML Subset} is limited. Also, if the text contains
1208     HTML img tags that load remote images, the text is reloaded.
1209
1210     Text provides read-only text. For editable text, see \l TextEdit.
1211
1212     \sa {declarative/text/fonts}{Fonts example}
1213 */
1214 QQuickText::QQuickText(QQuickItem *parent)
1215 : QQuickImplicitSizeItem(*(new QQuickTextPrivate), parent)
1216 {
1217     Q_D(QQuickText);
1218     d->init();
1219 }
1220
1221 QQuickText::~QQuickText()
1222 {
1223 }
1224
1225 /*!
1226   \qmlproperty bool QtQuick2::Text::clip
1227   This property holds whether the text is clipped.
1228
1229   Note that if the text does not fit in the bounding rectangle it will be abruptly chopped.
1230
1231   If you want to display potentially long text in a limited space, you probably want to use \c elide instead.
1232 */
1233
1234 /*!
1235     \qmlproperty bool QtQuick2::Text::smooth
1236
1237     This property holds whether the text is smoothly scaled or transformed.
1238
1239     Smooth filtering gives better visual quality, but is slower.  If
1240     the item is displayed at its natural size, this property has no visual or
1241     performance effect.
1242
1243     \note Generally scaling artifacts are only visible if the item is stationary on
1244     the screen.  A common pattern when animating an item is to disable smooth
1245     filtering at the beginning of the animation and reenable it at the conclusion.
1246 */
1247
1248 /*!
1249     \qmlsignal QtQuick2::Text::onLineLaidOut(line)
1250
1251     This handler is called for every line during the layout process.
1252     This gives the opportunity to position and resize a line as it is being laid out.
1253     It can for example be used to create columns or lay out text around objects.
1254
1255     The properties of a line are:
1256     \list
1257     \o number (read-only)
1258     \o x
1259     \o y
1260     \o width
1261     \o height
1262     \endlist
1263
1264     For example, this will move the first 5 lines of a text element by 100 pixels to the right:
1265     \code
1266     onLineLaidOut: {
1267         if (line.number < 5) {
1268             line.x = line.x + 100
1269             line.width = line.width - 100
1270         }
1271     }
1272     \endcode
1273 */
1274
1275 /*!
1276     \qmlsignal QtQuick2::Text::onLinkActivated(string link)
1277
1278     This handler is called when the user clicks on a link embedded in the text.
1279     The link must be in rich text or HTML format and the
1280     \a link string provides access to the particular link.
1281
1282     \snippet doc/src/snippets/declarative/text/onLinkActivated.qml 0
1283
1284     The example code will display the text
1285     "The main website is at \l{http://qt.nokia.com}{Nokia Qt DF}."
1286
1287     Clicking on the highlighted link will output
1288     \tt{http://qt.nokia.com link activated} to the console.
1289 */
1290
1291 /*!
1292     \qmlproperty string QtQuick2::Text::font.family
1293
1294     Sets the family name of the font.
1295
1296     The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
1297     If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
1298     If the family isn't available a family will be set using the font matching algorithm.
1299 */
1300
1301 /*!
1302     \qmlproperty bool QtQuick2::Text::font.bold
1303
1304     Sets whether the font weight is bold.
1305 */
1306
1307 /*!
1308     \qmlproperty enumeration QtQuick2::Text::font.weight
1309
1310     Sets the font's weight.
1311
1312     The weight can be one of:
1313     \list
1314     \o Font.Light
1315     \o Font.Normal - the default
1316     \o Font.DemiBold
1317     \o Font.Bold
1318     \o Font.Black
1319     \endlist
1320
1321     \qml
1322     Text { text: "Hello"; font.weight: Font.DemiBold }
1323     \endqml
1324 */
1325
1326 /*!
1327     \qmlproperty bool QtQuick2::Text::font.italic
1328
1329     Sets whether the font has an italic style.
1330 */
1331
1332 /*!
1333     \qmlproperty bool QtQuick2::Text::font.underline
1334
1335     Sets whether the text is underlined.
1336 */
1337
1338 /*!
1339     \qmlproperty bool QtQuick2::Text::font.strikeout
1340
1341     Sets whether the font has a strikeout style.
1342 */
1343
1344 /*!
1345     \qmlproperty real QtQuick2::Text::font.pointSize
1346
1347     Sets the font size in points. The point size must be greater than zero.
1348 */
1349
1350 /*!
1351     \qmlproperty int QtQuick2::Text::font.pixelSize
1352
1353     Sets the font size in pixels.
1354
1355     Using this function makes the font device dependent.
1356     Use \c pointSize to set the size of the font in a device independent manner.
1357 */
1358
1359 /*!
1360     \qmlproperty real QtQuick2::Text::font.letterSpacing
1361
1362     Sets the letter spacing for the font.
1363
1364     Letter spacing changes the default spacing between individual letters in the font.
1365     A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
1366 */
1367
1368 /*!
1369     \qmlproperty real QtQuick2::Text::font.wordSpacing
1370
1371     Sets the word spacing for the font.
1372
1373     Word spacing changes the default spacing between individual words.
1374     A positive value increases the word spacing by a corresponding amount of pixels,
1375     while a negative value decreases the inter-word spacing accordingly.
1376 */
1377
1378 /*!
1379     \qmlproperty enumeration QtQuick2::Text::font.capitalization
1380
1381     Sets the capitalization for the text.
1382
1383     \list
1384     \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
1385     \o Font.AllUppercase - This alters the text to be rendered in all uppercase type.
1386     \o Font.AllLowercase - This alters the text to be rendered in all lowercase type.
1387     \o Font.SmallCaps - This alters the text to be rendered in small-caps type.
1388     \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
1389     \endlist
1390
1391     \qml
1392     Text { text: "Hello"; font.capitalization: Font.AllLowercase }
1393     \endqml
1394 */
1395 QFont QQuickText::font() const
1396 {
1397     Q_D(const QQuickText);
1398     return d->sourceFont;
1399 }
1400
1401 void QQuickText::setFont(const QFont &font)
1402 {
1403     Q_D(QQuickText);
1404     if (d->sourceFont == font)
1405         return;
1406
1407     d->sourceFont = font;
1408     QFont oldFont = d->font;
1409     d->font = font;
1410
1411     if (d->font.pointSizeF() != -1) {
1412         // 0.5pt resolution
1413         qreal size = qRound(d->font.pointSizeF()*2.0);
1414         d->font.setPointSizeF(size/2.0);
1415     }
1416
1417     if (oldFont != d->font)
1418         d->updateLayout();
1419
1420     emit fontChanged(d->sourceFont);
1421 }
1422
1423 /*!
1424     \qmlproperty string QtQuick2::Text::text
1425
1426     The text to display. Text supports both plain and rich text strings.
1427
1428     The item will try to automatically determine whether the text should
1429     be treated as styled text. This determination is made using Qt::mightBeRichText().
1430 */
1431 QString QQuickText::text() const
1432 {
1433     Q_D(const QQuickText);
1434     return d->text;
1435 }
1436
1437 void QQuickText::setText(const QString &n)
1438 {
1439     Q_D(QQuickText);
1440     if (d->text == n)
1441         return;
1442
1443     d->richText = d->format == RichText;
1444     d->styledText = d->format == StyledText || (d->format == AutoText && Qt::mightBeRichText(n));
1445     d->text = n;
1446     if (isComponentComplete()) {
1447         if (d->richText) {
1448             d->ensureDoc();
1449             d->doc->setText(n);
1450             d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
1451             d->richTextAsImage = enableImageCache();
1452         } else {
1453             d->rightToLeftText = d->text.isRightToLeft();
1454         }
1455         d->determineHorizontalAlignment();
1456     }
1457     d->textHasChanged = true;
1458     qDeleteAll(d->imgTags);
1459     d->imgTags.clear();
1460     d->updateLayout();
1461     emit textChanged(d->text);
1462 }
1463
1464 /*!
1465     \qmlproperty color QtQuick2::Text::color
1466
1467     The text color.
1468
1469     An example of green text defined using hexadecimal notation:
1470     \qml
1471     Text {
1472         color: "#00FF00"
1473         text: "green text"
1474     }
1475     \endqml
1476
1477     An example of steel blue text defined using an SVG color name:
1478     \qml
1479     Text {
1480         color: "steelblue"
1481         text: "blue text"
1482     }
1483     \endqml
1484 */
1485 QColor QQuickText::color() const
1486 {
1487     Q_D(const QQuickText);
1488     return d->color;
1489 }
1490
1491 void QQuickText::setColor(const QColor &color)
1492 {
1493     Q_D(QQuickText);
1494     if (d->color == color)
1495         return;
1496
1497     d->color = color;
1498     d->markDirty();
1499     emit colorChanged(d->color);
1500 }
1501 /*!
1502     \qmlproperty enumeration QtQuick2::Text::style
1503
1504     Set an additional text style.
1505
1506     Supported text styles are:
1507     \list
1508     \o Text.Normal - the default
1509     \o Text.Outline
1510     \o Text.Raised
1511     \o Text.Sunken
1512     \endlist
1513
1514     \qml
1515     Row {
1516         Text { font.pointSize: 24; text: "Normal" }
1517         Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" }
1518         Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" }
1519         Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" }
1520     }
1521     \endqml
1522
1523     \image declarative-textstyle.png
1524 */
1525 QQuickText::TextStyle QQuickText::style() const
1526 {
1527     Q_D(const QQuickText);
1528     return d->style;
1529 }
1530
1531 void QQuickText::setStyle(QQuickText::TextStyle style)
1532 {
1533     Q_D(QQuickText);
1534     if (d->style == style)
1535         return;
1536
1537     // changing to/from Normal requires the boundingRect() to change
1538     if (isComponentComplete() && (d->style == Normal || style == Normal)) {
1539         d->updateType = QQuickTextPrivate::UpdatePaintNode;
1540         update();
1541     }
1542     d->style = style;
1543     d->markDirty();
1544     emit styleChanged(d->style);
1545 }
1546
1547 /*!
1548     \qmlproperty color QtQuick2::Text::styleColor
1549
1550     Defines the secondary color used by text styles.
1551
1552     \c styleColor is used as the outline color for outlined text, and as the
1553     shadow color for raised or sunken text. If no style has been set, it is not
1554     used at all.
1555
1556     \qml
1557     Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" }
1558     \endqml
1559
1560     \sa style
1561  */
1562 QColor QQuickText::styleColor() const
1563 {
1564     Q_D(const QQuickText);
1565     return d->styleColor;
1566 }
1567
1568 void QQuickText::setStyleColor(const QColor &color)
1569 {
1570     Q_D(QQuickText);
1571     if (d->styleColor == color)
1572         return;
1573
1574     d->styleColor = color;
1575     d->markDirty();
1576     emit styleColorChanged(d->styleColor);
1577 }
1578
1579 /*!
1580     \qmlproperty enumeration QtQuick2::Text::horizontalAlignment
1581     \qmlproperty enumeration QtQuick2::Text::verticalAlignment
1582     \qmlproperty enumeration QtQuick2::Text::effectiveHorizontalAlignment
1583
1584     Sets the horizontal and vertical alignment of the text within the Text items
1585     width and height. By default, the text is vertically aligned to the top. Horizontal
1586     alignment follows the natural alignment of the text, for example text that is read
1587     from left to right will be aligned to the left.
1588
1589     The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight, \c Text.AlignHCenter and
1590     \c Text.AlignJustify.  The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom
1591     and \c Text.AlignVCenter.
1592
1593     Note that for a single line of text, the size of the text is the area of the text. In this common case,
1594     all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will
1595     need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to
1596     that of the parent.
1597
1598     When using the attached property LayoutMirroring::enabled to mirror application
1599     layouts, the horizontal alignment of text will also be mirrored. However, the property
1600     \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
1601     of Text, use the read-only property \c effectiveHorizontalAlignment.
1602 */
1603 QQuickText::HAlignment QQuickText::hAlign() const
1604 {
1605     Q_D(const QQuickText);
1606     return d->hAlign;
1607 }
1608
1609 void QQuickText::setHAlign(HAlignment align)
1610 {
1611     Q_D(QQuickText);
1612     bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
1613     d->hAlignImplicit = false;
1614     if (d->setHAlign(align, forceAlign) && isComponentComplete())
1615         d->updateLayout();
1616 }
1617
1618 void QQuickText::resetHAlign()
1619 {
1620     Q_D(QQuickText);
1621     d->hAlignImplicit = true;
1622     if (isComponentComplete() && d->determineHorizontalAlignment())
1623         d->updateLayout();
1624 }
1625
1626 QQuickText::HAlignment QQuickText::effectiveHAlign() const
1627 {
1628     Q_D(const QQuickText);
1629     QQuickText::HAlignment effectiveAlignment = d->hAlign;
1630     if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
1631         switch (d->hAlign) {
1632         case QQuickText::AlignLeft:
1633             effectiveAlignment = QQuickText::AlignRight;
1634             break;
1635         case QQuickText::AlignRight:
1636             effectiveAlignment = QQuickText::AlignLeft;
1637             break;
1638         default:
1639             break;
1640         }
1641     }
1642     return effectiveAlignment;
1643 }
1644
1645 bool QQuickTextPrivate::setHAlign(QQuickText::HAlignment alignment, bool forceAlign)
1646 {
1647     Q_Q(QQuickText);
1648     if (hAlign != alignment || forceAlign) {
1649         QQuickText::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
1650         hAlign = alignment;
1651
1652         emit q->horizontalAlignmentChanged(hAlign);
1653         if (oldEffectiveHAlign != q->effectiveHAlign())
1654             emit q->effectiveHorizontalAlignmentChanged();
1655         return true;
1656     }
1657     return false;
1658 }
1659
1660 bool QQuickTextPrivate::determineHorizontalAlignment()
1661 {
1662     if (hAlignImplicit) {
1663         bool alignToRight = text.isEmpty() ? qApp->inputPanel()->inputDirection() == Qt::RightToLeft : rightToLeftText;
1664         return setHAlign(alignToRight ? QQuickText::AlignRight : QQuickText::AlignLeft);
1665     }
1666     return false;
1667 }
1668
1669 void QQuickTextPrivate::mirrorChange()
1670 {
1671     Q_Q(QQuickText);
1672     if (q->isComponentComplete()) {
1673         if (!hAlignImplicit && (hAlign == QQuickText::AlignRight || hAlign == QQuickText::AlignLeft)) {
1674             updateLayout();
1675             emit q->effectiveHorizontalAlignmentChanged();
1676         }
1677     }
1678 }
1679
1680 QTextDocument *QQuickTextPrivate::textDocument()
1681 {
1682     return doc;
1683 }
1684
1685 QQuickText::VAlignment QQuickText::vAlign() const
1686 {
1687     Q_D(const QQuickText);
1688     return d->vAlign;
1689 }
1690
1691 void QQuickText::setVAlign(VAlignment align)
1692 {
1693     Q_D(QQuickText);
1694     if (d->vAlign == align)
1695         return;
1696
1697     d->vAlign = align;
1698     emit verticalAlignmentChanged(align);
1699 }
1700
1701 /*!
1702     \qmlproperty enumeration QtQuick2::Text::wrapMode
1703
1704     Set this property to wrap the text to the Text item's width.  The text will only
1705     wrap if an explicit width has been set.  wrapMode can be one of:
1706
1707     \list
1708     \o Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l paintedWidth will exceed a set width.
1709     \o Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l paintedWidth will exceed a set width.
1710     \o Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
1711     \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.
1712     \endlist
1713 */
1714 QQuickText::WrapMode QQuickText::wrapMode() const
1715 {
1716     Q_D(const QQuickText);
1717     return d->wrapMode;
1718 }
1719
1720 void QQuickText::setWrapMode(WrapMode mode)
1721 {
1722     Q_D(QQuickText);
1723     if (mode == d->wrapMode)
1724         return;
1725
1726     d->wrapMode = mode;
1727     d->updateLayout();
1728
1729     emit wrapModeChanged();
1730 }
1731
1732 /*!
1733     \qmlproperty int QtQuick2::Text::lineCount
1734
1735     Returns the number of lines visible in the text item.
1736
1737     This property is not supported for rich text.
1738
1739     \sa maximumLineCount
1740 */
1741 int QQuickText::lineCount() const
1742 {
1743     Q_D(const QQuickText);
1744     return d->lineCount;
1745 }
1746
1747 /*!
1748     \qmlproperty bool QtQuick2::Text::truncated
1749
1750     Returns true if the text has been truncated due to \l maximumLineCount
1751     or \l elide.
1752
1753     This property is not supported for rich text.
1754
1755     \sa maximumLineCount, elide
1756 */
1757 bool QQuickText::truncated() const
1758 {
1759     Q_D(const QQuickText);
1760     return d->truncated;
1761 }
1762
1763 /*!
1764     \qmlproperty int QtQuick2::Text::maximumLineCount
1765
1766     Set this property to limit the number of lines that the text item will show.
1767     If elide is set to Text.ElideRight, the text will be elided appropriately.
1768     By default, this is the value of the largest possible integer.
1769
1770     This property is not supported for rich text.
1771
1772     \sa lineCount, elide
1773 */
1774 int QQuickText::maximumLineCount() const
1775 {
1776     Q_D(const QQuickText);
1777     return d->maximumLineCount;
1778 }
1779
1780 void QQuickText::setMaximumLineCount(int lines)
1781 {
1782     Q_D(QQuickText);
1783
1784     d->maximumLineCountValid = lines==INT_MAX ? false : true;
1785     if (d->maximumLineCount != lines) {
1786         d->maximumLineCount = lines;
1787         d->updateLayout();
1788         emit maximumLineCountChanged();
1789     }
1790 }
1791
1792 void QQuickText::resetMaximumLineCount()
1793 {
1794     Q_D(QQuickText);
1795     setMaximumLineCount(INT_MAX);
1796     d->elidePos = QPointF();
1797     if (d->truncated != false) {
1798         d->truncated = false;
1799         emit truncatedChanged();
1800     }
1801 }
1802
1803 /*!
1804     \qmlproperty enumeration QtQuick2::Text::textFormat
1805
1806     The way the text property should be displayed.
1807
1808     Supported text formats are:
1809
1810     \list
1811     \o Text.AutoText (default)
1812     \o Text.PlainText
1813     \o Text.StyledText
1814     \o Text.RichText
1815     \endlist
1816
1817     If the text format is \c Text.AutoText the text element
1818     will automatically determine whether the text should be treated as
1819     styled text.  This determination is made using Qt::mightBeRichText()
1820     which uses a fast and therefore simple heuristic. It mainly checks
1821     whether there is something that looks like a tag before the first
1822     line break. Although the result may be correct for common cases,
1823     there is no guarantee.
1824
1825     Text.StyledText is an optimized format supporting some basic text
1826     styling markup, in the style of html 3.2:
1827
1828     \code
1829     <b></b> - bold
1830     <strong></strong> - bold
1831     <i></i> - italic
1832     <br> - new line
1833     <p> - paragraph
1834     <u> - underlined text
1835     <font color="color_name" size="1-7"></font>
1836     <h1> to <h6> - headers
1837     <a href=""> - anchor
1838     <img src="" align="top,middle,bottom" width="" height=""> - inline images
1839     <ol type="">, <ul type=""> and <li> - ordered and unordered lists
1840     <pre></pre> - preformatted
1841     &gt; &lt; &amp;
1842     \endcode
1843
1844     \c Text.StyledText parser is strict, requiring tags to be correctly nested.
1845
1846     \table
1847     \row
1848     \o
1849     \qml
1850 Column {
1851     Text {
1852         font.pointSize: 24
1853         text: "<b>Hello</b> <i>World!</i>"
1854     }
1855     Text {
1856         font.pointSize: 24
1857         textFormat: Text.RichText
1858         text: "<b>Hello</b> <i>World!</i>"
1859     }
1860     Text {
1861         font.pointSize: 24
1862         textFormat: Text.PlainText
1863         text: "<b>Hello</b> <i>World!</i>"
1864     }
1865 }
1866     \endqml
1867     \o \image declarative-textformat.png
1868     \endtable
1869 */
1870 QQuickText::TextFormat QQuickText::textFormat() const
1871 {
1872     Q_D(const QQuickText);
1873     return d->format;
1874 }
1875
1876 void QQuickText::setTextFormat(TextFormat format)
1877 {
1878     Q_D(QQuickText);
1879     if (format == d->format)
1880         return;
1881     d->format = format;
1882     bool wasRich = d->richText;
1883     d->richText = format == RichText;
1884     d->styledText = format == StyledText || (format == AutoText && Qt::mightBeRichText(d->text));
1885
1886     if (isComponentComplete()) {
1887         if (!wasRich && d->richText) {
1888             d->ensureDoc();
1889             d->doc->setText(d->text);
1890             d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
1891             d->richTextAsImage = enableImageCache();
1892         } else {
1893             d->rightToLeftText = d->text.isRightToLeft();
1894         }
1895         d->determineHorizontalAlignment();
1896     }
1897     d->updateLayout();
1898
1899     emit textFormatChanged(d->format);
1900 }
1901
1902 /*!
1903     \qmlproperty enumeration QtQuick2::Text::elide
1904
1905     Set this property to elide parts of the text fit to the Text item's width.
1906     The text will only elide if an explicit width has been set.
1907
1908     This property cannot be used with rich text.
1909
1910     Eliding can be:
1911     \list
1912     \o Text.ElideNone  - the default
1913     \o Text.ElideLeft
1914     \o Text.ElideMiddle
1915     \o Text.ElideRight
1916     \endlist
1917
1918     If this property is set to Text.ElideRight, it can be used with \l {wrapMode}{wrapped}
1919     text. The text will only elide if \c maximumLineCount, or \c height has been set.
1920     If both \c maximumLineCount and \c height are set, \c maximumLineCount will
1921     apply unless the lines do not fit in the height allowed.
1922
1923     If the text is a multi-length string, and the mode is not \c Text.ElideNone,
1924     the first string that fits will be used, otherwise the last will be elided.
1925
1926     Multi-length strings are ordered from longest to shortest, separated by the
1927     Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}).
1928 */
1929 QQuickText::TextElideMode QQuickText::elideMode() const
1930 {
1931     Q_D(const QQuickText);
1932     return d->elideMode;
1933 }
1934
1935 void QQuickText::setElideMode(QQuickText::TextElideMode mode)
1936 {
1937     Q_D(QQuickText);
1938     if (mode == d->elideMode)
1939         return;
1940
1941     d->elideMode = mode;
1942     d->updateLayout();
1943
1944     emit elideModeChanged(d->elideMode);
1945 }
1946
1947 /*!
1948     \qmlproperty url QtQuick2::Text::baseUrl
1949
1950     This property specifies a base URL which is used to resolve relative URLs
1951     within the text.
1952
1953     By default is the url of the Text element.
1954 */
1955
1956 QUrl QQuickText::baseUrl() const
1957 {
1958     Q_D(const QQuickText);
1959     if (d->baseUrl.isEmpty()) {
1960         if (QDeclarativeContext *context = qmlContext(this))
1961             const_cast<QQuickTextPrivate *>(d)->baseUrl = context->baseUrl();
1962     }
1963     return d->baseUrl;
1964 }
1965
1966 void QQuickText::setBaseUrl(const QUrl &url)
1967 {
1968     Q_D(QQuickText);
1969     if (baseUrl() != url) {
1970         d->baseUrl = url;
1971
1972         if (d->doc)
1973             d->doc->setBaseUrl(url);
1974         emit baseUrlChanged();
1975     }
1976 }
1977
1978 void QQuickText::resetBaseUrl()
1979 {
1980     if (QDeclarativeContext *context = qmlContext(this))
1981         setBaseUrl(context->baseUrl());
1982     else
1983         setBaseUrl(QUrl());
1984 }
1985
1986 /*! \internal */
1987 QRectF QQuickText::boundingRect() const
1988 {
1989     Q_D(const QQuickText);
1990
1991     QRect rect = d->layedOutTextRect;
1992     if (d->style != Normal)
1993         rect.adjust(-1, 0, 1, 2);
1994
1995     // Could include font max left/right bearings to either side of rectangle.
1996
1997     int h = height();
1998     switch (d->vAlign) {
1999     case AlignTop:
2000         break;
2001     case AlignBottom:
2002         rect.moveTop(h - rect.height());
2003         break;
2004     case AlignVCenter:
2005         rect.moveTop((h - rect.height()) / 2);
2006         break;
2007     }
2008
2009     return QRectF(rect);
2010 }
2011
2012 /*! \internal */
2013 void QQuickText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
2014 {
2015     Q_D(QQuickText);
2016     if (d->text.isEmpty()) {
2017         QQuickItem::geometryChanged(newGeometry, oldGeometry);
2018         return;
2019     }
2020
2021     bool widthChanged = newGeometry.width() != oldGeometry.width();
2022     bool heightChanged = newGeometry.height() != oldGeometry.height();
2023     bool leftAligned = effectiveHAlign() == QQuickText::AlignLeft;
2024     bool wrapped = d->wrapMode != QQuickText::NoWrap;
2025     bool elide = d->elideMode != QQuickText::ElideNone;
2026     bool scaleFont = d->fontSizeMode != QQuickText::FixedSize && (widthValid() || heightValid());
2027
2028     if ((!widthChanged && !heightChanged) || d->internalWidthUpdate)
2029         goto geomChangeDone;
2030
2031     if (leftAligned && !wrapped && !elide && !scaleFont)
2032         goto geomChangeDone; // left aligned unwrapped text without eliding never needs relayout
2033
2034     if (!widthChanged && !wrapped && d->singleline && !scaleFont)
2035         goto geomChangeDone; // only height has changed which doesn't affect single line unwrapped text
2036
2037     if (!widthChanged && wrapped && d->elideMode != QQuickText::ElideRight && !scaleFont)
2038         goto geomChangeDone; // only height changed and no multiline eliding.
2039
2040     if (leftAligned && d->elideMode == QQuickText::ElideRight && !d->truncated && d->singleline
2041             && !wrapped && newGeometry.width() > oldGeometry.width() && !scaleFont)
2042         goto geomChangeDone; // Eliding not affected if we're not currently truncated and we get wider.
2043
2044     if (d->elideMode == QQuickText::ElideRight && wrapped && newGeometry.height() > oldGeometry.height() && !scaleFont) {
2045         if (!d->truncated)
2046             goto geomChangeDone; // Multiline eliding not affected if we're not currently truncated and we get higher.
2047         if (d->maximumLineCountValid && d->lineCount == d->maximumLineCount)
2048             goto geomChangeDone; // Multiline eliding not affected if we're already at max line count and we get higher.
2049     }
2050
2051     if (d->updateOnComponentComplete) {
2052         // We need to re-elide
2053         d->updateLayout();
2054     } else {
2055         // We just need to re-layout
2056         d->updateSize();
2057     }
2058
2059 geomChangeDone:
2060     QQuickItem::geometryChanged(newGeometry, oldGeometry);
2061 }
2062
2063 void QQuickText::triggerPreprocess()
2064 {
2065     Q_D(QQuickText);
2066     if (d->updateType == QQuickTextPrivate::UpdateNone)
2067         d->updateType = QQuickTextPrivate::UpdatePreprocess;
2068     update();
2069 }
2070
2071 QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
2072 {
2073     Q_UNUSED(data);
2074     Q_D(QQuickText);
2075
2076     if (d->text.isEmpty()) {
2077         delete oldNode;
2078         return 0;
2079     }
2080
2081     if (d->updateType != QQuickTextPrivate::UpdatePaintNode && oldNode != 0) {
2082         // Update done in preprocess() in the nodes
2083         d->updateType = QQuickTextPrivate::UpdateNone;
2084         return oldNode;
2085     }
2086
2087     d->updateType = QQuickTextPrivate::UpdateNone;
2088
2089     QRectF bounds = boundingRect();
2090
2091     // We need to make sure the layout is done in the current thread
2092 #if defined(Q_OS_MAC)
2093     d->paintingThread = QThread::currentThread();
2094     if (d->layoutThread != d->paintingThread)
2095         d->updateLayout();
2096 #endif
2097
2098     // XXX todo - some styled text can be done by the QQuickTextNode
2099     if (d->richTextAsImage || d->cacheAllTextAsImage || (d->disableDistanceField && d->style != Normal)) {
2100         bool wasDirty = d->textureImageCacheDirty;
2101         d->textureImageCacheDirty = false;
2102
2103         if (!d->imageCache || d->imageCache->isNull()) {
2104             delete oldNode;
2105             return 0;
2106         }
2107
2108         QSGImageNode *node = 0;
2109         if (!oldNode || d->nodeType != QQuickTextPrivate::NodeIsTexture) {
2110             delete oldNode;
2111             node = QQuickItemPrivate::get(this)->sceneGraphContext()->createImageNode();
2112             d->texture = new QSGPlainTexture();
2113             wasDirty = true;
2114             d->nodeType = QQuickTextPrivate::NodeIsTexture;
2115         } else {
2116             node = static_cast<QSGImageNode *>(oldNode);
2117             Q_ASSERT(d->texture);
2118         }
2119
2120         if (wasDirty) {
2121             qobject_cast<QSGPlainTexture *>(d->texture)->setImage(d->imageCache->toImage());
2122             node->setTexture(0);
2123             node->setTexture(d->texture);
2124         }
2125
2126         node->setTargetRect(QRectF(bounds.x(), bounds.y(), d->imageCache->width(), d->imageCache->height()));
2127         node->setSourceRect(QRectF(0, 0, 1, 1));
2128         node->setHorizontalWrapMode(QSGTexture::ClampToEdge);
2129         node->setVerticalWrapMode(QSGTexture::ClampToEdge);
2130         node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that..
2131         node->update();
2132
2133         return node;
2134
2135     } else {
2136         QQuickTextNode *node = 0;
2137         if (!oldNode || d->nodeType != QQuickTextPrivate::NodeIsText) {
2138             delete oldNode;
2139             node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext(), this);
2140             d->nodeType = QQuickTextPrivate::NodeIsText;
2141         } else {
2142             node = static_cast<QQuickTextNode *>(oldNode);
2143         }
2144
2145         node->deleteContent();
2146         node->setMatrix(QMatrix4x4());
2147
2148         if (d->richText) {
2149             d->ensureDoc();
2150             node->addTextDocument(bounds.topLeft(), d->doc, d->color, d->style, d->styleColor);
2151
2152         } else if (d->elideMode == QQuickText::ElideNone || bounds.width() > 0.) {
2153             node->addTextLayout(QPoint(0, bounds.y()), &d->layout, d->color, d->style, d->styleColor);
2154             if (d->elideLayout)
2155                 node->addTextLayout(QPoint(0, bounds.y()), d->elideLayout, d->color, d->style, d->styleColor);
2156         }
2157
2158         foreach (QDeclarativeStyledTextImgTag *img, d->visibleImgTags) {
2159             QDeclarativePixmap *pix = img->pix;
2160             if (pix && pix->isReady())
2161                 node->addImage(QRectF(img->pos.x(), img->pos.y() + bounds.y(), pix->width(), pix->height()), pix->image());
2162         }
2163         return node;
2164     }
2165 }
2166
2167 bool QQuickText::event(QEvent *e)
2168 {
2169     Q_D(QQuickText);
2170     if (e->type() == QEvent::User) {
2171         d->checkImageCache();
2172         return true;
2173     } else {
2174         return QQuickImplicitSizeItem::event(e);
2175     }
2176 }
2177
2178 void QQuickText::updatePolish()
2179 {
2180     Q_D(QQuickText);
2181     if (d->updateLayoutOnPolish)
2182         d->updateLayout();
2183     else
2184         d->updateSize();
2185 }
2186
2187 /*!
2188     \qmlproperty real QtQuick2::Text::paintedWidth
2189
2190     Returns the width of the text, including width past the width
2191     which is covered due to insufficient wrapping if WrapMode is set.
2192 */
2193 qreal QQuickText::paintedWidth() const
2194 {
2195     Q_D(const QQuickText);
2196     return d->paintedSize.width();
2197 }
2198
2199 /*!
2200     \qmlproperty real QtQuick2::Text::paintedHeight
2201
2202     Returns the height of the text, including height past the height
2203     which is covered due to there being more text than fits in the set height.
2204 */
2205 qreal QQuickText::paintedHeight() const
2206 {
2207     Q_D(const QQuickText);
2208     return d->paintedSize.height();
2209 }
2210
2211 /*!
2212     \qmlproperty real QtQuick2::Text::lineHeight
2213
2214     Sets the line height for the text.
2215     The value can be in pixels or a multiplier depending on lineHeightMode.
2216
2217     The default value is a multiplier of 1.0.
2218     The line height must be a positive value.
2219 */
2220 qreal QQuickText::lineHeight() const
2221 {
2222     Q_D(const QQuickText);
2223     return d->lineHeight;
2224 }
2225
2226 void QQuickText::setLineHeight(qreal lineHeight)
2227 {
2228     Q_D(QQuickText);
2229
2230     if ((d->lineHeight == lineHeight) || (lineHeight < 0.0))
2231         return;
2232
2233     d->lineHeight = lineHeight;
2234     d->updateLayout();
2235     emit lineHeightChanged(lineHeight);
2236 }
2237
2238 /*!
2239     \qmlproperty enumeration QtQuick2::Text::lineHeightMode
2240
2241     This property determines how the line height is specified.
2242     The possible values are:
2243
2244     \list
2245     \o Text.ProportionalHeight (default) - this sets the spacing proportional to the
2246        line (as a multiplier). For example, set to 2 for double spacing.
2247     \o Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
2248     \endlist
2249 */
2250 QQuickText::LineHeightMode QQuickText::lineHeightMode() const
2251 {
2252     Q_D(const QQuickText);
2253     return d->lineHeightMode;
2254 }
2255
2256 void QQuickText::setLineHeightMode(LineHeightMode mode)
2257 {
2258     Q_D(QQuickText);
2259     if (mode == d->lineHeightMode)
2260         return;
2261
2262     d->lineHeightMode = mode;
2263     d->updateLayout();
2264
2265     emit lineHeightModeChanged(mode);
2266 }
2267
2268 /*!
2269     \qmlproperty enumeration QtQuick2::Text::fontSizeMode
2270
2271     This property specifies how the font size of the displayed text is determined.
2272     The possible values are:
2273
2274     \list
2275     \o Text.FixedSize (default) - The size specified by \l font.pixelSize
2276     or \l font.pointSize is used.
2277     \o Text.HorizontalFit - The largest size up to the size specified that fits
2278     within the width of the item without wrapping is used.
2279     \o Text.VerticalFit - The largest size up to the size specified that fits
2280     the height of the item is used.
2281     \o Text.Fit - The largest size up to the size specified the fits within the
2282     width and height of the item is used.
2283     \endlist
2284
2285     The font size of fitted text has a minimum bound specified by the
2286     minimumPointSize or minimumPixelSize property and maximum bound specified
2287     by either the \l font.pointSize or \l font.pixelSize properties.
2288
2289     If the text does not fit within the item bounds with the minimum font size
2290     the text will be elided as per the \l elide property.
2291 */
2292
2293 QQuickText::FontSizeMode QQuickText::fontSizeMode() const
2294 {
2295     Q_D(const QQuickText);
2296     return d->fontSizeMode;
2297 }
2298
2299 void QQuickText::setFontSizeMode(FontSizeMode mode)
2300 {
2301     Q_D(QQuickText);
2302     if (d->fontSizeMode == mode)
2303         return;
2304
2305     polish();
2306
2307     d->fontSizeMode = mode;
2308     emit fontSizeModeChanged();
2309 }
2310
2311 /*!
2312     \qmlproperty int QtQuick2::Text::minimumPixelSize
2313
2314     This property specifies the minimum font pixel size of text scaled by the
2315     fontSizeMode property.
2316
2317     If the fontSizeMode is Text.FixedSize or the \l font.pixelSize is -1 this
2318     property is ignored.
2319 */
2320
2321 int QQuickText::minimumPixelSize() const
2322 {
2323     Q_D(const QQuickText);
2324     return d->minimumPixelSize;
2325 }
2326
2327 void QQuickText::setMinimumPixelSize(int size)
2328 {
2329     Q_D(QQuickText);
2330     if (d->minimumPixelSize == size)
2331         return;
2332
2333     if (d->fontSizeMode != FixedSize && (widthValid() || heightValid()))
2334         polish();
2335     d->minimumPixelSize = size;
2336     emit minimumPixelSizeChanged();
2337 }
2338
2339 /*!
2340     \qmlproperty int QtQuick2::Text::minimumPointSize
2341
2342     This property specifies the minimum font point \l size of text scaled by
2343     the fontSizeMode property.
2344
2345     If the fontSizeMode is Text.FixedSize or the \l font.pointSize is -1 this
2346     property is ignored.
2347 */
2348
2349 int QQuickText::minimumPointSize() const
2350 {
2351     Q_D(const QQuickText);
2352     return d->minimumPointSize;
2353 }
2354
2355 void QQuickText::setMinimumPointSize(int size)
2356 {
2357     Q_D(QQuickText);
2358     if (d->minimumPointSize == size)
2359         return;
2360
2361     if (d->fontSizeMode != FixedSize && (widthValid() || heightValid()))
2362         polish();
2363     d->minimumPointSize = size;
2364     emit minimumPointSizeChanged();
2365 }
2366
2367 /*!
2368     Returns the number of resources (images) that are being loaded asynchronously.
2369 */
2370 int QQuickText::resourcesLoading() const
2371 {
2372     Q_D(const QQuickText);
2373     return d->doc ? d->doc->resourcesLoading() : 0;
2374 }
2375
2376 /*! \internal */
2377 void QQuickText::componentComplete()
2378 {
2379     Q_D(QQuickText);
2380     if (d->updateOnComponentComplete) {
2381         if (d->richText) {
2382             d->ensureDoc();
2383             d->doc->setText(d->text);
2384             d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
2385             d->richTextAsImage = enableImageCache();
2386         } else {
2387             d->rightToLeftText = d->text.isRightToLeft();
2388         }
2389         d->determineHorizontalAlignment();
2390     }
2391     QQuickItem::componentComplete();
2392     if (d->updateOnComponentComplete)
2393         d->updateLayout();
2394
2395     // Enable accessibility for text items.
2396     d->setAccessibleFlagAndListener();
2397 }
2398
2399
2400 QString QQuickTextPrivate::anchorAt(const QPointF &mousePos)
2401 {
2402     if (styledText) {
2403         for (int i = 0; i < layout.lineCount(); ++i) {
2404             QTextLine line = layout.lineAt(i);
2405             if (line.naturalTextRect().contains(mousePos)) {
2406                 int charPos = line.xToCursor(mousePos.x());
2407                 foreach (const QTextLayout::FormatRange &formatRange, layout.additionalFormats()) {
2408                     if (formatRange.format.isAnchor()
2409                             && charPos >= formatRange.start
2410                             && charPos <= formatRange.start + formatRange.length) {
2411                         return formatRange.format.anchorHref();
2412                     }
2413                 }
2414                 break;
2415             }
2416         }
2417     }
2418     return QString();
2419 }
2420
2421 bool QQuickTextPrivate::isLinkActivatedConnected()
2422 {
2423     static int idx = this->signalIndex("linkActivated(QString)");
2424     return this->isSignalConnected(idx);
2425 }
2426
2427 /*!  \internal */
2428 void QQuickText::mousePressEvent(QMouseEvent *event)
2429 {
2430     Q_D(QQuickText);
2431
2432     if (d->isLinkActivatedConnected()) {
2433         if (d->styledText)
2434             d->activeLink = d->anchorAt(event->localPos());
2435         else if (d->richText && d->doc)
2436             d->activeLink = d->doc->documentLayout()->anchorAt(event->localPos());
2437     }
2438
2439     if (d->activeLink.isEmpty())
2440         event->setAccepted(false);
2441
2442     // ### may malfunction if two of the same links are clicked & dragged onto each other)
2443
2444     if (!event->isAccepted())
2445         QQuickItem::mousePressEvent(event);
2446
2447 }
2448
2449 /*! \internal */
2450 void QQuickText::mouseReleaseEvent(QMouseEvent *event)
2451 {
2452     Q_D(QQuickText);
2453
2454     // ### confirm the link, and send a signal out
2455
2456     QString link;
2457     if (d->isLinkActivatedConnected()) {
2458         if (d->styledText)
2459             link = d->anchorAt(event->localPos());
2460         else if (d->richText && d->doc)
2461             link = d->doc->documentLayout()->anchorAt(event->localPos());
2462     }
2463
2464     if (!link.isEmpty() && d->activeLink == link)
2465         emit linkActivated(d->activeLink);
2466     else
2467         event->setAccepted(false);
2468
2469     if (!event->isAccepted())
2470         QQuickItem::mouseReleaseEvent(event);
2471 }
2472
2473 QT_END_NAMESPACE