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