src/declarative/graphicsitems/qdeclarativetext.cpp
changeset 30 5dc02b23752f
child 33 3e2da88830cd
equal deleted inserted replaced
29:b72c6db6890b 30:5dc02b23752f
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 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 <QTextCursor>
       
    53 #include <QGraphicsSceneMouseEvent>
       
    54 #include <QPainter>
       
    55 #include <QAbstractTextDocumentLayout>
       
    56 #include <qmath.h>
       
    57 
       
    58 QT_BEGIN_NAMESPACE
       
    59 
       
    60 class QTextDocumentWithImageResources : public QTextDocument {
       
    61     Q_OBJECT
       
    62 
       
    63 public:
       
    64     QTextDocumentWithImageResources(QDeclarativeText *parent) :
       
    65         QTextDocument(parent),
       
    66         outstanding(0)
       
    67     {
       
    68     }
       
    69 
       
    70     int resourcesLoading() const { return outstanding; }
       
    71 
       
    72 protected:
       
    73     QVariant loadResource(int type, const QUrl &name)
       
    74     {
       
    75         QUrl url = qmlContext(parent())->resolvedUrl(name);
       
    76 
       
    77         if (type == QTextDocument::ImageResource) {
       
    78             QPixmap pm;
       
    79             QString errorString;
       
    80             QDeclarativePixmapReply::Status status = QDeclarativePixmapCache::get(url, &pm, &errorString, 0, false, 0, 0);
       
    81             if (status == QDeclarativePixmapReply::Ready)
       
    82                 return pm;
       
    83             if (status == QDeclarativePixmapReply::Error) {
       
    84                 if (!errors.contains(url)) {
       
    85                     errors.insert(url);
       
    86                     qmlInfo(parent()) << errorString;
       
    87                 }
       
    88             } else {
       
    89                 QDeclarativePixmapReply *reply = QDeclarativePixmapCache::request(qmlEngine(parent()), url);
       
    90                 connect(reply, SIGNAL(finished()), this, SLOT(requestFinished()));
       
    91                 outstanding++;
       
    92             }
       
    93         }
       
    94 
       
    95         return QTextDocument::loadResource(type,url); // The *resolved* URL
       
    96     }
       
    97 
       
    98 private slots:
       
    99     void requestFinished()
       
   100     {
       
   101         outstanding--;
       
   102         if (outstanding == 0)
       
   103             static_cast<QDeclarativeText*>(parent())->reloadWithResources();
       
   104     }
       
   105 
       
   106 private:
       
   107     int outstanding;
       
   108     static QSet<QUrl> errors;
       
   109 };
       
   110 
       
   111 QSet<QUrl> QTextDocumentWithImageResources::errors;
       
   112 
       
   113 /*!
       
   114     \qmlclass Text QDeclarativeText
       
   115   \since 4.7
       
   116     \brief The Text item allows you to add formatted text to a scene.
       
   117     \inherits Item
       
   118 
       
   119     A Text item can display both plain and rich text. For example:
       
   120 
       
   121     \qml
       
   122     Text { text: "Hello World!"; font.family: "Helvetica"; font.pointSize: 24; color: "red" }
       
   123     Text { text: "<b>Hello</b> <i>World!</i>" }
       
   124     \endqml
       
   125 
       
   126     \image declarative-text.png
       
   127 
       
   128     If height and width are not explicitly set, Text will attempt to determine how
       
   129     much room is needed and set it accordingly. Unless \c wrapMode is set, it will always
       
   130     prefer width to height (all text will be placed on a single line).
       
   131 
       
   132     The \c elide property can alternatively be used to fit a single line of
       
   133     plain text to a set width.
       
   134 
       
   135     Note that the \l{Supported HTML Subset} is limited. Also, if the text contains
       
   136     HTML img tags that load remote images, the text is reloaded.
       
   137 
       
   138     Text provides read-only text. For editable text, see \l TextEdit.
       
   139 */
       
   140 
       
   141 /*!
       
   142     \internal
       
   143     \class QDeclarativeText
       
   144     \qmlclass Text
       
   145 
       
   146     \brief The QDeclarativeText class provides a formatted text item that you can add to a QDeclarativeView.
       
   147 
       
   148     Text was designed for read-only text; it does not allow for any text editing.
       
   149     It can display both plain and rich text. For example:
       
   150 
       
   151     \qml
       
   152     Text { text: "Hello World!"; font.family: "Helvetica"; font.pointSize: 24; color: "red" }
       
   153     Text { text: "<b>Hello</b> <i>World!</i>" }
       
   154     \endqml
       
   155 
       
   156     \image text.png
       
   157 
       
   158     If height and width are not explicitly set, Text will attempt to determine how
       
   159     much room is needed and set it accordingly. Unless \c wrapMode is set, it will always
       
   160     prefer width to height (all text will be placed on a single line).
       
   161 
       
   162     The \c elide property can alternatively be used to fit a line of plain text to a set width.
       
   163 
       
   164     A QDeclarativeText object can be instantiated in Qml using the tag \c Text.
       
   165 */
       
   166 QDeclarativeText::QDeclarativeText(QDeclarativeItem *parent)
       
   167   : QDeclarativeItem(*(new QDeclarativeTextPrivate), parent)
       
   168 {
       
   169 }
       
   170 
       
   171 QDeclarativeText::~QDeclarativeText()
       
   172 {
       
   173 }
       
   174 
       
   175 
       
   176 QDeclarativeTextPrivate::~QDeclarativeTextPrivate()
       
   177 {
       
   178 }
       
   179 
       
   180 /*!
       
   181     \qmlproperty string Text::font.family
       
   182 
       
   183     Sets the family name of the font.
       
   184 
       
   185     The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
       
   186     If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
       
   187     If the family isn't available a family will be set using the font matching algorithm.
       
   188 */
       
   189 
       
   190 /*!
       
   191     \qmlproperty bool Text::font.bold
       
   192 
       
   193     Sets whether the font weight is bold.
       
   194 */
       
   195 
       
   196 /*!
       
   197     \qmlproperty enumeration Text::font.weight
       
   198 
       
   199     Sets the font's weight.
       
   200 
       
   201     The weight can be one of:
       
   202     \list
       
   203     \o Font.Light
       
   204     \o Font.Normal - the default
       
   205     \o Font.DemiBold
       
   206     \o Font.Bold
       
   207     \o Font.Black
       
   208     \endlist
       
   209 
       
   210     \qml
       
   211     Text { text: "Hello"; font.weight: Font.DemiBold }
       
   212     \endqml
       
   213 */
       
   214 
       
   215 /*!
       
   216     \qmlproperty bool Text::font.italic
       
   217 
       
   218     Sets whether the font has an italic style.
       
   219 */
       
   220 
       
   221 /*!
       
   222     \qmlproperty bool Text::font.underline
       
   223 
       
   224     Sets whether the text is underlined.
       
   225 */
       
   226 
       
   227 /*!
       
   228     \qmlproperty bool Text::font.outline
       
   229 
       
   230     Sets whether the font has an outline style.
       
   231 */
       
   232 
       
   233 /*!
       
   234     \qmlproperty bool Text::font.strikeout
       
   235 
       
   236     Sets whether the font has a strikeout style.
       
   237 */
       
   238 
       
   239 /*!
       
   240     \qmlproperty real Text::font.pointSize
       
   241 
       
   242     Sets the font size in points. The point size must be greater than zero.
       
   243 */
       
   244 
       
   245 /*!
       
   246     \qmlproperty int Text::font.pixelSize
       
   247 
       
   248     Sets the font size in pixels.
       
   249 
       
   250     Using this function makes the font device dependent.
       
   251     Use \c pointSize to set the size of the font in a device independent manner.
       
   252 */
       
   253 
       
   254 /*!
       
   255     \qmlproperty real Text::font.letterSpacing
       
   256 
       
   257     Sets the letter spacing for the font.
       
   258 
       
   259     Letter spacing changes the default spacing between individual letters in the font.
       
   260     A value of 100 will keep the spacing unchanged; a value of 200 will enlarge the spacing after a character by
       
   261     the width of the character itself.
       
   262 */
       
   263 
       
   264 /*!
       
   265     \qmlproperty real Text::font.wordSpacing
       
   266 
       
   267     Sets the word spacing for the font.
       
   268 
       
   269     Word spacing changes the default spacing between individual words.
       
   270     A positive value increases the word spacing by a corresponding amount of pixels,
       
   271     while a negative value decreases the inter-word spacing accordingly.
       
   272 */
       
   273 
       
   274 /*!
       
   275     \qmlproperty enumeration Text::font.capitalization
       
   276 
       
   277     Sets the capitalization for the text.
       
   278 
       
   279     \list
       
   280     \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
       
   281     \o Font.AllUppercase - This alters the text to be rendered in all uppercase type.
       
   282     \o Font.AllLowercase	 - This alters the text to be rendered in all lowercase type.
       
   283     \o Font.SmallCaps -	This alters the text to be rendered in small-caps type.
       
   284     \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
       
   285     \endlist
       
   286 
       
   287     \qml
       
   288     Text { text: "Hello"; font.capitalization: Font.AllLowercase }
       
   289     \endqml
       
   290 */
       
   291 
       
   292 QFont QDeclarativeText::font() const
       
   293 {
       
   294     Q_D(const QDeclarativeText);
       
   295     return d->font;
       
   296 }
       
   297 
       
   298 void QDeclarativeText::setFont(const QFont &font)
       
   299 {
       
   300     Q_D(QDeclarativeText);
       
   301     if (d->font == font)
       
   302         return;
       
   303 
       
   304     d->font = font;
       
   305 
       
   306     d->updateLayout();
       
   307     d->markImgDirty();
       
   308     emit fontChanged(d->font);
       
   309 }
       
   310 
       
   311 void QDeclarativeText::setText(const QString &n)
       
   312 {
       
   313     Q_D(QDeclarativeText);
       
   314     if (d->text == n)
       
   315         return;
       
   316 
       
   317     d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(n));
       
   318     if (d->richText) {
       
   319         if (isComponentComplete()) {
       
   320             d->ensureDoc();
       
   321             d->doc->setHtml(n);
       
   322         }
       
   323     }
       
   324 
       
   325     d->text = n;
       
   326     d->updateLayout();
       
   327     d->markImgDirty();
       
   328     emit textChanged(d->text);
       
   329 }
       
   330 
       
   331 /*!
       
   332     \qmlproperty string Text::text
       
   333 
       
   334     The text to display. Text supports both plain and rich text strings.
       
   335 
       
   336     The item will try to automatically determine whether the text should
       
   337     be treated as rich text. This determination is made using Qt::mightBeRichText().
       
   338 */
       
   339 QString QDeclarativeText::text() const
       
   340 {
       
   341     Q_D(const QDeclarativeText);
       
   342     return d->text;
       
   343 }
       
   344 
       
   345 void QDeclarativeText::setColor(const QColor &color)
       
   346 {
       
   347     Q_D(QDeclarativeText);
       
   348     if (d->color == color)
       
   349         return;
       
   350 
       
   351     d->color = color;
       
   352     d->markImgDirty();
       
   353     emit colorChanged(d->color);
       
   354 }
       
   355 
       
   356 /*!
       
   357     \qmlproperty color Text::color
       
   358 
       
   359     The text color.
       
   360 
       
   361     \qml
       
   362     //green text using hexadecimal notation
       
   363     Text { color: "#00FF00"; ... }
       
   364 
       
   365     //steelblue text using SVG color name
       
   366     Text { color: "steelblue"; ... }
       
   367     \endqml
       
   368 */
       
   369 
       
   370 QColor QDeclarativeText::color() const
       
   371 {
       
   372     Q_D(const QDeclarativeText);
       
   373     return d->color;
       
   374 }
       
   375 
       
   376 /*!
       
   377     \qmlproperty enumeration Text::style
       
   378 
       
   379     Set an additional text style.
       
   380 
       
   381     Supported text styles are:
       
   382     \list
       
   383     \o Text.Normal - the default
       
   384     \o Text.Outline
       
   385     \o Text.Raised
       
   386     \o Text.Sunken
       
   387     \endlist
       
   388 
       
   389     \qml
       
   390     Row {
       
   391         Text { font.pointSize: 24; text: "Normal" }
       
   392         Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" }
       
   393         Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" }
       
   394         Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" }
       
   395     }
       
   396     \endqml
       
   397 
       
   398     \image declarative-textstyle.png
       
   399 */
       
   400 QDeclarativeText::TextStyle QDeclarativeText::style() const
       
   401 {
       
   402     Q_D(const QDeclarativeText);
       
   403     return d->style;
       
   404 }
       
   405 
       
   406 void QDeclarativeText::setStyle(QDeclarativeText::TextStyle style)
       
   407 {
       
   408     Q_D(QDeclarativeText);
       
   409     if (d->style == style)
       
   410         return;
       
   411 
       
   412     d->style = style;
       
   413     d->markImgDirty();
       
   414     emit styleChanged(d->style);
       
   415 }
       
   416 
       
   417 void QDeclarativeText::setStyleColor(const QColor &color)
       
   418 {
       
   419     Q_D(QDeclarativeText);
       
   420     if (d->styleColor == color)
       
   421         return;
       
   422 
       
   423     d->styleColor = color;
       
   424     d->markImgDirty();
       
   425     emit styleColorChanged(d->styleColor);
       
   426 }
       
   427 
       
   428 /*!
       
   429     \qmlproperty color Text::styleColor
       
   430 
       
   431     Defines the secondary color used by text styles.
       
   432 
       
   433     \c styleColor is used as the outline color for outlined text, and as the
       
   434     shadow color for raised or sunken text. If no style has been set, it is not
       
   435     used at all.
       
   436 
       
   437     \qml
       
   438     Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" }
       
   439     \endqml
       
   440  */
       
   441 QColor QDeclarativeText::styleColor() const
       
   442 {
       
   443     Q_D(const QDeclarativeText);
       
   444     return d->styleColor;
       
   445 }
       
   446 
       
   447 /*!
       
   448     \qmlproperty enumeration Text::horizontalAlignment
       
   449     \qmlproperty enumeration Text::verticalAlignment
       
   450 
       
   451     Sets the horizontal and vertical alignment of the text within the Text items
       
   452     width and height.  By default, the text is top-left aligned.
       
   453 
       
   454     The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight and
       
   455     \c Text.AlignHCenter.  The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom
       
   456     and \c Text.AlignVCenter.
       
   457 
       
   458     Note that for a single line of text, the size of the text is the area of the text. In this common case,
       
   459     all alignments are equivalent. If you want the text to be, say, centered in it parent, then you will
       
   460     need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to 
       
   461     that of the parent.
       
   462 */
       
   463 QDeclarativeText::HAlignment QDeclarativeText::hAlign() const
       
   464 {
       
   465     Q_D(const QDeclarativeText);
       
   466     return d->hAlign;
       
   467 }
       
   468 
       
   469 void QDeclarativeText::setHAlign(HAlignment align)
       
   470 {
       
   471     Q_D(QDeclarativeText);
       
   472     if (d->hAlign == align)
       
   473         return;
       
   474 
       
   475     d->hAlign = align;
       
   476     emit horizontalAlignmentChanged(align);
       
   477 }
       
   478 
       
   479 QDeclarativeText::VAlignment QDeclarativeText::vAlign() const
       
   480 {
       
   481     Q_D(const QDeclarativeText);
       
   482     return d->vAlign;
       
   483 }
       
   484 
       
   485 void QDeclarativeText::setVAlign(VAlignment align)
       
   486 {
       
   487     Q_D(QDeclarativeText);
       
   488     if (d->vAlign == align)
       
   489         return;
       
   490 
       
   491     d->vAlign = align;
       
   492     emit verticalAlignmentChanged(align);
       
   493 }
       
   494 
       
   495 /*!
       
   496     \qmlproperty enumeration Text::wrapMode
       
   497 
       
   498     Set this property to wrap the text to the Text item's width.  The text will only
       
   499     wrap if an explicit width has been set.  wrapMode can be one of:
       
   500 
       
   501     \list
       
   502     \o Text.NoWrap - no wrapping will be performed.
       
   503     \o Text.WordWrap - wrapping is done on word boundaries. If the text cannot be
       
   504     word-wrapped to the specified width it will be partially drawn outside of the item's bounds.
       
   505     If this is undesirable then enable clipping on the item (Item::clip).
       
   506     \o Text.WrapAnywhere - Text can be wrapped at any point on a line, even if it occurs in the middle of a word.
       
   507     \o Text.WrapAtWordBoundaryOrAnywhere - If possible, wrapping occurs at a word boundary; otherwise it
       
   508        will occur at the appropriate point on the line, even in the middle of a word.
       
   509     \endlist
       
   510 
       
   511     The default is Text.NoWrap.
       
   512 */
       
   513 QDeclarativeText::WrapMode QDeclarativeText::wrapMode() const
       
   514 {
       
   515     Q_D(const QDeclarativeText);
       
   516     return d->wrapMode;
       
   517 }
       
   518 
       
   519 void QDeclarativeText::setWrapMode(WrapMode mode)
       
   520 {
       
   521     Q_D(QDeclarativeText);
       
   522     if (mode == d->wrapMode)
       
   523         return;
       
   524 
       
   525     d->wrapMode = mode;
       
   526 
       
   527     d->updateLayout();
       
   528     d->markImgDirty();
       
   529     emit wrapModeChanged();
       
   530 }
       
   531 
       
   532 
       
   533 /*!
       
   534     \qmlproperty enumeration Text::textFormat
       
   535 
       
   536     The way the text property should be displayed.
       
   537 
       
   538     Supported text formats are:
       
   539     
       
   540     \list
       
   541     \o Text.AutoText
       
   542     \o Text.PlainText
       
   543     \o Text.RichText
       
   544     \o Text.StyledText
       
   545     \endlist
       
   546 
       
   547     The default is Text.AutoText.  If the text format is Text.AutoText the text element
       
   548     will automatically determine whether the text should be treated as
       
   549     rich text.  This determination is made using Qt::mightBeRichText().
       
   550 
       
   551     Text.StyledText is an optimized format supporting some basic text
       
   552     styling markup, in the style of html 3.2:
       
   553 
       
   554     \code
       
   555     <font size="4" color="#ff0000">font size and color</font>
       
   556     <b>bold</b>
       
   557     <i>italic</i>
       
   558     <br>
       
   559     &gt; &lt; &amp;
       
   560     \endcode
       
   561 
       
   562     \c Text.StyledText parser is strict, requiring tags to be correctly nested.
       
   563 
       
   564     \table
       
   565     \row
       
   566     \o
       
   567     \qml
       
   568 Column {
       
   569     Text {
       
   570         font.pointSize: 24
       
   571         text: "<b>Hello</b> <i>World!</i>"
       
   572     }
       
   573     Text {
       
   574         font.pointSize: 24
       
   575         textFormat: Text.RichText
       
   576         text: "<b>Hello</b> <i>World!</i>"
       
   577     }
       
   578     Text {
       
   579         font.pointSize: 24
       
   580         textFormat: Text.PlainText
       
   581         text: "<b>Hello</b> <i>World!</i>"
       
   582     }
       
   583 }
       
   584     \endqml
       
   585     \o \image declarative-textformat.png
       
   586     \endtable
       
   587 */
       
   588 
       
   589 QDeclarativeText::TextFormat QDeclarativeText::textFormat() const
       
   590 {
       
   591     Q_D(const QDeclarativeText);
       
   592     return d->format;
       
   593 }
       
   594 
       
   595 void QDeclarativeText::setTextFormat(TextFormat format)
       
   596 {
       
   597     Q_D(QDeclarativeText);
       
   598     if (format == d->format)
       
   599         return;
       
   600     d->format = format;
       
   601     bool wasRich = d->richText;
       
   602     d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text));
       
   603 
       
   604     if (wasRich && !d->richText) {
       
   605         //### delete control? (and vice-versa below)
       
   606         d->updateLayout();
       
   607         d->markImgDirty();
       
   608     } else if (!wasRich && d->richText) {
       
   609         if (isComponentComplete()) {
       
   610             d->ensureDoc();
       
   611             d->doc->setHtml(d->text);
       
   612         }
       
   613         d->updateLayout();
       
   614         d->markImgDirty();
       
   615     }
       
   616 
       
   617     emit textFormatChanged(d->format);
       
   618 }
       
   619 
       
   620 /*!
       
   621     \qmlproperty enumeration Text::elide
       
   622 
       
   623     Set this property to elide parts of the text fit to the Text item's width.
       
   624     The text will only elide if an explicit width has been set.
       
   625 
       
   626     This property cannot be used with wrapping enabled or with rich text.
       
   627 
       
   628     Eliding can be:
       
   629     \list
       
   630     \o Text.ElideNone  - the default
       
   631     \o Text.ElideLeft
       
   632     \o Text.ElideMiddle
       
   633     \o Text.ElideRight
       
   634     \endlist
       
   635 
       
   636     If the text is a multi-length string, and the mode is not \c Text.ElideNone,
       
   637     the first string that fits will be used, otherwise the last will be elided.
       
   638 
       
   639     Multi-length strings are ordered from longest to shortest, separated by the
       
   640     Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}).
       
   641 */
       
   642 QDeclarativeText::TextElideMode QDeclarativeText::elideMode() const
       
   643 {
       
   644     Q_D(const QDeclarativeText);
       
   645     return d->elideMode;
       
   646 }
       
   647 
       
   648 void QDeclarativeText::setElideMode(QDeclarativeText::TextElideMode mode)
       
   649 {
       
   650     Q_D(QDeclarativeText);
       
   651     if (mode == d->elideMode)
       
   652         return;
       
   653 
       
   654     d->elideMode = mode;
       
   655 
       
   656     d->updateLayout();
       
   657     d->markImgDirty();
       
   658     emit elideModeChanged(d->elideMode);
       
   659 }
       
   660 
       
   661 void QDeclarativeText::geometryChanged(const QRectF &newGeometry,
       
   662                               const QRectF &oldGeometry)
       
   663 {
       
   664     Q_D(QDeclarativeText);
       
   665     if (newGeometry.width() != oldGeometry.width()) {
       
   666         if (d->wrapMode != QDeclarativeText::NoWrap || d->elideMode != QDeclarativeText::ElideNone) {
       
   667             //re-elide if needed
       
   668             if (d->singleline && d->elideMode != QDeclarativeText::ElideNone &&
       
   669                 isComponentComplete() && widthValid()) {
       
   670 
       
   671                 QFontMetrics fm(d->font);
       
   672                 QString tmp = fm.elidedText(d->text,(Qt::TextElideMode)d->elideMode,width()); // XXX still worth layout...?
       
   673                 d->layout.setText(tmp);
       
   674             }
       
   675 
       
   676             d->imgDirty = true;
       
   677             d->updateSize();
       
   678         }
       
   679     }
       
   680     QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
       
   681 }
       
   682 
       
   683 void QDeclarativeTextPrivate::updateLayout()
       
   684 {
       
   685     Q_Q(QDeclarativeText);
       
   686     if (q->isComponentComplete()) {
       
   687         //setup instance of QTextLayout for all cases other than richtext
       
   688         if (!richText) {
       
   689             layout.clearLayout();
       
   690             layout.setFont(font);
       
   691             if (format != QDeclarativeText::StyledText) {
       
   692                 QString tmp = text;
       
   693                 tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
       
   694                 singleline = !tmp.contains(QChar::LineSeparator);
       
   695                 if (singleline && elideMode != QDeclarativeText::ElideNone && q->widthValid()) {
       
   696                     QFontMetrics fm(font);
       
   697                     tmp = fm.elidedText(tmp,(Qt::TextElideMode)elideMode,q->width()); // XXX still worth layout...?
       
   698                 }
       
   699                 layout.setText(tmp);
       
   700             } else {
       
   701                 singleline = false;
       
   702                 QDeclarativeStyledText::parse(text, layout);
       
   703             }
       
   704         }
       
   705         updateSize();
       
   706     } else {
       
   707         dirty = true;
       
   708     }
       
   709 }
       
   710 
       
   711 void QDeclarativeTextPrivate::updateSize()
       
   712 {
       
   713     Q_Q(QDeclarativeText);
       
   714     if (q->isComponentComplete()) {
       
   715         QFontMetrics fm(font);
       
   716         if (text.isEmpty()) {
       
   717             q->setImplicitHeight(fm.height());
       
   718             return;
       
   719         }
       
   720 
       
   721         int dy = q->height();
       
   722         QSize size(0, 0);
       
   723 
       
   724         //setup instance of QTextLayout for all cases other than richtext
       
   725         if (!richText) {
       
   726             size = setupTextLayout(&layout);
       
   727             cachedLayoutSize = size;
       
   728             dy -= size.height();
       
   729         } else {
       
   730             singleline = false; // richtext can't elide or be optimized for single-line case
       
   731             ensureDoc();
       
   732             doc->setDefaultFont(font);
       
   733             QTextOption option((Qt::Alignment)int(hAlign | vAlign));
       
   734             option.setWrapMode(QTextOption::WrapMode(wrapMode));
       
   735             doc->setDefaultTextOption(option);
       
   736             if (wrapMode != QDeclarativeText::NoWrap && q->widthValid())
       
   737                 doc->setTextWidth(q->width());
       
   738             else
       
   739                 doc->setTextWidth(doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug)
       
   740             dy -= (int)doc->size().height();
       
   741             cachedLayoutSize = doc->size().toSize();
       
   742         }
       
   743         int yoff = 0;
       
   744 
       
   745         if (q->heightValid()) {
       
   746             if (vAlign == QDeclarativeText::AlignBottom)
       
   747                 yoff = dy;
       
   748             else if (vAlign == QDeclarativeText::AlignVCenter)
       
   749                 yoff = dy/2;
       
   750         }
       
   751         q->setBaselineOffset(fm.ascent() + yoff);
       
   752 
       
   753         //### need to comfirm cost of always setting these for richText
       
   754         q->setImplicitWidth(richText ? (int)doc->idealWidth() : size.width());
       
   755         q->setImplicitHeight(richText ? (int)doc->size().height() : size.height());
       
   756     } else {
       
   757         dirty = true;
       
   758     }
       
   759 }
       
   760 
       
   761 // ### text layout handling should be profiled and optimized as needed
       
   762 // what about QStackTextEngine engine(tmp, d->font.font()); QTextLayout textLayout(&engine);
       
   763 
       
   764 void QDeclarativeTextPrivate::drawOutline()
       
   765 {
       
   766     QPixmap img = QPixmap(imgStyleCache.width()+2,imgStyleCache.height()+2);
       
   767     img.fill(Qt::transparent);
       
   768 
       
   769     QPainter ppm(&img);
       
   770 
       
   771     QPoint pos(imgCache.rect().topLeft());
       
   772     pos += QPoint(-1, 0);
       
   773     ppm.drawPixmap(pos, imgStyleCache);
       
   774     pos += QPoint(2, 0);
       
   775     ppm.drawPixmap(pos, imgStyleCache);
       
   776     pos += QPoint(-1, -1);
       
   777     ppm.drawPixmap(pos, imgStyleCache);
       
   778     pos += QPoint(0, 2);
       
   779     ppm.drawPixmap(pos, imgStyleCache);
       
   780 
       
   781     pos += QPoint(0, -1);
       
   782     ppm.drawPixmap(pos, imgCache);
       
   783     ppm.end();
       
   784 
       
   785     imgCache = img;
       
   786 }
       
   787 
       
   788 void QDeclarativeTextPrivate::drawOutline(int yOffset)
       
   789 {
       
   790     QPixmap img = QPixmap(imgStyleCache.width()+2,imgStyleCache.height()+2);
       
   791     img.fill(Qt::transparent);
       
   792 
       
   793     QPainter ppm(&img);
       
   794 
       
   795     QPoint pos(imgCache.rect().topLeft());
       
   796     pos += QPoint(0, yOffset);
       
   797     ppm.drawPixmap(pos, imgStyleCache);
       
   798 
       
   799     pos += QPoint(0, -yOffset);
       
   800     ppm.drawPixmap(pos, imgCache);
       
   801     ppm.end();
       
   802 
       
   803     imgCache = img;
       
   804 }
       
   805 
       
   806 QSize QDeclarativeTextPrivate::setupTextLayout(QTextLayout *layout)
       
   807 {
       
   808     Q_Q(QDeclarativeText);
       
   809     layout->setCacheEnabled(true);
       
   810 
       
   811     int height = 0;
       
   812     qreal widthUsed = 0;
       
   813     qreal lineWidth = 0;
       
   814 
       
   815     //set manual width
       
   816     if ((wrapMode != QDeclarativeText::NoWrap || elideMode != QDeclarativeText::ElideNone) && q->widthValid())
       
   817         lineWidth = q->width();
       
   818 
       
   819     QTextOption textOption = layout->textOption();
       
   820     textOption.setWrapMode(QTextOption::WrapMode(wrapMode));
       
   821     layout->setTextOption(textOption);
       
   822 
       
   823     layout->beginLayout();
       
   824 
       
   825     while (1) {
       
   826         QTextLine line = layout->createLine();
       
   827         if (!line.isValid())
       
   828             break;
       
   829 
       
   830         if ((wrapMode != QDeclarativeText::NoWrap || elideMode != QDeclarativeText::ElideNone) && q->widthValid())
       
   831             line.setLineWidth(lineWidth);
       
   832     }
       
   833     layout->endLayout();
       
   834 
       
   835     int x = 0;
       
   836     for (int i = 0; i < layout->lineCount(); ++i) {
       
   837         QTextLine line = layout->lineAt(i);
       
   838         widthUsed = qMax(widthUsed, line.naturalTextWidth());
       
   839         line.setPosition(QPointF(0, height));
       
   840         height += int(line.height());
       
   841 
       
   842         if (!cache) {
       
   843             if (hAlign == QDeclarativeText::AlignLeft) {
       
   844                 x = 0;
       
   845             } else if (hAlign == QDeclarativeText::AlignRight) {
       
   846                 x = q->width() - (int)line.naturalTextWidth();
       
   847             } else if (hAlign == QDeclarativeText::AlignHCenter) {
       
   848                 x = (q->width() - (int)line.naturalTextWidth()) / 2;
       
   849             }
       
   850             line.setPosition(QPoint(x, (int)line.y()));
       
   851         }
       
   852     }
       
   853 
       
   854     return QSize(qCeil(widthUsed), height);
       
   855 }
       
   856 
       
   857 QPixmap QDeclarativeTextPrivate::wrappedTextImage(bool drawStyle)
       
   858 {
       
   859     //do layout
       
   860     QSize size = cachedLayoutSize;
       
   861 
       
   862     int x = 0;
       
   863     for (int i = 0; i < layout.lineCount(); ++i) {
       
   864         QTextLine line = layout.lineAt(i);
       
   865         if (hAlign == QDeclarativeText::AlignLeft) {
       
   866             x = 0;
       
   867         } else if (hAlign == QDeclarativeText::AlignRight) {
       
   868             x = size.width() - (int)line.naturalTextWidth();
       
   869         } else if (hAlign == QDeclarativeText::AlignHCenter) {
       
   870             x = (size.width() - (int)line.naturalTextWidth()) / 2;
       
   871         }
       
   872         line.setPosition(QPoint(x, (int)line.y()));
       
   873     }
       
   874 
       
   875     //paint text
       
   876     QPixmap img(size);
       
   877     if (!size.isEmpty()) {
       
   878         img.fill(Qt::transparent);
       
   879         QPainter p(&img);
       
   880         drawWrappedText(&p, QPointF(0,0), drawStyle);
       
   881     }
       
   882     return img;
       
   883 }
       
   884 
       
   885 void QDeclarativeTextPrivate::drawWrappedText(QPainter *p, const QPointF &pos, bool drawStyle)
       
   886 {
       
   887     if (drawStyle)
       
   888         p->setPen(styleColor);
       
   889     else
       
   890         p->setPen(color);
       
   891     p->setFont(font);
       
   892     layout.draw(p, pos);
       
   893 }
       
   894 
       
   895 QPixmap QDeclarativeTextPrivate::richTextImage(bool drawStyle)
       
   896 {
       
   897     QSize size = doc->size().toSize();
       
   898 
       
   899     //paint text
       
   900     QPixmap img(size);
       
   901     img.fill(Qt::transparent);
       
   902     QPainter p(&img);
       
   903 
       
   904     QAbstractTextDocumentLayout::PaintContext context;
       
   905 
       
   906     if (drawStyle) {
       
   907         context.palette.setColor(QPalette::Text, styleColor);
       
   908         // ### Do we really want this?
       
   909         QTextOption colorOption;
       
   910         colorOption.setFlags(QTextOption::SuppressColors);
       
   911         doc->setDefaultTextOption(colorOption);
       
   912     } else {
       
   913         context.palette.setColor(QPalette::Text, color);
       
   914     }
       
   915     doc->documentLayout()->draw(&p, context);
       
   916     if (drawStyle)
       
   917         doc->setDefaultTextOption(QTextOption());
       
   918     return img;
       
   919 }
       
   920 
       
   921 void QDeclarativeTextPrivate::checkImgCache()
       
   922 {
       
   923     if (!imgDirty)
       
   924         return;
       
   925 
       
   926     bool empty = text.isEmpty();
       
   927     if (empty) {
       
   928         imgCache = QPixmap();
       
   929         imgStyleCache = QPixmap();
       
   930     } else if (richText) {
       
   931         imgCache = richTextImage(false);
       
   932         if (style != QDeclarativeText::Normal)
       
   933             imgStyleCache = richTextImage(true); //### should use styleColor
       
   934     } else {
       
   935         imgCache = wrappedTextImage(false);
       
   936         if (style != QDeclarativeText::Normal)
       
   937             imgStyleCache = wrappedTextImage(true); //### should use styleColor
       
   938     }
       
   939     if (!empty)
       
   940         switch (style) {
       
   941         case QDeclarativeText::Outline:
       
   942             drawOutline();
       
   943             break;
       
   944         case QDeclarativeText::Sunken:
       
   945             drawOutline(-1);
       
   946             break;
       
   947         case QDeclarativeText::Raised:
       
   948             drawOutline(1);
       
   949             break;
       
   950         default:
       
   951             break;
       
   952         }
       
   953 
       
   954     imgDirty = false;
       
   955 }
       
   956 
       
   957 void QDeclarativeTextPrivate::ensureDoc()
       
   958 {
       
   959     if (!doc) {
       
   960         Q_Q(QDeclarativeText);
       
   961         doc = new QTextDocumentWithImageResources(q);
       
   962         doc->setDocumentMargin(0);
       
   963     }
       
   964 }
       
   965 
       
   966 void QDeclarativeText::reloadWithResources()
       
   967 {
       
   968     Q_D(QDeclarativeText);
       
   969     if (!d->richText)
       
   970         return;
       
   971     d->doc->setHtml(d->text);
       
   972     d->updateLayout();
       
   973     d->markImgDirty();
       
   974 }
       
   975 
       
   976 /*!
       
   977     Returns the number of resources (images) that are being loaded asynchronously.
       
   978 */
       
   979 int QDeclarativeText::resourcesLoading() const
       
   980 {
       
   981     Q_D(const QDeclarativeText);
       
   982     return d->doc ? d->doc->resourcesLoading() : 0;
       
   983 }
       
   984 
       
   985 void QDeclarativeText::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
       
   986 {
       
   987     Q_D(QDeclarativeText);
       
   988 
       
   989     if (d->cache || d->style != Normal) {
       
   990         d->checkImgCache();
       
   991         if (d->imgCache.isNull())
       
   992             return;
       
   993 
       
   994         bool oldAA = p->testRenderHint(QPainter::Antialiasing);
       
   995         bool oldSmooth = p->testRenderHint(QPainter::SmoothPixmapTransform);
       
   996         if (d->smooth)
       
   997             p->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth);
       
   998 
       
   999         int w = width();
       
  1000         int h = height();
       
  1001 
       
  1002         int x = 0;
       
  1003         int y = 0;
       
  1004 
       
  1005         switch (d->hAlign) {
       
  1006         case AlignLeft:
       
  1007             x = 0;
       
  1008             break;
       
  1009         case AlignRight:
       
  1010             x = w - d->imgCache.width();
       
  1011             break;
       
  1012         case AlignHCenter:
       
  1013             x = (w - d->imgCache.width()) / 2;
       
  1014             break;
       
  1015         }
       
  1016 
       
  1017         switch (d->vAlign) {
       
  1018         case AlignTop:
       
  1019             y = 0;
       
  1020             break;
       
  1021         case AlignBottom:
       
  1022             y = h - d->imgCache.height();
       
  1023             break;
       
  1024         case AlignVCenter:
       
  1025             y = (h - d->imgCache.height()) / 2;
       
  1026             break;
       
  1027         }
       
  1028 
       
  1029         bool needClip = clip() && (d->imgCache.width() > width() ||
       
  1030                                    d->imgCache.height() > height());
       
  1031 
       
  1032         if (needClip) {
       
  1033             p->save();
       
  1034             p->setClipRect(boundingRect(), Qt::IntersectClip);
       
  1035         }
       
  1036         p->drawPixmap(x, y, d->imgCache);
       
  1037         if (needClip)
       
  1038             p->restore();
       
  1039 
       
  1040         if (d->smooth) {
       
  1041             p->setRenderHint(QPainter::Antialiasing, oldAA);
       
  1042             p->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth);
       
  1043         }
       
  1044     } else {
       
  1045         int h = height();
       
  1046         int y = 0;
       
  1047 
       
  1048         switch (d->vAlign) {
       
  1049         case AlignTop:
       
  1050             y = 0;
       
  1051             break;
       
  1052         case AlignBottom:
       
  1053             y = h - d->cachedLayoutSize.height();
       
  1054             break;
       
  1055         case AlignVCenter:
       
  1056             y = (h - d->cachedLayoutSize.height()) / 2;
       
  1057             break;
       
  1058         }
       
  1059         bool needClip = !clip() && (d->cachedLayoutSize.width() > width() ||
       
  1060                                     d->cachedLayoutSize.height() > height());
       
  1061 
       
  1062         if (needClip) {
       
  1063             p->save();
       
  1064             p->setClipRect(boundingRect(), Qt::IntersectClip);
       
  1065         }
       
  1066         if (d->richText) {
       
  1067             QAbstractTextDocumentLayout::PaintContext context;
       
  1068             context.palette.setColor(QPalette::Text, d->color);
       
  1069             p->translate(0, y);
       
  1070             d->doc->documentLayout()->draw(p, context);
       
  1071             p->translate(0, -y);
       
  1072         } else {
       
  1073             d->drawWrappedText(p, QPointF(0,y), false);
       
  1074         }
       
  1075         if (needClip)
       
  1076             p->restore();
       
  1077     }
       
  1078 }
       
  1079 
       
  1080 /*!
       
  1081     \qmlproperty bool Text::smooth
       
  1082 
       
  1083     This property holds whether the text is smoothly scaled or transformed.
       
  1084 
       
  1085     Smooth filtering gives better visual quality, but is slower.  If
       
  1086     the item is displayed at its natural size, this property has no visual or
       
  1087     performance effect.
       
  1088 
       
  1089     \note Generally scaling artifacts are only visible if the item is stationary on
       
  1090     the screen.  A common pattern when animating an item is to disable smooth
       
  1091     filtering at the beginning of the animation and reenable it at the conclusion.
       
  1092 */
       
  1093 
       
  1094 void QDeclarativeText::componentComplete()
       
  1095 {
       
  1096     Q_D(QDeclarativeText);
       
  1097     QDeclarativeItem::componentComplete();
       
  1098     if (d->dirty) {
       
  1099         if (d->richText) {
       
  1100             d->ensureDoc();
       
  1101             d->doc->setHtml(d->text);
       
  1102         }
       
  1103         d->updateLayout();
       
  1104         d->dirty = false;
       
  1105     }
       
  1106 }
       
  1107 
       
  1108 /*!
       
  1109   \overload
       
  1110   Handles the given mouse \a event.
       
  1111  */
       
  1112 void QDeclarativeText::mousePressEvent(QGraphicsSceneMouseEvent *event)
       
  1113 {
       
  1114     Q_D(QDeclarativeText);
       
  1115 
       
  1116     if (!d->richText || !d->doc || d->doc->documentLayout()->anchorAt(event->pos()).isEmpty()) {
       
  1117         event->setAccepted(false);
       
  1118         d->activeLink = QString();
       
  1119     } else {
       
  1120         d->activeLink = d->doc->documentLayout()->anchorAt(event->pos());
       
  1121     }
       
  1122 
       
  1123     // ### may malfunction if two of the same links are clicked & dragged onto each other)
       
  1124 
       
  1125     if (!event->isAccepted())
       
  1126         QDeclarativeItem::mousePressEvent(event);
       
  1127 
       
  1128 }
       
  1129 
       
  1130 /*!
       
  1131     \qmlsignal Text::linkActivated(link)
       
  1132 
       
  1133     This handler is called when the user clicks on a link embedded in the text.
       
  1134 */
       
  1135 
       
  1136 /*!
       
  1137   \overload
       
  1138   Handles the given mouse \a event.
       
  1139  */
       
  1140 void QDeclarativeText::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
       
  1141 {
       
  1142     Q_D(QDeclarativeText);
       
  1143 
       
  1144         // ### confirm the link, and send a signal out
       
  1145     if (d->richText && d->doc && d->activeLink == d->doc->documentLayout()->anchorAt(event->pos()))
       
  1146         emit linkActivated(d->activeLink);
       
  1147     else
       
  1148         event->setAccepted(false);
       
  1149 
       
  1150     if (!event->isAccepted())
       
  1151         QDeclarativeItem::mouseReleaseEvent(event);
       
  1152 }
       
  1153 
       
  1154 QT_END_NAMESPACE
       
  1155 
       
  1156 #include "qdeclarativetext.moc"