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