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