Remove Q_WS_*, symbian and maemo code in QtDeclarative
[profile/ivi/qtdeclarative.git] / src / declarative / items / qquicktext.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
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 <private/qsgdistancefieldglyphcache_p.h>
46 #include <private/qsgcontext_p.h>
47 #include <private/qsgadaptationlayer_p.h>
48 #include "qquicktextnode_p.h"
49 #include "qquickimage_p_p.h"
50 #include <private/qsgtexture_p.h>
51
52 #include <QtDeclarative/qdeclarativeinfo.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
61 #include <private/qdeclarativestyledtext_p.h>
62 #include <private/qdeclarativepixmapcache_p.h>
63
64 #include <qmath.h>
65 #include <limits.h>
66
67 QT_BEGIN_NAMESPACE
68
69 extern Q_GUI_EXPORT bool qt_applefontsmoothing_enabled;
70
71 class QQuickTextDocumentWithImageResources : public QTextDocument {
72     Q_OBJECT
73
74 public:
75     QQuickTextDocumentWithImageResources(QQuickText *parent);
76     virtual ~QQuickTextDocumentWithImageResources();
77
78     void setText(const QString &);
79     int resourcesLoading() const { return outstanding; }
80
81 protected:
82     QVariant loadResource(int type, const QUrl &name);
83
84 private slots:
85     void requestFinished();
86
87 private:
88     QHash<QUrl, QDeclarativePixmap *> m_resources;
89
90     int outstanding;
91     static QSet<QUrl> errors;
92 };
93
94 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
95 DEFINE_BOOL_CONFIG_OPTION(enableImageCache, QML_ENABLE_TEXT_IMAGE_CACHE);
96
97 QString QQuickTextPrivate::elideChar = QString(0x2026);
98
99 QQuickTextPrivate::QQuickTextPrivate()
100 : color((QRgb)0), style(QQuickText::Normal), hAlign(QQuickText::AlignLeft),
101   vAlign(QQuickText::AlignTop), elideMode(QQuickText::ElideNone),
102   format(QQuickText::AutoText), wrapMode(QQuickText::NoWrap), lineHeight(1),
103   lineHeightMode(QQuickText::ProportionalHeight), lineCount(1), maximumLineCount(INT_MAX),
104   maximumLineCountValid(false),
105   texture(0),
106   imageCacheDirty(false), updateOnComponentComplete(true),
107   richText(false), styledText(false), singleline(false), cacheAllTextAsImage(true), internalWidthUpdate(false),
108   requireImplicitWidth(false), truncated(false), hAlignImplicit(true), rightToLeftText(false),
109   layoutTextElided(false), richTextAsImage(false), textureImageCacheDirty(false), textHasChanged(true),
110   naturalWidth(0), doc(0), textLine(0), nodeType(NodeIsNull)
111
112 #if defined(Q_OS_MAC)
113 , layoutThread(0), paintingThread(0)
114 #endif
115
116 {
117     cacheAllTextAsImage = enableImageCache();
118 }
119
120 void QQuickTextPrivate::init()
121 {
122     Q_Q(QQuickText);
123     q->setAcceptedMouseButtons(Qt::LeftButton);
124     q->setFlag(QQuickItem::ItemHasContents);
125 }
126
127 QQuickTextDocumentWithImageResources::QQuickTextDocumentWithImageResources(QQuickText *parent)
128 : QTextDocument(parent), outstanding(0)
129 {
130     setUndoRedoEnabled(false);
131 }
132
133 QQuickTextDocumentWithImageResources::~QQuickTextDocumentWithImageResources()
134 {
135     if (!m_resources.isEmpty())
136         qDeleteAll(m_resources);
137 }
138
139 QVariant QQuickTextDocumentWithImageResources::loadResource(int type, const QUrl &name)
140 {
141     QDeclarativeContext *context = qmlContext(parent());
142     QUrl url = context->resolvedUrl(name);
143
144     if (type == QTextDocument::ImageResource) {
145         QHash<QUrl, QDeclarativePixmap *>::Iterator iter = m_resources.find(url);
146
147         if (iter == m_resources.end()) {
148             QDeclarativePixmap *p = new QDeclarativePixmap(context->engine(), url);
149             iter = m_resources.insert(name, p);
150
151             if (p->isLoading()) {
152                 p->connectFinished(this, SLOT(requestFinished()));
153                 outstanding++;
154             }
155         }
156
157         QDeclarativePixmap *p = *iter;
158         if (p->isReady()) {
159             return p->pixmap();
160         } else if (p->isError()) {
161             if (!errors.contains(url)) {
162                 errors.insert(url);
163                 qmlInfo(parent()) << p->error();
164             }
165         }
166     }
167
168     return QTextDocument::loadResource(type,url); // The *resolved* URL
169 }
170
171 void QQuickTextDocumentWithImageResources::requestFinished()
172 {
173     outstanding--;
174     if (outstanding == 0) {
175         QQuickText *textItem = static_cast<QQuickText*>(parent());
176         QString text = textItem->text();
177 #ifndef QT_NO_TEXTHTMLPARSER
178         setHtml(text);
179 #else
180         setPlainText(text);
181 #endif
182         QQuickTextPrivate *d = QQuickTextPrivate::get(textItem);
183         d->updateLayout();
184     }
185 }
186
187 void QQuickTextDocumentWithImageResources::setText(const QString &text)
188 {
189     if (!m_resources.isEmpty()) {
190         qDeleteAll(m_resources);
191         m_resources.clear();
192         outstanding = 0;
193     }
194
195 #ifndef QT_NO_TEXTHTMLPARSER
196     setHtml(text);
197 #else
198     setPlainText(text);
199 #endif
200 }
201
202 QSet<QUrl> QQuickTextDocumentWithImageResources::errors;
203
204 QQuickTextPrivate::~QQuickTextPrivate()
205 {
206     delete textLine; textLine = 0;
207 }
208
209 qreal QQuickTextPrivate::getImplicitWidth() const
210 {
211     if (!requireImplicitWidth) {
212         // We don't calculate implicitWidth unless it is required.
213         // We need to force a size update now to ensure implicitWidth is calculated
214         QQuickTextPrivate *me = const_cast<QQuickTextPrivate*>(this);
215         me->requireImplicitWidth = true;
216         me->updateSize();
217     }
218     return implicitWidth;
219 }
220
221 void QQuickTextPrivate::updateLayout()
222 {
223     Q_Q(QQuickText);
224     if (!q->isComponentComplete()) {
225         updateOnComponentComplete = true;
226         return;
227     }
228
229     layoutTextElided = false;
230     // Setup instance of QTextLayout for all cases other than richtext
231     if (!richText) {
232         layout.clearLayout();
233         layout.setFont(font);
234         if (!styledText) {
235             QString tmp = text;
236             tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
237             singleline = !tmp.contains(QChar::LineSeparator);
238             if (singleline && !maximumLineCountValid && elideMode != QQuickText::ElideNone && q->widthValid()) {
239                 QFontMetrics fm(font);
240                 tmp = fm.elidedText(tmp,(Qt::TextElideMode)elideMode,q->width());
241                 if (tmp != text) {
242                     layoutTextElided = true;
243                     if (!truncated) {
244                         truncated = true;
245                         emit q->truncatedChanged();
246                     }
247                 }
248             }
249             layout.setText(tmp);
250         } else {
251             singleline = false;
252             if (textHasChanged) {
253                 QDeclarativeStyledText::parse(text, layout);
254                 textHasChanged = false;
255             }
256         }
257     } else {
258         ensureDoc();
259         QTextBlockFormat::LineHeightTypes type;
260         type = lineHeightMode == QQuickText::FixedHeight ? QTextBlockFormat::FixedHeight : QTextBlockFormat::ProportionalHeight;
261         QTextBlockFormat blockFormat;
262         blockFormat.setLineHeight((lineHeightMode == QQuickText::FixedHeight ? lineHeight : lineHeight * 100), type);
263         for (QTextBlock it = doc->begin(); it != doc->end(); it = it.next()) {
264             QTextCursor cursor(it);
265             cursor.mergeBlockFormat(blockFormat);
266         }
267     }
268
269     updateSize();
270 }
271
272 void QQuickTextPrivate::updateSize()
273 {
274     Q_Q(QQuickText);
275
276     if (!q->isComponentComplete()) {
277         updateOnComponentComplete = true;
278         return;
279     }
280
281     if (!requireImplicitWidth) {
282         emit q->implicitWidthChanged();
283         // if the implicitWidth is used, then updateSize() has already been called (recursively)
284         if (requireImplicitWidth)
285             return;
286     }
287
288     invalidateImageCache();
289
290     QFontMetrics fm(font);
291     if (text.isEmpty()) {
292         q->setImplicitWidth(0);
293         q->setImplicitHeight(fm.height());
294         paintedSize = QSize(0, fm.height());
295         emit q->paintedSizeChanged();
296         q->update();
297         return;
298     }
299
300     int dy = q->height();
301     QSize size(0, 0);
302
303 #if defined(Q_OS_MAC)
304     layoutThread = QThread::currentThread();
305 #endif
306
307     //setup instance of QTextLayout for all cases other than richtext
308     if (!richText) {
309         QRect textRect = setupTextLayout();
310         layedOutTextRect = textRect;
311         size = textRect.size();
312         dy -= size.height();
313     } else {
314         singleline = false; // richtext can't elide or be optimized for single-line case
315         ensureDoc();
316         doc->setDefaultFont(font);
317         QQuickText::HAlignment horizontalAlignment = q->effectiveHAlign();
318         if (rightToLeftText) {
319             if (horizontalAlignment == QQuickText::AlignLeft)
320                 horizontalAlignment = QQuickText::AlignRight;
321             else if (horizontalAlignment == QQuickText::AlignRight)
322                 horizontalAlignment = QQuickText::AlignLeft;
323         }
324         QTextOption option;
325         option.setAlignment((Qt::Alignment)int(horizontalAlignment | vAlign));
326         option.setWrapMode(QTextOption::WrapMode(wrapMode));
327         if (!cacheAllTextAsImage && !richTextAsImage && !qmlDisableDistanceField())
328             option.setUseDesignMetrics(true);
329         doc->setDefaultTextOption(option);
330         if (requireImplicitWidth && q->widthValid()) {
331             doc->setTextWidth(-1);
332             naturalWidth = doc->idealWidth();
333         }
334         if (wrapMode != QQuickText::NoWrap && q->widthValid())
335             doc->setTextWidth(q->width());
336         else
337             doc->setTextWidth(doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug)
338         dy -= (int)doc->size().height();
339         QSize dsize = doc->size().toSize();
340         layedOutTextRect = QRect(QPoint(0,0), dsize);
341         size = QSize(int(doc->idealWidth()),dsize.height());
342     }
343     int yoff = 0;
344
345     if (q->heightValid()) {
346         if (vAlign == QQuickText::AlignBottom)
347             yoff = dy;
348         else if (vAlign == QQuickText::AlignVCenter)
349             yoff = dy/2;
350     }
351     q->setBaselineOffset(fm.ascent() + yoff);
352
353     //### need to comfirm cost of always setting these for richText
354     internalWidthUpdate = true;
355     if (!q->widthValid())
356         q->setImplicitWidth(size.width());
357     else if (requireImplicitWidth)
358         q->setImplicitWidth(naturalWidth);
359     internalWidthUpdate = false;
360
361     q->setImplicitHeight(size.height());
362     if (paintedSize != size) {
363         paintedSize = size;
364         emit q->paintedSizeChanged();
365     }
366     q->update();
367 }
368
369 QQuickTextLine::QQuickTextLine()
370     : QObject(), m_line(0), m_height(0)
371 {
372 }
373
374 void QQuickTextLine::setLine(QTextLine *line)
375 {
376     m_line = line;
377 }
378
379 int QQuickTextLine::number() const
380 {
381     if (m_line)
382         return m_line->lineNumber();
383     return 0;
384 }
385
386 qreal QQuickTextLine::width() const
387 {
388     if (m_line)
389         return m_line->width();
390     return 0;
391 }
392
393 void QQuickTextLine::setWidth(qreal width)
394 {
395     if (m_line)
396         m_line->setLineWidth(width);
397 }
398
399 qreal QQuickTextLine::height() const
400 {
401     if (m_height)
402         return m_height;
403     if (m_line)
404         return m_line->height();
405     return 0;
406 }
407
408 void QQuickTextLine::setHeight(qreal height)
409 {
410     if (m_line)
411         m_line->setPosition(QPointF(m_line->x(), m_line->y() - m_line->height() + height));
412     m_height = height;
413 }
414
415 qreal QQuickTextLine::x() const
416 {
417     if (m_line)
418         return m_line->x();
419     return 0;
420 }
421
422 void QQuickTextLine::setX(qreal x)
423 {
424     if (m_line)
425         m_line->setPosition(QPointF(x, m_line->y()));
426 }
427
428 qreal QQuickTextLine::y() const
429 {
430     if (m_line)
431         return m_line->y();
432     return 0;
433 }
434
435 void QQuickTextLine::setY(qreal y)
436 {
437     if (m_line)
438         m_line->setPosition(QPointF(m_line->x(), y));
439 }
440
441 void QQuickText::doLayout()
442 {
443     Q_D(QQuickText);
444     d->updateSize();
445 }
446
447 /*!
448     \qmlsignal QtQuick2::Text::onLineLaidOut(line)
449
450     This handler is called for every line during the layout process.
451     This gives the opportunity to position and resize a line as it is being laid out.
452     It can for example be used to create columns or lay out text around objects.
453
454     The properties of a line are:
455     \list
456     \o number (read-only)
457     \o x
458     \o y
459     \o width
460     \o height
461     \endlist
462
463     For example, this will move the first 5 lines of a text element by 100 pixels to the right:
464     \code
465     onLineLaidOut: {
466         if (line.number < 5) {
467             line.x = line.x + 100
468             line.width = line.width - 100
469         }
470     }
471     \endcode
472 */
473
474 bool QQuickTextPrivate::isLineLaidOutConnected()
475 {
476     static int idx = this->signalIndex("lineLaidOut(QQuickTextLine*)");
477     return this->isSignalConnected(idx);
478 }
479
480 void QQuickTextPrivate::setupCustomLineGeometry(QTextLine &line, qreal &height, qreal elideWidth = 0)
481 {
482     Q_Q(QQuickText);
483
484 #if defined(Q_OS_MAC)
485     if (QThread::currentThread() != paintingThread) {
486 #endif
487         if (!line.lineNumber())
488             linesRects.clear();
489
490         if (!textLine)
491             textLine = new QQuickTextLine;
492         textLine->setLine(&line);
493         textLine->setY(height);
494         textLine->setHeight(0);
495
496         // use the text item's width by default if it has one and wrap is on
497         if (q->widthValid() && q->wrapMode() != QQuickText::NoWrap)
498             textLine->setWidth(q->width() - elideWidth);
499         else
500             textLine->setWidth(INT_MAX);
501         if (lineHeight != 1.0)
502             textLine->setHeight((lineHeightMode == QQuickText::FixedHeight) ? lineHeight : line.height() * lineHeight);
503
504         emit q->lineLaidOut(textLine);
505
506         linesRects << QRectF(textLine->x(), textLine->y(), textLine->width(), textLine->height());
507         height += textLine->height();
508
509 #if defined(Q_OS_MAC)
510     } else {
511         if (line.lineNumber() < linesRects.count()) {
512             QRectF r = linesRects.at(line.lineNumber());
513             line.setLineWidth(r.width());
514             line.setPosition(r.topLeft());
515         }
516     }
517 #endif
518 }
519
520 /*!
521     Lays out the QQuickTextPrivate::layout QTextLayout in the constraints of the QQuickText.
522
523     Returns the size of the final text.  This can be used to position the text vertically (the text is
524     already absolutely positioned horizontally).
525 */
526 QRect QQuickTextPrivate::setupTextLayout()
527 {
528     // ### text layout handling should be profiled and optimized as needed
529     // what about QStackTextEngine engine(tmp, d->font.font()); QTextLayout textLayout(&engine);
530     Q_Q(QQuickText);
531     layout.setCacheEnabled(true);
532
533     qreal lineWidth = 0;
534     int visibleCount = 0;
535
536     //set manual width
537     if (q->widthValid())
538         lineWidth = q->width();
539
540     QTextOption textOption = layout.textOption();
541     textOption.setAlignment(Qt::Alignment(q->effectiveHAlign()));
542     textOption.setWrapMode(QTextOption::WrapMode(wrapMode));
543     if (!cacheAllTextAsImage && !richTextAsImage && !qmlDisableDistanceField())
544         textOption.setUseDesignMetrics(true);
545     layout.setTextOption(textOption);
546
547     bool elideText = false;
548     bool truncate = false;
549
550     QFontMetrics fm(layout.font());
551     elidePos = QPointF();
552
553     if (requireImplicitWidth && q->widthValid()) {
554         // requires an extra layout
555         QString elidedText;
556         if (layoutTextElided) {
557             // We have provided elided text to the layout, but we must calculate unelided width.
558             elidedText = layout.text();
559             layout.setText(text);
560         }
561         layout.beginLayout();
562         forever {
563             QTextLine line = layout.createLine();
564             if (!line.isValid())
565                 break;
566         }
567         layout.endLayout();
568         QRectF br;
569         for (int i = 0; i < layout.lineCount(); ++i) {
570             QTextLine line = layout.lineAt(i);
571             br = br.united(line.naturalTextRect());
572         }
573         naturalWidth = br.width();
574         if (layoutTextElided)
575             layout.setText(elidedText);
576     }
577
578     qreal height = 0;
579     bool customLayout = isLineLaidOutConnected();
580
581     if (maximumLineCountValid) {
582         layout.beginLayout();
583         if (!lineWidth)
584             lineWidth = INT_MAX;
585         int linesLeft = maximumLineCount;
586         int visibleTextLength = 0;
587         while (linesLeft > 0) {
588             QTextLine line = layout.createLine();
589             if (!line.isValid())
590                 break;
591
592             visibleCount++;
593
594             if (customLayout)
595                 setupCustomLineGeometry(line, height);
596             else if (lineWidth)
597                 line.setLineWidth(lineWidth);
598             visibleTextLength += line.textLength();
599
600             if (--linesLeft == 0) {
601                 if (visibleTextLength < text.length()) {
602                     truncate = true;
603                     if (elideMode == QQuickText::ElideRight && q->widthValid()) {
604                         qreal elideWidth = fm.width(elideChar);
605                         // Need to correct for alignment
606                         if (customLayout)
607                             setupCustomLineGeometry(line, height, elideWidth);
608                         else
609                             line.setLineWidth(lineWidth - elideWidth);
610                         if (layout.text().mid(line.textStart(), line.textLength()).isRightToLeft()) {
611                             line.setPosition(QPointF(line.position().x() + elideWidth, line.position().y()));
612                             elidePos.setX(line.naturalTextRect().left() - elideWidth);
613                         } else {
614                             elidePos.setX(line.naturalTextRect().right());
615                         }
616                         elideText = true;
617                     }
618                 }
619             }
620         }
621         layout.endLayout();
622
623         //Update truncated
624         if (truncated != truncate) {
625             truncated = truncate;
626             emit q->truncatedChanged();
627         }
628     } else {
629         layout.beginLayout();
630         forever {
631             QTextLine line = layout.createLine();
632             if (!line.isValid())
633                 break;
634             visibleCount++;
635             if (customLayout)
636                 setupCustomLineGeometry(line, height);
637             else {
638                 if (lineWidth)
639                     line.setLineWidth(lineWidth);
640             }
641         }
642         layout.endLayout();
643     }
644
645     height = 0;
646     QRectF br;
647     for (int i = 0; i < layout.lineCount(); ++i) {
648         QTextLine line = layout.lineAt(i);
649         // set line spacing
650         if (!customLayout)
651             line.setPosition(QPointF(line.position().x(), height));
652         if (elideText && i == layout.lineCount()-1) {
653             elidePos.setY(height + fm.ascent());
654             br = br.united(QRectF(elidePos, QSizeF(fm.width(elideChar), fm.ascent())));
655         }
656         br = br.united(line.naturalTextRect());
657         height += (lineHeightMode == QQuickText::FixedHeight) ? lineHeight : line.height() * lineHeight;
658     }
659     if (!customLayout)
660         br.setHeight(height);
661
662     if (!q->widthValid())
663         naturalWidth = br.width();
664
665     //Update the number of visible lines
666     if (lineCount != visibleCount) {
667         lineCount = visibleCount;
668         emit q->lineCountChanged();
669     }
670
671     return QRect(qRound(br.x()), qRound(br.y()), qCeil(br.width()), qCeil(br.height()));
672 }
673
674 /*!
675     Returns a painted version of the QQuickTextPrivate::layout QTextLayout.
676     If \a drawStyle is true, the style color overrides all colors in the document.
677 */
678 QPixmap QQuickTextPrivate::textLayoutImage(bool drawStyle)
679 {
680     QSize size = layedOutTextRect.size();
681
682     //paint text
683     QPixmap img(size);
684     if (!size.isEmpty()) {
685         img.fill(Qt::transparent);
686 /*#ifdef Q_OS_MAC // Fails on CocoaX64
687         bool oldSmooth = qt_applefontsmoothing_enabled;
688         qt_applefontsmoothing_enabled = false;
689 #endif*/
690         QPainter p(&img);
691 /*#ifdef Q_OS_MAC // Fails on CocoaX64
692         qt_applefontsmoothing_enabled = oldSmooth;
693 #endif*/
694         drawTextLayout(&p, QPointF(-layedOutTextRect.x(),0), drawStyle);
695     }
696     return img;
697 }
698
699 /*!
700     Paints the QQuickTextPrivate::layout QTextLayout into \a painter at \a pos.  If
701     \a drawStyle is true, the style color overrides all colors in the document.
702 */
703 void QQuickTextPrivate::drawTextLayout(QPainter *painter, const QPointF &pos, bool drawStyle)
704 {
705     if (drawStyle)
706         painter->setPen(styleColor);
707     else
708         painter->setPen(color);
709     painter->setFont(font);
710     layout.draw(painter, pos);
711     if (!elidePos.isNull())
712         painter->drawText(pos + elidePos, elideChar);
713 }
714
715 /*!
716     Returns a painted version of the QQuickTextPrivate::doc QTextDocument.
717     If \a drawStyle is true, the style color overrides all colors in the document.
718 */
719 QPixmap QQuickTextPrivate::textDocumentImage(bool drawStyle)
720 {
721     QSize size = doc->size().toSize();
722
723     //paint text
724     QPixmap img(size);
725     img.fill(Qt::transparent);
726 /*#ifdef Q_OS_MAC // Fails on CocoaX64
727     bool oldSmooth = qt_applefontsmoothing_enabled;
728     qt_applefontsmoothing_enabled = false;
729 #endif*/
730     QPainter p(&img);
731 /*#ifdef Q_OS_MAC // Fails on CocoaX64
732     qt_applefontsmoothing_enabled = oldSmooth;
733 #endif*/
734
735     QAbstractTextDocumentLayout::PaintContext context;
736
737     QTextOption oldOption(doc->defaultTextOption());
738     if (drawStyle) {
739         context.palette.setColor(QPalette::Text, styleColor);
740         QTextOption colorOption(doc->defaultTextOption());
741         colorOption.setFlags(QTextOption::SuppressColors);
742         doc->setDefaultTextOption(colorOption);
743     } else {
744         context.palette.setColor(QPalette::Text, color);
745     }
746     doc->documentLayout()->draw(&p, context);
747     if (drawStyle)
748         doc->setDefaultTextOption(oldOption);
749     return img;
750 }
751
752 /*!
753     Mark the image cache as dirty.
754 */
755 void QQuickTextPrivate::invalidateImageCache()
756 {
757     Q_Q(QQuickText);
758
759     if (richTextAsImage || cacheAllTextAsImage || (qmlDisableDistanceField() && style != QQuickText::Normal)) { // If actually using the image cache
760         if (imageCacheDirty)
761             return;
762
763         imageCacheDirty = true;
764
765         if (q->isComponentComplete())
766             QCoreApplication::postEvent(q, new QEvent(QEvent::User));
767     } else if (q->isComponentComplete())
768         q->update();
769 }
770
771 /*!
772     Tests if the image cache is dirty, and repaints it if it is.
773 */
774 void QQuickTextPrivate::checkImageCache()
775 {
776     Q_Q(QQuickText);
777
778     if (!imageCacheDirty)
779         return;
780
781     if (text.isEmpty()) {
782
783         imageCache = QPixmap();
784
785     } else {
786
787         QPixmap textImage;
788         QPixmap styledImage;
789
790         if (richText) {
791             textImage = textDocumentImage(false);
792             if (style != QQuickText::Normal)
793                 styledImage = textDocumentImage(true); //### should use styleColor
794         } else {
795             textImage = textLayoutImage(false);
796             if (style != QQuickText::Normal)
797                 styledImage = textLayoutImage(true); //### should use styleColor
798         }
799
800         switch (style) {
801         case QQuickText::Outline:
802             imageCache = drawOutline(textImage, styledImage);
803             break;
804         case QQuickText::Sunken:
805             imageCache = drawOutline(textImage, styledImage, -1);
806             break;
807         case QQuickText::Raised:
808             imageCache = drawOutline(textImage, styledImage, 1);
809             break;
810         default:
811             imageCache = textImage;
812             break;
813         }
814
815     }
816
817     imageCacheDirty = false;
818     textureImageCacheDirty = true;
819     q->update();
820 }
821
822 /*!
823     Ensures the QQuickTextPrivate::doc variable is set to a valid text document
824 */
825 void QQuickTextPrivate::ensureDoc()
826 {
827     if (!doc) {
828         Q_Q(QQuickText);
829         doc = new QQuickTextDocumentWithImageResources(q);
830         doc->setDocumentMargin(0);
831     }
832 }
833
834 /*!
835     Draw \a styleSource as an outline around \a source and return the new image.
836 */
837 QPixmap QQuickTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource)
838 {
839     QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
840     img.fill(Qt::transparent);
841
842     QPainter ppm(&img);
843
844     QPoint pos(0, 0);
845     pos += QPoint(-1, 0);
846     ppm.drawPixmap(pos, styleSource);
847     pos += QPoint(2, 0);
848     ppm.drawPixmap(pos, styleSource);
849     pos += QPoint(-1, -1);
850     ppm.drawPixmap(pos, styleSource);
851     pos += QPoint(0, 2);
852     ppm.drawPixmap(pos, styleSource);
853
854     pos += QPoint(0, -1);
855     ppm.drawPixmap(pos, source);
856     ppm.end();
857
858     return img;
859 }
860
861 /*!
862     Draw \a styleSource below \a source at \a yOffset and return the new image.
863 */
864 QPixmap QQuickTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource, int yOffset)
865 {
866     QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
867     img.fill(Qt::transparent);
868
869     QPainter ppm(&img);
870
871     ppm.drawPixmap(QPoint(0, yOffset), styleSource);
872     ppm.drawPixmap(0, 0, source);
873
874     ppm.end();
875
876     return img;
877 }
878
879 /*!
880     \qmlclass Text QQuickText
881     \inqmlmodule QtQuick 2
882     \ingroup qml-basic-visual-elements
883     \brief The Text item allows you to add formatted text to a scene.
884     \inherits Item
885
886     Text items can display both plain and rich text. For example, red text with
887     a specific font and size can be defined like this:
888
889     \qml
890     Text {
891         text: "Hello World!"
892         font.family: "Helvetica"
893         font.pointSize: 24
894         color: "red"
895     }
896     \endqml
897
898     Rich text is defined using HTML-style markup:
899
900     \qml
901     Text {
902         text: "<b>Hello</b> <i>World!</i>"
903     }
904     \endqml
905
906     \image declarative-text.png
907
908     If height and width are not explicitly set, Text will attempt to determine how
909     much room is needed and set it accordingly. Unless \l wrapMode is set, it will always
910     prefer width to height (all text will be placed on a single line).
911
912     The \l elide property can alternatively be used to fit a single line of
913     plain text to a set width.
914
915     Note that the \l{Supported HTML Subset} is limited. Also, if the text contains
916     HTML img tags that load remote images, the text is reloaded.
917
918     Text provides read-only text. For editable text, see \l TextEdit.
919
920     \sa {declarative/text/fonts}{Fonts example}
921 */
922 QQuickText::QQuickText(QQuickItem *parent)
923 : QQuickImplicitSizeItem(*(new QQuickTextPrivate), parent)
924 {
925     Q_D(QQuickText);
926     d->init();
927 }
928
929 QQuickText::~QQuickText()
930 {
931 }
932
933 /*!
934   \qmlproperty bool QtQuick2::Text::clip
935   This property holds whether the text is clipped.
936
937   Note that if the text does not fit in the bounding rectangle it will be abruptly chopped.
938
939   If you want to display potentially long text in a limited space, you probably want to use \c elide instead.
940 */
941
942 /*!
943     \qmlproperty bool QtQuick2::Text::smooth
944
945     This property holds whether the text is smoothly scaled or transformed.
946
947     Smooth filtering gives better visual quality, but is slower.  If
948     the item is displayed at its natural size, this property has no visual or
949     performance effect.
950
951     \note Generally scaling artifacts are only visible if the item is stationary on
952     the screen.  A common pattern when animating an item is to disable smooth
953     filtering at the beginning of the animation and reenable it at the conclusion.
954 */
955
956 /*!
957     \qmlsignal QtQuick2::Text::onLinkActivated(string link)
958
959     This handler is called when the user clicks on a link embedded in the text.
960     The link must be in rich text or HTML format and the
961     \a link string provides access to the particular link.
962
963     \snippet doc/src/snippets/declarative/text/onLinkActivated.qml 0
964
965     The example code will display the text
966     "The main website is at \l{http://qt.nokia.com}{Nokia Qt DF}."
967
968     Clicking on the highlighted link will output
969     \tt{http://qt.nokia.com link activated} to the console.
970 */
971
972 /*!
973     \qmlproperty string QtQuick2::Text::font.family
974
975     Sets the family name of the font.
976
977     The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
978     If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
979     If the family isn't available a family will be set using the font matching algorithm.
980 */
981
982 /*!
983     \qmlproperty bool QtQuick2::Text::font.bold
984
985     Sets whether the font weight is bold.
986 */
987
988 /*!
989     \qmlproperty enumeration QtQuick2::Text::font.weight
990
991     Sets the font's weight.
992
993     The weight can be one of:
994     \list
995     \o Font.Light
996     \o Font.Normal - the default
997     \o Font.DemiBold
998     \o Font.Bold
999     \o Font.Black
1000     \endlist
1001
1002     \qml
1003     Text { text: "Hello"; font.weight: Font.DemiBold }
1004     \endqml
1005 */
1006
1007 /*!
1008     \qmlproperty bool QtQuick2::Text::font.italic
1009
1010     Sets whether the font has an italic style.
1011 */
1012
1013 /*!
1014     \qmlproperty bool QtQuick2::Text::font.underline
1015
1016     Sets whether the text is underlined.
1017 */
1018
1019 /*!
1020     \qmlproperty bool QtQuick2::Text::font.strikeout
1021
1022     Sets whether the font has a strikeout style.
1023 */
1024
1025 /*!
1026     \qmlproperty real QtQuick2::Text::font.pointSize
1027
1028     Sets the font size in points. The point size must be greater than zero.
1029 */
1030
1031 /*!
1032     \qmlproperty int QtQuick2::Text::font.pixelSize
1033
1034     Sets the font size in pixels.
1035
1036     Using this function makes the font device dependent.
1037     Use \c pointSize to set the size of the font in a device independent manner.
1038 */
1039
1040 /*!
1041     \qmlproperty real QtQuick2::Text::font.letterSpacing
1042
1043     Sets the letter spacing for the font.
1044
1045     Letter spacing changes the default spacing between individual letters in the font.
1046     A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
1047 */
1048
1049 /*!
1050     \qmlproperty real QtQuick2::Text::font.wordSpacing
1051
1052     Sets the word spacing for the font.
1053
1054     Word spacing changes the default spacing between individual words.
1055     A positive value increases the word spacing by a corresponding amount of pixels,
1056     while a negative value decreases the inter-word spacing accordingly.
1057 */
1058
1059 /*!
1060     \qmlproperty enumeration QtQuick2::Text::font.capitalization
1061
1062     Sets the capitalization for the text.
1063
1064     \list
1065     \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
1066     \o Font.AllUppercase - This alters the text to be rendered in all uppercase type.
1067     \o Font.AllLowercase - This alters the text to be rendered in all lowercase type.
1068     \o Font.SmallCaps - This alters the text to be rendered in small-caps type.
1069     \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
1070     \endlist
1071
1072     \qml
1073     Text { text: "Hello"; font.capitalization: Font.AllLowercase }
1074     \endqml
1075 */
1076 QFont QQuickText::font() const
1077 {
1078     Q_D(const QQuickText);
1079     return d->sourceFont;
1080 }
1081
1082 void QQuickText::setFont(const QFont &font)
1083 {
1084     Q_D(QQuickText);
1085     if (d->sourceFont == font)
1086         return;
1087
1088     d->sourceFont = font;
1089     QFont oldFont = d->font;
1090     d->font = font;
1091
1092     if (d->font.pointSizeF() != -1) {
1093         // 0.5pt resolution
1094         qreal size = qRound(d->font.pointSizeF()*2.0);
1095         d->font.setPointSizeF(size/2.0);
1096     }
1097
1098     if (oldFont != d->font)
1099         d->updateLayout();
1100
1101     emit fontChanged(d->sourceFont);
1102 }
1103
1104 /*!
1105     \qmlproperty string QtQuick2::Text::text
1106
1107     The text to display. Text supports both plain and rich text strings.
1108
1109     The item will try to automatically determine whether the text should
1110     be treated as styled text. This determination is made using Qt::mightBeRichText().
1111 */
1112 QString QQuickText::text() const
1113 {
1114     Q_D(const QQuickText);
1115     return d->text;
1116 }
1117
1118 void QQuickText::setText(const QString &n)
1119 {
1120     Q_D(QQuickText);
1121     if (d->text == n)
1122         return;
1123
1124     d->richText = d->format == RichText;
1125     d->styledText = d->format == StyledText || (d->format == AutoText && Qt::mightBeRichText(n));
1126     d->text = n;
1127     if (isComponentComplete()) {
1128         if (d->richText) {
1129             d->ensureDoc();
1130             d->doc->setText(n);
1131             d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
1132             d->richTextAsImage = enableImageCache();
1133         } else {
1134             d->rightToLeftText = d->text.isRightToLeft();
1135         }
1136         d->determineHorizontalAlignment();
1137     }
1138     d->textHasChanged = true;
1139     d->updateLayout();
1140     emit textChanged(d->text);
1141 }
1142
1143 /*!
1144     \qmlproperty color QtQuick2::Text::color
1145
1146     The text color.
1147
1148     An example of green text defined using hexadecimal notation:
1149     \qml
1150     Text {
1151         color: "#00FF00"
1152         text: "green text"
1153     }
1154     \endqml
1155
1156     An example of steel blue text defined using an SVG color name:
1157     \qml
1158     Text {
1159         color: "steelblue"
1160         text: "blue text"
1161     }
1162     \endqml
1163 */
1164 QColor QQuickText::color() const
1165 {
1166     Q_D(const QQuickText);
1167     return d->color;
1168 }
1169
1170 void QQuickText::setColor(const QColor &color)
1171 {
1172     Q_D(QQuickText);
1173     if (d->color == color)
1174         return;
1175
1176     d->color = color;
1177     d->invalidateImageCache();
1178     emit colorChanged(d->color);
1179 }
1180 /*!
1181     \qmlproperty enumeration QtQuick2::Text::style
1182
1183     Set an additional text style.
1184
1185     Supported text styles are:
1186     \list
1187     \o Text.Normal - the default
1188     \o Text.Outline
1189     \o Text.Raised
1190     \o Text.Sunken
1191     \endlist
1192
1193     \qml
1194     Row {
1195         Text { font.pointSize: 24; text: "Normal" }
1196         Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" }
1197         Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" }
1198         Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" }
1199     }
1200     \endqml
1201
1202     \image declarative-textstyle.png
1203 */
1204 QQuickText::TextStyle QQuickText::style() const
1205 {
1206     Q_D(const QQuickText);
1207     return d->style;
1208 }
1209
1210 void QQuickText::setStyle(QQuickText::TextStyle style)
1211 {
1212     Q_D(QQuickText);
1213     if (d->style == style)
1214         return;
1215
1216     // changing to/from Normal requires the boundingRect() to change
1217     if (isComponentComplete() && (d->style == Normal || style == Normal))
1218         update();
1219     d->style = style;
1220     d->invalidateImageCache();
1221     emit styleChanged(d->style);
1222 }
1223
1224 /*!
1225     \qmlproperty color QtQuick2::Text::styleColor
1226
1227     Defines the secondary color used by text styles.
1228
1229     \c styleColor is used as the outline color for outlined text, and as the
1230     shadow color for raised or sunken text. If no style has been set, it is not
1231     used at all.
1232
1233     \qml
1234     Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" }
1235     \endqml
1236
1237     \sa style
1238  */
1239 QColor QQuickText::styleColor() const
1240 {
1241     Q_D(const QQuickText);
1242     return d->styleColor;
1243 }
1244
1245 void QQuickText::setStyleColor(const QColor &color)
1246 {
1247     Q_D(QQuickText);
1248     if (d->styleColor == color)
1249         return;
1250
1251     d->styleColor = color;
1252     d->invalidateImageCache();
1253     emit styleColorChanged(d->styleColor);
1254 }
1255
1256 /*!
1257     \qmlproperty enumeration QtQuick2::Text::horizontalAlignment
1258     \qmlproperty enumeration QtQuick2::Text::verticalAlignment
1259     \qmlproperty enumeration QtQuick2::Text::effectiveHorizontalAlignment
1260
1261     Sets the horizontal and vertical alignment of the text within the Text items
1262     width and height. By default, the text is vertically aligned to the top. Horizontal
1263     alignment follows the natural alignment of the text, for example text that is read
1264     from left to right will be aligned to the left.
1265
1266     The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight, \c Text.AlignHCenter and
1267     \c Text.AlignJustify.  The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom
1268     and \c Text.AlignVCenter.
1269
1270     Note that for a single line of text, the size of the text is the area of the text. In this common case,
1271     all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will
1272     need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to
1273     that of the parent.
1274
1275     When using the attached property LayoutMirroring::enabled to mirror application
1276     layouts, the horizontal alignment of text will also be mirrored. However, the property
1277     \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
1278     of Text, use the read-only property \c effectiveHorizontalAlignment.
1279 */
1280 QQuickText::HAlignment QQuickText::hAlign() const
1281 {
1282     Q_D(const QQuickText);
1283     return d->hAlign;
1284 }
1285
1286 void QQuickText::setHAlign(HAlignment align)
1287 {
1288     Q_D(QQuickText);
1289     bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
1290     d->hAlignImplicit = false;
1291     if (d->setHAlign(align, forceAlign) && isComponentComplete())
1292         d->updateLayout();
1293 }
1294
1295 void QQuickText::resetHAlign()
1296 {
1297     Q_D(QQuickText);
1298     d->hAlignImplicit = true;
1299     if (d->determineHorizontalAlignment() && isComponentComplete())
1300         d->updateLayout();
1301 }
1302
1303 QQuickText::HAlignment QQuickText::effectiveHAlign() const
1304 {
1305     Q_D(const QQuickText);
1306     QQuickText::HAlignment effectiveAlignment = d->hAlign;
1307     if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
1308         switch (d->hAlign) {
1309         case QQuickText::AlignLeft:
1310             effectiveAlignment = QQuickText::AlignRight;
1311             break;
1312         case QQuickText::AlignRight:
1313             effectiveAlignment = QQuickText::AlignLeft;
1314             break;
1315         default:
1316             break;
1317         }
1318     }
1319     return effectiveAlignment;
1320 }
1321
1322 bool QQuickTextPrivate::setHAlign(QQuickText::HAlignment alignment, bool forceAlign)
1323 {
1324     Q_Q(QQuickText);
1325     if (hAlign != alignment || forceAlign) {
1326         QQuickText::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
1327         hAlign = alignment;
1328
1329         emit q->horizontalAlignmentChanged(hAlign);
1330         if (oldEffectiveHAlign != q->effectiveHAlign())
1331             emit q->effectiveHorizontalAlignmentChanged();
1332         return true;
1333     }
1334     return false;
1335 }
1336
1337 bool QQuickTextPrivate::determineHorizontalAlignment()
1338 {
1339     Q_Q(QQuickText);
1340     if (hAlignImplicit && q->isComponentComplete()) {
1341         bool alignToRight = text.isEmpty() ? QGuiApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText;
1342         return setHAlign(alignToRight ? QQuickText::AlignRight : QQuickText::AlignLeft);
1343     }
1344     return false;
1345 }
1346
1347 void QQuickTextPrivate::mirrorChange()
1348 {
1349     Q_Q(QQuickText);
1350     if (q->isComponentComplete()) {
1351         if (!hAlignImplicit && (hAlign == QQuickText::AlignRight || hAlign == QQuickText::AlignLeft)) {
1352             updateLayout();
1353             emit q->effectiveHorizontalAlignmentChanged();
1354         }
1355     }
1356 }
1357
1358 QTextDocument *QQuickTextPrivate::textDocument()
1359 {
1360     return doc;
1361 }
1362
1363 QQuickText::VAlignment QQuickText::vAlign() const
1364 {
1365     Q_D(const QQuickText);
1366     return d->vAlign;
1367 }
1368
1369 void QQuickText::setVAlign(VAlignment align)
1370 {
1371     Q_D(QQuickText);
1372     if (d->vAlign == align)
1373         return;
1374
1375     d->vAlign = align;
1376     emit verticalAlignmentChanged(align);
1377 }
1378
1379 /*!
1380     \qmlproperty enumeration QtQuick2::Text::wrapMode
1381
1382     Set this property to wrap the text to the Text item's width.  The text will only
1383     wrap if an explicit width has been set.  wrapMode can be one of:
1384
1385     \list
1386     \o Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l paintedWidth will exceed a set width.
1387     \o Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l paintedWidth will exceed a set width.
1388     \o Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
1389     \o Text.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word.
1390     \endlist
1391 */
1392 QQuickText::WrapMode QQuickText::wrapMode() const
1393 {
1394     Q_D(const QQuickText);
1395     return d->wrapMode;
1396 }
1397
1398 void QQuickText::setWrapMode(WrapMode mode)
1399 {
1400     Q_D(QQuickText);
1401     if (mode == d->wrapMode)
1402         return;
1403
1404     d->wrapMode = mode;
1405     d->updateLayout();
1406
1407     emit wrapModeChanged();
1408 }
1409
1410 /*!
1411     \qmlproperty int QtQuick2::Text::lineCount
1412
1413     Returns the number of lines visible in the text item.
1414
1415     This property is not supported for rich text.
1416
1417     \sa maximumLineCount
1418 */
1419 int QQuickText::lineCount() const
1420 {
1421     Q_D(const QQuickText);
1422     return d->lineCount;
1423 }
1424
1425 /*!
1426     \qmlproperty bool QtQuick2::Text::truncated
1427
1428     Returns true if the text has been truncated due to \l maximumLineCount
1429     or \l elide.
1430
1431     This property is not supported for rich text.
1432
1433     \sa maximumLineCount, elide
1434 */
1435 bool QQuickText::truncated() const
1436 {
1437     Q_D(const QQuickText);
1438     return d->truncated;
1439 }
1440
1441 /*!
1442     \qmlproperty int QtQuick2::Text::maximumLineCount
1443
1444     Set this property to limit the number of lines that the text item will show.
1445     If elide is set to Text.ElideRight, the text will be elided appropriately.
1446     By default, this is the value of the largest possible integer.
1447
1448     This property is not supported for rich text.
1449
1450     \sa lineCount, elide
1451 */
1452 int QQuickText::maximumLineCount() const
1453 {
1454     Q_D(const QQuickText);
1455     return d->maximumLineCount;
1456 }
1457
1458 void QQuickText::setMaximumLineCount(int lines)
1459 {
1460     Q_D(QQuickText);
1461
1462     d->maximumLineCountValid = lines==INT_MAX ? false : true;
1463     if (d->maximumLineCount != lines) {
1464         d->maximumLineCount = lines;
1465         d->updateLayout();
1466         emit maximumLineCountChanged();
1467     }
1468 }
1469
1470 void QQuickText::resetMaximumLineCount()
1471 {
1472     Q_D(QQuickText);
1473     setMaximumLineCount(INT_MAX);
1474     d->elidePos = QPointF();
1475     if (d->truncated != false) {
1476         d->truncated = false;
1477         emit truncatedChanged();
1478     }
1479 }
1480
1481 /*!
1482     \qmlproperty enumeration QtQuick2::Text::textFormat
1483
1484     The way the text property should be displayed.
1485
1486     Supported text formats are:
1487
1488     \list
1489     \o Text.AutoText (default)
1490     \o Text.PlainText
1491     \o Text.StyledText
1492     \o Text.RichText
1493     \endlist
1494
1495     If the text format is \c Text.AutoText the text element
1496     will automatically determine whether the text should be treated as
1497     styled text.  This determination is made using Qt::mightBeRichText().
1498
1499     Text.StyledText is an optimized format supporting some basic text
1500     styling markup, in the style of html 3.2:
1501
1502     \code
1503     <b></b> - bold
1504     <i></i> - italic
1505     <br> - new line
1506     <p> - paragraph
1507     <u> - underlined text
1508     <font color="color_name" size="1-7"></font>
1509     <h1> to <h6> - headers
1510     <a href=""> - anchor
1511     <ol type="">, <ul type=""> and <li> - ordered and unordered lists
1512     &gt; &lt; &amp;
1513     \endcode
1514
1515     \c Text.StyledText parser is strict, requiring tags to be correctly nested.
1516
1517     \table
1518     \row
1519     \o
1520     \qml
1521 Column {
1522     Text {
1523         font.pointSize: 24
1524         text: "<b>Hello</b> <i>World!</i>"
1525     }
1526     Text {
1527         font.pointSize: 24
1528         textFormat: Text.RichText
1529         text: "<b>Hello</b> <i>World!</i>"
1530     }
1531     Text {
1532         font.pointSize: 24
1533         textFormat: Text.PlainText
1534         text: "<b>Hello</b> <i>World!</i>"
1535     }
1536 }
1537     \endqml
1538     \o \image declarative-textformat.png
1539     \endtable
1540 */
1541 QQuickText::TextFormat QQuickText::textFormat() const
1542 {
1543     Q_D(const QQuickText);
1544     return d->format;
1545 }
1546
1547 void QQuickText::setTextFormat(TextFormat format)
1548 {
1549     Q_D(QQuickText);
1550     if (format == d->format)
1551         return;
1552     d->format = format;
1553     bool wasRich = d->richText;
1554     d->richText = format == RichText;
1555     d->styledText = format == StyledText || (format == AutoText && Qt::mightBeRichText(d->text));
1556
1557     if (!wasRich && d->richText && isComponentComplete()) {
1558         d->ensureDoc();
1559         d->doc->setText(d->text);
1560         d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
1561         d->richTextAsImage = enableImageCache();
1562     } else {
1563         d->rightToLeftText = d->text.isRightToLeft();
1564     }
1565     d->determineHorizontalAlignment();
1566     d->updateLayout();
1567
1568     emit textFormatChanged(d->format);
1569 }
1570
1571 /*!
1572     \qmlproperty enumeration QtQuick2::Text::elide
1573
1574     Set this property to elide parts of the text fit to the Text item's width.
1575     The text will only elide if an explicit width has been set.
1576
1577     This property cannot be used with rich text.
1578
1579     Eliding can be:
1580     \list
1581     \o Text.ElideNone  - the default
1582     \o Text.ElideLeft
1583     \o Text.ElideMiddle
1584     \o Text.ElideRight
1585     \endlist
1586
1587     If this property is set to Text.ElideRight, it can be used with multiline
1588     text. The text will only elide if maximumLineCount has been set.
1589
1590     If the text is a multi-length string, and the mode is not \c Text.ElideNone,
1591     the first string that fits will be used, otherwise the last will be elided.
1592
1593     Multi-length strings are ordered from longest to shortest, separated by the
1594     Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}).
1595 */
1596 QQuickText::TextElideMode QQuickText::elideMode() const
1597 {
1598     Q_D(const QQuickText);
1599     return d->elideMode;
1600 }
1601
1602 void QQuickText::setElideMode(QQuickText::TextElideMode mode)
1603 {
1604     Q_D(QQuickText);
1605     if (mode == d->elideMode)
1606         return;
1607
1608     d->elideMode = mode;
1609     d->updateLayout();
1610
1611     emit elideModeChanged(d->elideMode);
1612 }
1613
1614 /*! \internal */
1615 QRectF QQuickText::boundingRect() const
1616 {
1617     Q_D(const QQuickText);
1618
1619     QRect rect = d->layedOutTextRect;
1620     if (d->style != Normal)
1621         rect.adjust(-1, 0, 1, 2);
1622
1623     // Could include font max left/right bearings to either side of rectangle.
1624
1625     int h = height();
1626     switch (d->vAlign) {
1627     case AlignTop:
1628         break;
1629     case AlignBottom:
1630         rect.moveTop(h - rect.height());
1631         break;
1632     case AlignVCenter:
1633         rect.moveTop((h - rect.height()) / 2);
1634         break;
1635     }
1636
1637     return QRectF(rect);
1638 }
1639
1640 /*! \internal */
1641 void QQuickText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
1642 {
1643     Q_D(QQuickText);
1644     if ((!d->internalWidthUpdate && newGeometry.width() != oldGeometry.width())
1645             && (d->wrapMode != QQuickText::NoWrap
1646                 || d->elideMode != QQuickText::ElideNone
1647                 || d->hAlign != QQuickText::AlignLeft)) {
1648         if ((d->singleline || d->maximumLineCountValid) && d->elideMode != QQuickText::ElideNone && widthValid()) {
1649             // We need to re-elide
1650             d->updateLayout();
1651         } else {
1652             // We just need to re-layout
1653             d->updateSize();
1654         }
1655     }
1656
1657     QQuickItem::geometryChanged(newGeometry, oldGeometry);
1658 }
1659
1660 QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
1661 {
1662     Q_UNUSED(data);
1663     Q_D(QQuickText);
1664
1665     if (d->text.isEmpty()) {
1666         delete oldNode;
1667         return 0;
1668     }
1669
1670     QRectF bounds = boundingRect();
1671
1672     // We need to make sure the layout is done in the current thread
1673 #if defined(Q_OS_MAC)
1674     d->paintingThread = QThread::currentThread();
1675     if (d->layoutThread != d->paintingThread)
1676         d->updateLayout();
1677 #endif
1678
1679     // XXX todo - some styled text can be done by the QQuickTextNode
1680     if (d->richTextAsImage || d->cacheAllTextAsImage || (qmlDisableDistanceField() && d->style != Normal)) {
1681         bool wasDirty = d->textureImageCacheDirty;
1682         d->textureImageCacheDirty = false;
1683
1684         if (d->imageCache.isNull()) {
1685             delete oldNode;
1686             return 0;
1687         }
1688
1689         QSGImageNode *node = 0;
1690         if (!oldNode || d->nodeType != QQuickTextPrivate::NodeIsTexture) {
1691             delete oldNode;
1692             node = QQuickItemPrivate::get(this)->sceneGraphContext()->createImageNode();
1693             d->texture = new QSGPlainTexture();
1694             wasDirty = true;
1695             d->nodeType = QQuickTextPrivate::NodeIsTexture;
1696         } else {
1697             node = static_cast<QSGImageNode *>(oldNode);
1698             Q_ASSERT(d->texture);
1699         }
1700
1701         if (wasDirty) {
1702             qobject_cast<QSGPlainTexture *>(d->texture)->setImage(d->imageCache.toImage());
1703             node->setTexture(0);
1704             node->setTexture(d->texture);
1705         }
1706
1707         node->setTargetRect(QRectF(bounds.x(), bounds.y(), d->imageCache.width(), d->imageCache.height()));
1708         node->setSourceRect(QRectF(0, 0, 1, 1));
1709         node->setHorizontalWrapMode(QSGTexture::ClampToEdge);
1710         node->setVerticalWrapMode(QSGTexture::ClampToEdge);
1711         node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that..
1712         node->update();
1713
1714         return node;
1715
1716     } else {
1717         QQuickTextNode *node = 0;
1718         if (!oldNode || d->nodeType != QQuickTextPrivate::NodeIsText) {
1719             delete oldNode;
1720             node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext());
1721             d->nodeType = QQuickTextPrivate::NodeIsText;
1722         } else {
1723             node = static_cast<QQuickTextNode *>(oldNode);
1724         }
1725
1726         node->deleteContent();
1727         node->setMatrix(QMatrix4x4());
1728
1729         if (d->richText) {
1730             d->ensureDoc();
1731             node->addTextDocument(bounds.topLeft(), d->doc, QColor(), d->style, d->styleColor);
1732
1733         } else {
1734             node->addTextLayout(QPoint(0, bounds.y()), &d->layout, d->color, d->style, d->styleColor);
1735         }
1736
1737         return node;
1738     }
1739 }
1740
1741 bool QQuickText::event(QEvent *e)
1742 {
1743     Q_D(QQuickText);
1744     if (e->type() == QEvent::User) {
1745         d->checkImageCache();
1746         return true;
1747     } else {
1748         return QQuickImplicitSizeItem::event(e);
1749     }
1750 }
1751
1752 /*!
1753     \qmlproperty real QtQuick2::Text::paintedWidth
1754
1755     Returns the width of the text, including width past the width
1756     which is covered due to insufficient wrapping if WrapMode is set.
1757 */
1758 qreal QQuickText::paintedWidth() const
1759 {
1760     Q_D(const QQuickText);
1761     return d->paintedSize.width();
1762 }
1763
1764 /*!
1765     \qmlproperty real QtQuick2::Text::paintedHeight
1766
1767     Returns the height of the text, including height past the height
1768     which is covered due to there being more text than fits in the set height.
1769 */
1770 qreal QQuickText::paintedHeight() const
1771 {
1772     Q_D(const QQuickText);
1773     return d->paintedSize.height();
1774 }
1775
1776 /*!
1777     \qmlproperty real QtQuick2::Text::lineHeight
1778
1779     Sets the line height for the text.
1780     The value can be in pixels or a multiplier depending on lineHeightMode.
1781
1782     The default value is a multiplier of 1.0.
1783     The line height must be a positive value.
1784 */
1785 qreal QQuickText::lineHeight() const
1786 {
1787     Q_D(const QQuickText);
1788     return d->lineHeight;
1789 }
1790
1791 void QQuickText::setLineHeight(qreal lineHeight)
1792 {
1793     Q_D(QQuickText);
1794
1795     if ((d->lineHeight == lineHeight) || (lineHeight < 0.0))
1796         return;
1797
1798     d->lineHeight = lineHeight;
1799     d->updateLayout();
1800     emit lineHeightChanged(lineHeight);
1801 }
1802
1803 /*!
1804     \qmlproperty enumeration QtQuick2::Text::lineHeightMode
1805
1806     This property determines how the line height is specified.
1807     The possible values are:
1808
1809     \list
1810     \o Text.ProportionalHeight (default) - this sets the spacing proportional to the
1811        line (as a multiplier). For example, set to 2 for double spacing.
1812     \o Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
1813     \endlist
1814 */
1815 QQuickText::LineHeightMode QQuickText::lineHeightMode() const
1816 {
1817     Q_D(const QQuickText);
1818     return d->lineHeightMode;
1819 }
1820
1821 void QQuickText::setLineHeightMode(LineHeightMode mode)
1822 {
1823     Q_D(QQuickText);
1824     if (mode == d->lineHeightMode)
1825         return;
1826
1827     d->lineHeightMode = mode;
1828     d->updateLayout();
1829
1830     emit lineHeightModeChanged(mode);
1831 }
1832
1833 /*!
1834     Returns the number of resources (images) that are being loaded asynchronously.
1835 */
1836 int QQuickText::resourcesLoading() const
1837 {
1838     Q_D(const QQuickText);
1839     return d->doc ? d->doc->resourcesLoading() : 0;
1840 }
1841
1842 /*! \internal */
1843 void QQuickText::componentComplete()
1844 {
1845     Q_D(QQuickText);
1846     QQuickItem::componentComplete();
1847     if (d->updateOnComponentComplete) {
1848         d->updateOnComponentComplete = false;
1849         if (d->richText) {
1850             d->ensureDoc();
1851             d->doc->setText(d->text);
1852             d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
1853             d->richTextAsImage = enableImageCache();
1854         } else {
1855             d->rightToLeftText = d->text.isRightToLeft();
1856         }
1857         d->determineHorizontalAlignment();
1858         d->updateLayout();
1859     }
1860 }
1861
1862
1863 QString QQuickTextPrivate::anchorAt(const QPointF &mousePos)
1864 {
1865     if (styledText) {
1866         for (int i = 0; i < layout.lineCount(); ++i) {
1867             QTextLine line = layout.lineAt(i);
1868             if (line.naturalTextRect().contains(mousePos)) {
1869                 int charPos = line.xToCursor(mousePos.x());
1870                 foreach (const QTextLayout::FormatRange &formatRange, layout.additionalFormats()) {
1871                     if (formatRange.format.isAnchor()
1872                             && charPos >= formatRange.start
1873                             && charPos <= formatRange.start + formatRange.length) {
1874                         return formatRange.format.anchorHref();
1875                     }
1876                 }
1877                 break;
1878             }
1879         }
1880     }
1881     return QString();
1882 }
1883
1884 bool QQuickTextPrivate::isLinkActivatedConnected()
1885 {
1886     static int idx = this->signalIndex("linkActivated(QString)");
1887     return this->isSignalConnected(idx);
1888 }
1889
1890 /*!  \internal */
1891 void QQuickText::mousePressEvent(QMouseEvent *event)
1892 {
1893     Q_D(QQuickText);
1894
1895     if (d->isLinkActivatedConnected()) {
1896         if (d->styledText)
1897             d->activeLink = d->anchorAt(event->localPos());
1898         else if (d->richText && d->doc)
1899             d->activeLink = d->doc->documentLayout()->anchorAt(event->localPos());
1900     }
1901
1902     if (d->activeLink.isEmpty())
1903         event->setAccepted(false);
1904
1905     // ### may malfunction if two of the same links are clicked & dragged onto each other)
1906
1907     if (!event->isAccepted())
1908         QQuickItem::mousePressEvent(event);
1909
1910 }
1911
1912 /*! \internal */
1913 void QQuickText::mouseReleaseEvent(QMouseEvent *event)
1914 {
1915     Q_D(QQuickText);
1916
1917     // ### confirm the link, and send a signal out
1918
1919     QString link;
1920     if (d->isLinkActivatedConnected()) {
1921         if (d->styledText)
1922             link = d->anchorAt(event->localPos());
1923         else if (d->richText && d->doc)
1924             link = d->doc->documentLayout()->anchorAt(event->localPos());
1925     }
1926
1927     if (!link.isEmpty() && d->activeLink == link)
1928         emit linkActivated(d->activeLink);
1929     else
1930         event->setAccepted(false);
1931
1932     if (!event->isAccepted())
1933         QQuickItem::mouseReleaseEvent(event);
1934 }
1935
1936 QT_END_NAMESPACE
1937
1938 #include "qquicktext.moc"