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