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