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