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