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