~saviq/ubuntu/saucy/qtdeclarative-opensource-src/add-qtquick-delegate-range

« back to all changes in this revision

Viewing changes to src/quick/items/qquicktext.cpp

  • Committer: Package Import Robot
  • Author(s): Timo Jyrinki
  • Date: 2013-02-05 14:17:19 UTC
  • Revision ID: package-import@ubuntu.com-20130205141719-qqeyml8wslpyez52
Tags: upstream-5.0.1
ImportĀ upstreamĀ versionĀ 5.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
 
4
** Contact: http://www.qt-project.org/legal
 
5
**
 
6
** This file is part of the QtQml module of the Qt Toolkit.
 
7
**
 
8
** $QT_BEGIN_LICENSE:LGPL$
 
9
** Commercial License Usage
 
10
** Licensees holding valid commercial Qt licenses may use this file in
 
11
** accordance with the commercial license agreement provided with the
 
12
** Software or, alternatively, in accordance with the terms contained in
 
13
** a written agreement between you and Digia.  For licensing terms and
 
14
** conditions see http://qt.digia.com/licensing.  For further information
 
15
** use the contact form at http://qt.digia.com/contact-us.
 
16
**
 
17
** GNU Lesser General Public License Usage
 
18
** Alternatively, this file may be used under the terms of the GNU Lesser
 
19
** General Public License version 2.1 as published by the Free Software
 
20
** Foundation and appearing in the file LICENSE.LGPL included in the
 
21
** packaging of this file.  Please review the following information to
 
22
** ensure the GNU Lesser General Public License version 2.1 requirements
 
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 
24
**
 
25
** In addition, as a special exception, Digia gives you certain additional
 
26
** rights.  These rights are described in the Digia Qt LGPL Exception
 
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 
28
**
 
29
** GNU General Public License Usage
 
30
** Alternatively, this file may be used under the terms of the GNU
 
31
** General Public License version 3.0 as published by the Free Software
 
32
** Foundation and appearing in the file LICENSE.GPL included in the
 
33
** packaging of this file.  Please review the following information to
 
34
** ensure the GNU General Public License version 3.0 requirements will be
 
35
** met: http://www.gnu.org/copyleft/gpl.html.
 
36
**
 
37
**
 
38
** $QT_END_LICENSE$
 
39
**
 
40
****************************************************************************/
 
41
 
 
42
#include "qquicktext_p.h"
 
43
#include "qquicktext_p_p.h"
 
44
 
 
45
#include <QtQuick/private/qsgcontext_p.h>
 
46
#include <private/qqmlglobal_p.h>
 
47
#include <private/qsgadaptationlayer_p.h>
 
48
#include "qquicktextnode_p.h"
 
49
#include "qquickimage_p_p.h"
 
50
#include "qquicktextutil_p.h"
 
51
 
 
52
#include <QtQuick/private/qsgtexture_p.h>
 
53
 
 
54
#include <QtQml/qqmlinfo.h>
 
55
#include <QtGui/qevent.h>
 
56
#include <QtGui/qabstracttextdocumentlayout.h>
 
57
#include <QtGui/qpainter.h>
 
58
#include <QtGui/qtextdocument.h>
 
59
#include <QtGui/qtextobject.h>
 
60
#include <QtGui/qtextcursor.h>
 
61
#include <QtGui/qguiapplication.h>
 
62
#include <QtGui/qinputmethod.h>
 
63
 
 
64
#include <private/qtextengine_p.h>
 
65
#include <private/qquickstyledtext_p.h>
 
66
#include <QtQuick/private/qquickpixmapcache_p.h>
 
67
 
 
68
#include <qmath.h>
 
69
#include <limits.h>
 
70
 
 
71
QT_BEGIN_NAMESPACE
 
72
 
 
73
 
 
74
const QChar QQuickTextPrivate::elideChar = QChar(0x2026);
 
75
 
 
76
QQuickTextPrivate::QQuickTextPrivate()
 
77
    : elideLayout(0), textLine(0), lineWidth(0)
 
78
    , color(0xFF000000), linkColor(0xFF0000FF), styleColor(0xFF000000)
 
79
    , lineCount(1), multilengthEos(-1)
 
80
    , elideMode(QQuickText::ElideNone), hAlign(QQuickText::AlignLeft), vAlign(QQuickText::AlignTop)
 
81
    , format(QQuickText::AutoText), wrapMode(QQuickText::NoWrap)
 
82
    , style(QQuickText::Normal)
 
83
    , renderType(QQuickText::QtRendering)
 
84
    , updateType(UpdatePaintNode)
 
85
    , maximumLineCountValid(false), updateOnComponentComplete(true), richText(false)
 
86
    , styledText(false), widthExceeded(false), heightExceeded(false), internalWidthUpdate(false)
 
87
    , requireImplicitSize(false), implicitWidthValid(false), implicitHeightValid(false)
 
88
    , truncated(false), hAlignImplicit(true), rightToLeftText(false)
 
89
    , layoutTextElided(false), textHasChanged(true), needToUpdateLayout(false), formatModifiesFontSize(false)
 
90
{
 
91
}
 
92
 
 
93
QQuickTextPrivate::ExtraData::ExtraData()
 
94
    : lineHeight(1.0)
 
95
    , doc(0)
 
96
    , minimumPixelSize(12)
 
97
    , minimumPointSize(12)
 
98
    , nbActiveDownloads(0)
 
99
    , maximumLineCount(INT_MAX)
 
100
    , lineHeightMode(QQuickText::ProportionalHeight)
 
101
    , fontSizeMode(QQuickText::FixedSize)
 
102
{
 
103
}
 
104
 
 
105
void QQuickTextPrivate::init()
 
106
{
 
107
    Q_Q(QQuickText);
 
108
    q->setAcceptedMouseButtons(Qt::LeftButton);
 
109
    q->setFlag(QQuickItem::ItemHasContents);
 
110
}
 
111
 
 
112
QQuickTextDocumentWithImageResources::QQuickTextDocumentWithImageResources(QQuickItem *parent)
 
113
: QTextDocument(parent), outstanding(0)
 
114
{
 
115
    setUndoRedoEnabled(false);
 
116
    documentLayout()->registerHandler(QTextFormat::ImageObject, this);
 
117
}
 
118
 
 
119
QQuickTextDocumentWithImageResources::~QQuickTextDocumentWithImageResources()
 
120
{
 
121
    if (!m_resources.isEmpty())
 
122
        qDeleteAll(m_resources);
 
123
}
 
124
 
 
125
QVariant QQuickTextDocumentWithImageResources::loadResource(int type, const QUrl &name)
 
126
{
 
127
    QQmlContext *context = qmlContext(parent());
 
128
    QUrl url = m_baseUrl.resolved(name);
 
129
 
 
130
    if (type == QTextDocument::ImageResource) {
 
131
        QQuickPixmap *p = loadPixmap(context, url);
 
132
        return p->image();
 
133
    }
 
134
 
 
135
    return QTextDocument::loadResource(type,url); // The *resolved* URL
 
136
}
 
137
 
 
138
void QQuickTextDocumentWithImageResources::requestFinished()
 
139
{
 
140
    outstanding--;
 
141
    if (outstanding == 0) {
 
142
        markContentsDirty(0, characterCount());
 
143
        emit imagesLoaded();
 
144
    }
 
145
}
 
146
 
 
147
void QQuickTextDocumentWithImageResources::clear()
 
148
{
 
149
    clearResources();
 
150
 
 
151
    QTextDocument::clear();
 
152
}
 
153
 
 
154
 
 
155
QSizeF QQuickTextDocumentWithImageResources::intrinsicSize(
 
156
        QTextDocument *, int, const QTextFormat &format)
 
157
{
 
158
    if (format.isImageFormat()) {
 
159
        QTextImageFormat imageFormat = format.toImageFormat();
 
160
 
 
161
        const bool hasWidth = imageFormat.hasProperty(QTextFormat::ImageWidth);
 
162
        const int width = qRound(imageFormat.width());
 
163
        const bool hasHeight = imageFormat.hasProperty(QTextFormat::ImageHeight);
 
164
        const int height = qRound(imageFormat.height());
 
165
 
 
166
        QSizeF size(width, height);
 
167
        if (!hasWidth || !hasHeight) {
 
168
            QQmlContext *context = qmlContext(parent());
 
169
            QUrl url = m_baseUrl.resolved(QUrl(imageFormat.name()));
 
170
 
 
171
            QQuickPixmap *p = loadPixmap(context, url);
 
172
            if (!p->isReady()) {
 
173
                if (!hasWidth)
 
174
                    size.setWidth(16);
 
175
                if (!hasHeight)
 
176
                    size.setHeight(16);
 
177
                return size;
 
178
            }
 
179
            QSize implicitSize = p->implicitSize();
 
180
 
 
181
            if (!hasWidth) {
 
182
                if (!hasHeight)
 
183
                    size.setWidth(implicitSize.width());
 
184
                else
 
185
                    size.setWidth(qRound(height * (implicitSize.width() / (qreal) implicitSize.height())));
 
186
            }
 
187
            if (!hasHeight) {
 
188
                if (!hasWidth)
 
189
                    size.setHeight(implicitSize.height());
 
190
                else
 
191
                    size.setHeight(qRound(width * (implicitSize.height() / (qreal) implicitSize.width())));
 
192
            }
 
193
        }
 
194
        return size;
 
195
    }
 
196
    return QSizeF();
 
197
}
 
198
 
 
199
void QQuickTextDocumentWithImageResources::drawObject(
 
200
        QPainter *, const QRectF &, QTextDocument *, int, const QTextFormat &)
 
201
{
 
202
}
 
203
 
 
204
QImage QQuickTextDocumentWithImageResources::image(const QTextImageFormat &format)
 
205
{
 
206
    QQmlContext *context = qmlContext(parent());
 
207
    QUrl url = m_baseUrl.resolved(QUrl(format.name()));
 
208
 
 
209
    QQuickPixmap *p = loadPixmap(context, url);
 
210
    return p->image();
 
211
}
 
212
 
 
213
void QQuickTextDocumentWithImageResources::setBaseUrl(const QUrl &url, bool clear)
 
214
{
 
215
    m_baseUrl = url;
 
216
    if (clear) {
 
217
        clearResources();
 
218
        markContentsDirty(0, characterCount());
 
219
    }
 
220
}
 
221
 
 
222
QQuickPixmap *QQuickTextDocumentWithImageResources::loadPixmap(
 
223
        QQmlContext *context, const QUrl &url)
 
224
{
 
225
 
 
226
    QHash<QUrl, QQuickPixmap *>::Iterator iter = m_resources.find(url);
 
227
 
 
228
    if (iter == m_resources.end()) {
 
229
        QQuickPixmap *p = new QQuickPixmap(context->engine(), url);
 
230
        iter = m_resources.insert(url, p);
 
231
 
 
232
        if (p->isLoading()) {
 
233
            p->connectFinished(this, SLOT(requestFinished()));
 
234
            outstanding++;
 
235
        }
 
236
    }
 
237
 
 
238
    QQuickPixmap *p = *iter;
 
239
    if (p->isError()) {
 
240
        if (!errors.contains(url)) {
 
241
            errors.insert(url);
 
242
            qmlInfo(parent()) << p->error();
 
243
        }
 
244
    }
 
245
    return p;
 
246
}
 
247
 
 
248
void QQuickTextDocumentWithImageResources::clearResources()
 
249
{
 
250
    foreach (QQuickPixmap *pixmap, m_resources)
 
251
        pixmap->clear(this);
 
252
    qDeleteAll(m_resources);
 
253
    m_resources.clear();
 
254
    outstanding = 0;
 
255
}
 
256
 
 
257
void QQuickTextDocumentWithImageResources::setText(const QString &text)
 
258
{
 
259
    clearResources();
 
260
 
 
261
#ifndef QT_NO_TEXTHTMLPARSER
 
262
    setHtml(text);
 
263
#else
 
264
    setPlainText(text);
 
265
#endif
 
266
}
 
267
 
 
268
QSet<QUrl> QQuickTextDocumentWithImageResources::errors;
 
269
 
 
270
QQuickTextPrivate::~QQuickTextPrivate()
 
271
{
 
272
    delete elideLayout;
 
273
    delete textLine; textLine = 0;
 
274
    qDeleteAll(imgTags);
 
275
    imgTags.clear();
 
276
}
 
277
 
 
278
qreal QQuickTextPrivate::getImplicitWidth() const
 
279
{
 
280
    if (!requireImplicitSize) {
 
281
        // We don't calculate implicitWidth unless it is required.
 
282
        // We need to force a size update now to ensure implicitWidth is calculated
 
283
        QQuickTextPrivate *me = const_cast<QQuickTextPrivate*>(this);
 
284
        me->requireImplicitSize = true;
 
285
        me->updateSize();
 
286
    }
 
287
    return implicitWidth;
 
288
}
 
289
 
 
290
qreal QQuickTextPrivate::getImplicitHeight() const
 
291
{
 
292
    if (!requireImplicitSize) {
 
293
        QQuickTextPrivate *me = const_cast<QQuickTextPrivate*>(this);
 
294
        me->requireImplicitSize = true;
 
295
        me->updateSize();
 
296
    }
 
297
    return implicitHeight;
 
298
}
 
299
 
 
300
/*!
 
301
    \qmlproperty enumeration QtQuick2::Text::renderType
 
302
 
 
303
    Override the default rendering type for this component.
 
304
 
 
305
    Supported render types are:
 
306
    \list
 
307
    \li Text.QtRendering - the default
 
308
    \li Text.NativeRendering
 
309
    \endlist
 
310
 
 
311
    Select Text.NativeRendering if you prefer text to look native on the target platform and do
 
312
    not require advanced features such as transformation of the text. Using such features in
 
313
    combination with the NativeRendering render type will lend poor and sometimes pixelated
 
314
    results.
 
315
*/
 
316
QQuickText::RenderType QQuickText::renderType() const
 
317
{
 
318
    Q_D(const QQuickText);
 
319
    return d->renderType;
 
320
}
 
321
 
 
322
void QQuickText::setRenderType(QQuickText::RenderType renderType)
 
323
{
 
324
    Q_D(QQuickText);
 
325
    if (d->renderType == renderType)
 
326
        return;
 
327
 
 
328
    d->renderType = renderType;
 
329
    emit renderTypeChanged();
 
330
 
 
331
    if (isComponentComplete())
 
332
        d->updateLayout();
 
333
}
 
334
 
 
335
void QQuickText::q_imagesLoaded()
 
336
{
 
337
    Q_D(QQuickText);
 
338
    d->updateLayout();
 
339
}
 
340
 
 
341
void QQuickTextPrivate::updateLayout()
 
342
{
 
343
    Q_Q(QQuickText);
 
344
    if (!q->isComponentComplete()) {
 
345
        updateOnComponentComplete = true;
 
346
        return;
 
347
    }
 
348
    updateOnComponentComplete = false;
 
349
    layoutTextElided = false;
 
350
 
 
351
    if (!visibleImgTags.isEmpty())
 
352
        visibleImgTags.clear();
 
353
    needToUpdateLayout = false;
 
354
 
 
355
    // Setup instance of QTextLayout for all cases other than richtext
 
356
    if (!richText) {
 
357
        if (textHasChanged) {
 
358
            if (styledText && !text.isEmpty()) {
 
359
                layout.setFont(font);
 
360
                // needs temporary bool because formatModifiesFontSize is in a bit-field
 
361
                bool fontSizeModified = false;
 
362
                QQuickStyledText::parse(text, layout, imgTags, q->baseUrl(), qmlContext(q), !maximumLineCountValid, &fontSizeModified);
 
363
                formatModifiesFontSize = fontSizeModified;
 
364
                multilengthEos = -1;
 
365
            } else {
 
366
                layout.clearAdditionalFormats();
 
367
                if (elideLayout)
 
368
                    elideLayout->clearAdditionalFormats();
 
369
                QString tmp = text;
 
370
                multilengthEos = tmp.indexOf(QLatin1Char('\x9c'));
 
371
                if (multilengthEos != -1) {
 
372
                    tmp = tmp.mid(0, multilengthEos);
 
373
                    tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
 
374
                } else if (tmp.contains(QLatin1Char('\n'))) {
 
375
                    // Replace always does a detach.  Checking for the new line character first
 
376
                    // means iterating over those items again if found but prevents a realloc
 
377
                    // otherwise.
 
378
                    tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
 
379
                }
 
380
                layout.setText(tmp);
 
381
            }
 
382
            textHasChanged = false;
 
383
        }
 
384
    } else {
 
385
        ensureDoc();
 
386
        QTextBlockFormat::LineHeightTypes type;
 
387
        type = lineHeightMode() == QQuickText::FixedHeight ? QTextBlockFormat::FixedHeight : QTextBlockFormat::ProportionalHeight;
 
388
        QTextBlockFormat blockFormat;
 
389
        blockFormat.setLineHeight((lineHeightMode() == QQuickText::FixedHeight ? lineHeight() : lineHeight() * 100), type);
 
390
        for (QTextBlock it = extra->doc->begin(); it != extra->doc->end(); it = it.next()) {
 
391
            QTextCursor cursor(it);
 
392
            cursor.mergeBlockFormat(blockFormat);
 
393
        }
 
394
    }
 
395
 
 
396
    updateSize();
 
397
 
 
398
    if (needToUpdateLayout) {
 
399
        needToUpdateLayout = false;
 
400
        textHasChanged = true;
 
401
        updateLayout();
 
402
    }
 
403
}
 
404
 
 
405
void QQuickText::imageDownloadFinished()
 
406
{
 
407
    Q_D(QQuickText);
 
408
 
 
409
    (d->extra->nbActiveDownloads)--;
 
410
 
 
411
    // when all the remote images have been downloaded,
 
412
    // if one of the sizes was not specified at parsing time
 
413
    // we use the implicit size from pixmapcache and re-layout.
 
414
 
 
415
    if (d->extra.isAllocated() && d->extra->nbActiveDownloads == 0) {
 
416
        bool needToUpdateLayout = false;
 
417
        foreach (QQuickStyledTextImgTag *img, d->visibleImgTags) {
 
418
            if (!img->size.isValid()) {
 
419
                img->size = img->pix->implicitSize();
 
420
                needToUpdateLayout = true;
 
421
            }
 
422
        }
 
423
 
 
424
        if (needToUpdateLayout) {
 
425
            d->textHasChanged = true;
 
426
            d->updateLayout();
 
427
        } else {
 
428
            d->updateType = QQuickTextPrivate::UpdatePaintNode;
 
429
            update();
 
430
        }
 
431
    }
 
432
}
 
433
 
 
434
void QQuickTextPrivate::updateBaseline(qreal baseline, qreal dy)
 
435
{
 
436
    Q_Q(QQuickText);
 
437
 
 
438
    qreal yoff = 0;
 
439
 
 
440
    if (q->heightValid()) {
 
441
        if (vAlign == QQuickText::AlignBottom)
 
442
            yoff = dy;
 
443
        else if (vAlign == QQuickText::AlignVCenter)
 
444
            yoff = dy/2;
 
445
    }
 
446
 
 
447
    q->setBaselineOffset(baseline + yoff);
 
448
}
 
449
 
 
450
void QQuickTextPrivate::updateSize()
 
451
{
 
452
    Q_Q(QQuickText);
 
453
 
 
454
    if (!q->isComponentComplete()) {
 
455
        updateOnComponentComplete = true;
 
456
        return;
 
457
    }
 
458
 
 
459
    if (!requireImplicitSize) {
 
460
        emit q->implicitWidthChanged();
 
461
        emit q->implicitHeightChanged();
 
462
        // if the implicitWidth is used, then updateSize() has already been called (recursively)
 
463
        if (requireImplicitSize)
 
464
            return;
 
465
    }
 
466
 
 
467
    if (text.isEmpty() && !isLineLaidOutConnected() && fontSizeMode() == QQuickText::FixedSize) {
 
468
        // How much more expensive is it to just do a full layout on an empty string here?
 
469
        // There may be subtle differences in the height and baseline calculations between
 
470
        // QTextLayout and QFontMetrics and the number of variables that can affect the size
 
471
        // and position of a line is increasing.
 
472
        QFontMetricsF fm(font);
 
473
        qreal fontHeight = qCeil(fm.height());  // QScriptLine and therefore QTextLine rounds up
 
474
        if (!richText) {                        // line height, so we will as well.
 
475
            fontHeight = lineHeightMode() == QQuickText::FixedHeight
 
476
                    ? lineHeight()
 
477
                    : fontHeight * lineHeight();
 
478
        }
 
479
        updateBaseline(fm.ascent(), q->height() - fontHeight);
 
480
        q->setImplicitSize(0, fontHeight);
 
481
        layedOutTextRect = QRectF(0, 0, 0, fontHeight);
 
482
        emit q->contentSizeChanged();
 
483
        updateType = UpdatePaintNode;
 
484
        q->update();
 
485
        return;
 
486
    }
 
487
 
 
488
    QSizeF size(0, 0);
 
489
    QSizeF previousSize = layedOutTextRect.size();
 
490
 
 
491
    //setup instance of QTextLayout for all cases other than richtext
 
492
    if (!richText) {
 
493
        qreal baseline = 0;
 
494
        QRectF textRect = setupTextLayout(&baseline);
 
495
 
 
496
        if (internalWidthUpdate)    // probably the result of a binding loop, but by letting it
 
497
            return;      // get this far we'll get a warning to that effect if it is.
 
498
 
 
499
        layedOutTextRect = textRect;
 
500
        size = textRect.size();
 
501
        updateBaseline(baseline, q->height() - size.height());
 
502
    } else {
 
503
        widthExceeded = true; // always relayout rich text on width changes..
 
504
        heightExceeded = false; // rich text layout isn't affected by height changes.
 
505
        ensureDoc();
 
506
        extra->doc->setDefaultFont(font);
 
507
        QQuickText::HAlignment horizontalAlignment = q->effectiveHAlign();
 
508
        if (rightToLeftText) {
 
509
            if (horizontalAlignment == QQuickText::AlignLeft)
 
510
                horizontalAlignment = QQuickText::AlignRight;
 
511
            else if (horizontalAlignment == QQuickText::AlignRight)
 
512
                horizontalAlignment = QQuickText::AlignLeft;
 
513
        }
 
514
        QTextOption option;
 
515
        option.setAlignment((Qt::Alignment)int(horizontalAlignment | vAlign));
 
516
        option.setWrapMode(QTextOption::WrapMode(wrapMode));
 
517
        option.setUseDesignMetrics(renderType != QQuickText::NativeRendering);
 
518
        extra->doc->setDefaultTextOption(option);
 
519
        qreal naturalWidth = 0;
 
520
        if (requireImplicitSize && q->widthValid()) {
 
521
            extra->doc->setTextWidth(-1);
 
522
            naturalWidth = extra->doc->idealWidth();
 
523
            const bool wasInLayout = internalWidthUpdate;
 
524
            internalWidthUpdate = true;
 
525
            q->setImplicitWidth(naturalWidth);
 
526
            internalWidthUpdate = wasInLayout;
 
527
        }
 
528
        if (internalWidthUpdate)
 
529
            return;
 
530
        if (wrapMode != QQuickText::NoWrap && q->widthValid())
 
531
            extra->doc->setTextWidth(q->width());
 
532
        else
 
533
            extra->doc->setTextWidth(extra->doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug)
 
534
        widthExceeded = extra->doc->textWidth() < extra->doc->idealWidth();
 
535
        QSizeF dsize = extra->doc->size();
 
536
        layedOutTextRect = QRectF(QPointF(0,0), dsize);
 
537
        size = QSizeF(extra->doc->idealWidth(),dsize.height());
 
538
 
 
539
        QFontMetricsF fm(font);
 
540
        updateBaseline(fm.ascent(), q->height() - size.height());
 
541
 
 
542
        //### need to confirm cost of always setting these for richText
 
543
        internalWidthUpdate = true;
 
544
        qreal iWidth = -1;
 
545
        if (!q->widthValid())
 
546
            iWidth = size.width();
 
547
        if (iWidth > -1)
 
548
            q->setImplicitSize(iWidth, size.height());
 
549
        internalWidthUpdate = false;
 
550
 
 
551
        if (iWidth == -1)
 
552
            q->setImplicitHeight(size.height());
 
553
    }
 
554
 
 
555
    if (layedOutTextRect.size() != previousSize)
 
556
        emit q->contentSizeChanged();
 
557
    updateType = UpdatePaintNode;
 
558
    q->update();
 
559
}
 
560
 
 
561
QQuickTextLine::QQuickTextLine()
 
562
    : QObject(), m_line(0), m_height(0)
 
563
{
 
564
}
 
565
 
 
566
void QQuickTextLine::setLine(QTextLine *line)
 
567
{
 
568
    m_line = line;
 
569
}
 
570
 
 
571
void QQuickTextLine::setLineOffset(int offset)
 
572
{
 
573
    m_lineOffset = offset;
 
574
}
 
575
 
 
576
int QQuickTextLine::number() const
 
577
{
 
578
    if (m_line)
 
579
        return m_line->lineNumber() + m_lineOffset;
 
580
    return 0;
 
581
}
 
582
 
 
583
qreal QQuickTextLine::width() const
 
584
{
 
585
    if (m_line)
 
586
        return m_line->width();
 
587
    return 0;
 
588
}
 
589
 
 
590
void QQuickTextLine::setWidth(qreal width)
 
591
{
 
592
    if (m_line)
 
593
        m_line->setLineWidth(width);
 
594
}
 
595
 
 
596
qreal QQuickTextLine::height() const
 
597
{
 
598
    if (m_height)
 
599
        return m_height;
 
600
    if (m_line)
 
601
        return m_line->height();
 
602
    return 0;
 
603
}
 
604
 
 
605
void QQuickTextLine::setHeight(qreal height)
 
606
{
 
607
    if (m_line)
 
608
        m_line->setPosition(QPointF(m_line->x(), m_line->y() - m_line->height() + height));
 
609
    m_height = height;
 
610
}
 
611
 
 
612
qreal QQuickTextLine::x() const
 
613
{
 
614
    if (m_line)
 
615
        return m_line->x();
 
616
    return 0;
 
617
}
 
618
 
 
619
void QQuickTextLine::setX(qreal x)
 
620
{
 
621
    if (m_line)
 
622
        m_line->setPosition(QPointF(x, m_line->y()));
 
623
}
 
624
 
 
625
qreal QQuickTextLine::y() const
 
626
{
 
627
    if (m_line)
 
628
        return m_line->y();
 
629
    return 0;
 
630
}
 
631
 
 
632
void QQuickTextLine::setY(qreal y)
 
633
{
 
634
    if (m_line)
 
635
        m_line->setPosition(QPointF(m_line->x(), y));
 
636
}
 
637
 
 
638
/*!
 
639
    \qmlmethod QtQuick2::Text::doLayout()
 
640
 
 
641
    Triggers a re-layout of the displayed text.
 
642
*/
 
643
void QQuickText::doLayout()
 
644
{
 
645
    Q_D(QQuickText);
 
646
    d->updateSize();
 
647
}
 
648
 
 
649
bool QQuickTextPrivate::isLineLaidOutConnected()
 
650
{
 
651
    Q_Q(QQuickText);
 
652
    IS_SIGNAL_CONNECTED(q, QQuickText, lineLaidOut, (QQuickTextLine *));
 
653
}
 
654
 
 
655
void QQuickTextPrivate::setupCustomLineGeometry(QTextLine &line, qreal &height, int lineOffset)
 
656
{
 
657
    Q_Q(QQuickText);
 
658
 
 
659
    if (!textLine)
 
660
        textLine = new QQuickTextLine;
 
661
    textLine->setLine(&line);
 
662
    textLine->setY(height);
 
663
    textLine->setHeight(0);
 
664
    textLine->setLineOffset(lineOffset);
 
665
 
 
666
    // use the text item's width by default if it has one and wrap is on
 
667
    if (q->widthValid() && q->wrapMode() != QQuickText::NoWrap)
 
668
        textLine->setWidth(q->width());
 
669
    else
 
670
        textLine->setWidth(INT_MAX);
 
671
    if (lineHeight() != 1.0)
 
672
        textLine->setHeight((lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : line.height() * lineHeight());
 
673
 
 
674
    emit q->lineLaidOut(textLine);
 
675
 
 
676
    height += textLine->height();
 
677
}
 
678
 
 
679
void QQuickTextPrivate::elideFormats(
 
680
        const int start, const int length, int offset, QList<QTextLayout::FormatRange> *elidedFormats)
 
681
{
 
682
    const int end = start + length;
 
683
    QList<QTextLayout::FormatRange> formats = layout.additionalFormats();
 
684
    for (int i = 0; i < formats.count(); ++i) {
 
685
        QTextLayout::FormatRange format = formats.at(i);
 
686
        const int formatLength = qMin(format.start + format.length, end) - qMax(format.start, start);
 
687
        if (formatLength > 0) {
 
688
            format.start = qMax(offset, format.start - start + offset);
 
689
            format.length = formatLength;
 
690
            elidedFormats->append(format);
 
691
        }
 
692
    }
 
693
}
 
694
 
 
695
QString QQuickTextPrivate::elidedText(qreal lineWidth, const QTextLine &line, QTextLine *nextLine) const
 
696
{
 
697
    if (nextLine) {
 
698
        return layout.engine()->elidedText(
 
699
                Qt::TextElideMode(elideMode),
 
700
                QFixed::fromReal(lineWidth),
 
701
                0,
 
702
                line.textStart(),
 
703
                line.textLength() + nextLine->textLength());
 
704
    } else {
 
705
        QString elideText = layout.text().mid(line.textStart(), line.textLength());
 
706
        if (!styledText) {
 
707
            // QFontMetrics won't help eliding styled text.
 
708
            elideText[elideText.length() - 1] = elideChar;
 
709
            // Appending the elide character may push the line over the maximum width
 
710
            // in which case the elided text will need to be elided.
 
711
            QFontMetricsF metrics(layout.font());
 
712
            if (metrics.width(elideChar) + line.naturalTextWidth() >= lineWidth)
 
713
                elideText = metrics.elidedText(elideText, Qt::TextElideMode(elideMode), lineWidth);
 
714
        }
 
715
        return elideText;
 
716
    }
 
717
}
 
718
 
 
719
/*!
 
720
    Lays out the QQuickTextPrivate::layout QTextLayout in the constraints of the QQuickText.
 
721
 
 
722
    Returns the size of the final text.  This can be used to position the text vertically (the text is
 
723
    already absolutely positioned horizontally).
 
724
*/
 
725
 
 
726
QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
 
727
{
 
728
    Q_Q(QQuickText);
 
729
 
 
730
    bool singlelineElide = elideMode != QQuickText::ElideNone && q->widthValid();
 
731
    bool multilineElide = elideMode == QQuickText::ElideRight
 
732
            && q->widthValid()
 
733
            && (q->heightValid() || maximumLineCountValid);
 
734
 
 
735
    if ((!requireImplicitSize || (implicitWidthValid && implicitHeightValid))
 
736
            && ((singlelineElide && q->width() <= 0.) || (multilineElide && q->heightValid() && q->height() <= 0.))) {
 
737
        // we are elided and we have a zero width or height
 
738
        widthExceeded = q->widthValid() && q->width() <= 0.;
 
739
        heightExceeded = q->heightValid() && q->height() <= 0.;
 
740
 
 
741
        if (!truncated) {
 
742
            truncated = true;
 
743
            emit q->truncatedChanged();
 
744
        }
 
745
        if (lineCount) {
 
746
            lineCount = 0;
 
747
            emit q->lineCountChanged();
 
748
        }
 
749
 
 
750
        QFontMetricsF fm(font);
 
751
        qreal height = (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : qCeil(fm.height()) * lineHeight();
 
752
        *baseline = fm.ascent();
 
753
        return QRectF(0, 0, 0, height);
 
754
    }
 
755
 
 
756
    bool shouldUseDesignMetrics = renderType != QQuickText::NativeRendering;
 
757
 
 
758
    layout.setCacheEnabled(true);
 
759
    QTextOption textOption = layout.textOption();
 
760
    if (textOption.alignment() != q->effectiveHAlign()
 
761
            || textOption.wrapMode() != QTextOption::WrapMode(wrapMode)
 
762
            || textOption.useDesignMetrics() != shouldUseDesignMetrics) {
 
763
        textOption.setAlignment(Qt::Alignment(q->effectiveHAlign()));
 
764
        textOption.setWrapMode(QTextOption::WrapMode(wrapMode));
 
765
        textOption.setUseDesignMetrics(shouldUseDesignMetrics);
 
766
        layout.setTextOption(textOption);
 
767
    }
 
768
    if (layout.font() != font)
 
769
        layout.setFont(font);
 
770
 
 
771
    lineWidth = (q->widthValid() || implicitWidthValid) && q->width() > 0
 
772
            ? q->width()
 
773
            : FLT_MAX;
 
774
    qreal maxHeight = q->heightValid() ? q->height() : FLT_MAX;
 
775
 
 
776
    const bool customLayout = isLineLaidOutConnected();
 
777
    const bool wasTruncated = truncated;
 
778
 
 
779
    bool canWrap = wrapMode != QQuickText::NoWrap && q->widthValid();
 
780
 
 
781
    bool horizontalFit = fontSizeMode() & QQuickText::HorizontalFit && q->widthValid();
 
782
    bool verticalFit = fontSizeMode() & QQuickText::VerticalFit
 
783
            && (q->heightValid() || (maximumLineCountValid && canWrap));
 
784
 
 
785
    const bool pixelSize = font.pixelSize() != -1;
 
786
    QString layoutText = layout.text();
 
787
 
 
788
    int largeFont = pixelSize ? font.pixelSize() : font.pointSize();
 
789
    int smallFont = fontSizeMode() != QQuickText::FixedSize
 
790
            ? qMin(pixelSize ? minimumPixelSize() : minimumPointSize(), largeFont)
 
791
            : largeFont;
 
792
    int scaledFontSize = largeFont;
 
793
 
 
794
    widthExceeded = q->width() <= 0 && (singlelineElide || canWrap || horizontalFit);
 
795
    heightExceeded = q->height() <= 0 && (multilineElide || verticalFit);
 
796
 
 
797
    QRectF br;
 
798
 
 
799
    QFont scaledFont = font;
 
800
 
 
801
    int visibleCount = 0;
 
802
    bool elide;
 
803
    qreal height = 0;
 
804
    QString elideText;
 
805
    bool once = true;
 
806
    int elideStart = 0;
 
807
    int elideEnd = 0;
 
808
 
 
809
    int eos = multilengthEos;
 
810
 
 
811
    // Repeated layouts with reduced font sizes or abbreviated strings may be required if the text
 
812
    // doesn't fit within the item dimensions,  or a binding to implicitWidth/Height changes
 
813
    // the item dimensions.
 
814
    for (;;) {
 
815
        if (!once) {
 
816
            if (pixelSize)
 
817
                scaledFont.setPixelSize(scaledFontSize);
 
818
            else
 
819
                scaledFont.setPointSize(scaledFontSize);
 
820
            if (layout.font() != scaledFont)
 
821
                layout.setFont(scaledFont);
 
822
        }
 
823
 
 
824
        layout.beginLayout();
 
825
 
 
826
        bool wrapped = false;
 
827
        bool truncateHeight = false;
 
828
        truncated = false;
 
829
        elide = false;
 
830
        int unwrappedLineCount = 1;
 
831
        int maxLineCount = maximumLineCount();
 
832
        height = 0;
 
833
        qreal naturalHeight = 0;
 
834
        qreal previousHeight = 0;
 
835
        br = QRectF();
 
836
 
 
837
        QRectF unelidedRect;
 
838
        QTextLine line = layout.createLine();
 
839
        for (visibleCount = 1; ; ++visibleCount) {
 
840
            if (customLayout) {
 
841
                setupCustomLineGeometry(line, naturalHeight);
 
842
            } else {
 
843
                setLineGeometry(line, lineWidth, naturalHeight);
 
844
            }
 
845
 
 
846
            unelidedRect = br.united(line.naturalTextRect());
 
847
 
 
848
            // Elide the previous line if the accumulated height of the text exceeds the height
 
849
            // of the element.
 
850
            if (multilineElide && naturalHeight > maxHeight && visibleCount > 1) {
 
851
                elide = true;
 
852
                heightExceeded = true;
 
853
                if (eos != -1)  // There's an abbreviated string available, skip the rest as it's
 
854
                    break;      // all going to be discarded.
 
855
 
 
856
                truncated = true;
 
857
                truncateHeight = true;
 
858
 
 
859
                visibleCount -= 1;
 
860
 
 
861
                QTextLine previousLine = layout.lineAt(visibleCount - 1);
 
862
                elideText = layoutText.at(line.textStart() - 1) != QChar::LineSeparator
 
863
                        ? elidedText(lineWidth, previousLine, &line)
 
864
                        : elidedText(lineWidth, previousLine);
 
865
                elideStart = previousLine.textStart();
 
866
                // elideEnd isn't required for right eliding.
 
867
 
 
868
                height = previousHeight;
 
869
                break;
 
870
            }
 
871
 
 
872
            const QTextLine previousLine = line;
 
873
            line = layout.createLine();
 
874
            if (!line.isValid()) {
 
875
                if (singlelineElide && visibleCount == 1 && previousLine.naturalTextWidth() > lineWidth) {
 
876
                    // Elide a single previousLine of  text if its width exceeds the element width.
 
877
                    elide = true;
 
878
                    widthExceeded = true;
 
879
                    if (eos != -1) // There's an abbreviated string available.
 
880
                        break;
 
881
 
 
882
                    truncated = true;
 
883
                    elideText = layout.engine()->elidedText(
 
884
                            Qt::TextElideMode(elideMode),
 
885
                            QFixed::fromReal(lineWidth),
 
886
                            0,
 
887
                            previousLine.textStart(),
 
888
                            previousLine.textLength());
 
889
                    elideStart = previousLine.textStart();
 
890
                    elideEnd = elideStart + previousLine.textLength();
 
891
                } else {
 
892
                    br = unelidedRect;
 
893
                    height = naturalHeight;
 
894
                }
 
895
                break;
 
896
            } else {
 
897
                const bool wrappedLine = layoutText.at(line.textStart() - 1) != QChar::LineSeparator;
 
898
                wrapped |= wrappedLine;
 
899
 
 
900
                if (!wrappedLine)
 
901
                    ++unwrappedLineCount;
 
902
 
 
903
                // Stop if the maximum number of lines has been reached and elide the last line
 
904
                // if enabled.
 
905
                if (visibleCount == maxLineCount) {
 
906
                    truncated = true;
 
907
                    heightExceeded |= wrapped;
 
908
 
 
909
                    if (multilineElide) {
 
910
                        elide = true;
 
911
                        if (eos != -1)  // There's an abbreviated string available
 
912
                            break;
 
913
                        elideText = wrappedLine
 
914
                                ? elidedText(lineWidth, previousLine, &line)
 
915
                                : elidedText(lineWidth, previousLine);
 
916
                        elideStart = previousLine.textStart();
 
917
                        // elideEnd isn't required for right eliding.
 
918
                    } else {
 
919
                        br = unelidedRect;
 
920
                        height = naturalHeight;
 
921
                    }
 
922
                    break;
 
923
                }
 
924
            }
 
925
            br = unelidedRect;
 
926
            previousHeight = height;
 
927
            height = naturalHeight;
 
928
        }
 
929
        widthExceeded |= wrapped;
 
930
 
 
931
        // Save the implicit size of the text on the first layout only.
 
932
        if (once) {
 
933
            once = false;
 
934
 
 
935
            // If implicit sizes are required layout any additional lines up to the maximum line
 
936
            // count.
 
937
            if ((requireImplicitSize) && line.isValid() && unwrappedLineCount < maxLineCount) {
 
938
                // Layout the remainder of the wrapped lines up to maxLineCount to get the implicit
 
939
                // height.
 
940
                for (int lineCount = layout.lineCount(); lineCount < maxLineCount; ++lineCount) {
 
941
                    line = layout.createLine();
 
942
                    if (!line.isValid())
 
943
                        break;
 
944
                    if (layoutText.at(line.textStart() - 1) == QChar::LineSeparator)
 
945
                        ++unwrappedLineCount;
 
946
                    setLineGeometry(line, lineWidth, naturalHeight);
 
947
                }
 
948
 
 
949
                // Create the remainder of the unwrapped lines up to maxLineCount to get the
 
950
                // implicit width.
 
951
                if (line.isValid() && layoutText.at(line.textStart() + line.textLength()) != QChar::LineSeparator)
 
952
                    line = layout.createLine();
 
953
                for (; line.isValid() && unwrappedLineCount <= maxLineCount; ++unwrappedLineCount)
 
954
                    line = layout.createLine();
 
955
            }
 
956
            layout.endLayout();
 
957
 
 
958
            const qreal naturalWidth = layout.maximumWidth();
 
959
 
 
960
            bool wasInLayout = internalWidthUpdate;
 
961
            internalWidthUpdate = true;
 
962
            q->setImplicitSize(naturalWidth, naturalHeight);
 
963
            internalWidthUpdate = wasInLayout;
 
964
 
 
965
            // Update any variables that are dependent on the validity of the width or height.
 
966
            singlelineElide = elideMode != QQuickText::ElideNone && q->widthValid();
 
967
            multilineElide = elideMode == QQuickText::ElideRight
 
968
                    && q->widthValid()
 
969
                    && (q->heightValid() || maximumLineCountValid);
 
970
            canWrap = wrapMode != QQuickText::NoWrap && q->widthValid();
 
971
 
 
972
            horizontalFit = fontSizeMode() & QQuickText::HorizontalFit && q->widthValid();
 
973
            verticalFit = fontSizeMode() & QQuickText::VerticalFit
 
974
                    && (q->heightValid() || (maximumLineCountValid && canWrap));
 
975
 
 
976
            const qreal oldWidth = lineWidth;
 
977
            const qreal oldHeight = maxHeight;
 
978
 
 
979
            lineWidth = q->widthValid() && q->width() > 0 ? q->width() : naturalWidth;
 
980
            maxHeight = q->heightValid() ? q->height() : FLT_MAX;
 
981
 
 
982
            // If the width of the item has changed and it's possible the result of wrapping,
 
983
            // eliding, or scaling has changed do another layout.
 
984
            if ((lineWidth < qMin(oldWidth, naturalWidth) || (widthExceeded && lineWidth > oldWidth))
 
985
                    && (singlelineElide || multilineElide || canWrap || horizontalFit)) {
 
986
                widthExceeded = false;
 
987
                heightExceeded = false;
 
988
                continue;
 
989
            }
 
990
 
 
991
            // If the height of the item has changed and it's possible the result of eliding,
 
992
            // line count truncation or scaling has changed, do another layout.
 
993
            if ((maxHeight < qMin(oldHeight, naturalHeight) || (heightExceeded && maxHeight > oldHeight))
 
994
                    && (multilineElide || (canWrap && maximumLineCountValid))) {
 
995
                widthExceeded = false;
 
996
                heightExceeded = false;
 
997
                continue;
 
998
            }
 
999
 
 
1000
            // If the horizontal alignment is not left and the width was not valid we need to relayout
 
1001
            // now that we know the maximum line width.
 
1002
            if (!implicitWidthValid && lineCount > 1 && q->effectiveHAlign() != QQuickText::AlignLeft) {
 
1003
                widthExceeded = false;
 
1004
                heightExceeded = false;
 
1005
                continue;
 
1006
            }
 
1007
        } else {
 
1008
            layout.endLayout();
 
1009
        }
 
1010
 
 
1011
        // If the next needs to be elided and there's an abbreviated string available
 
1012
        // go back and do another layout with the abbreviated string.
 
1013
        if (eos != -1 && elide) {
 
1014
            int start = eos + 1;
 
1015
            eos = text.indexOf(QLatin1Char('\x9c'),  start);
 
1016
            layoutText = text.mid(start, eos != -1 ? eos - start : -1);
 
1017
            layoutText.replace(QLatin1Char('\n'), QChar::LineSeparator);
 
1018
            layout.setText(layoutText);
 
1019
            textHasChanged = true;
 
1020
            continue;
 
1021
        }
 
1022
 
 
1023
        br.moveTop(0);
 
1024
 
 
1025
        if (!horizontalFit && !verticalFit)
 
1026
            break;
 
1027
 
 
1028
        // Try and find a font size that better fits the dimensions of the element.
 
1029
        if (horizontalFit) {
 
1030
            if (unelidedRect.width() > lineWidth || (!verticalFit && wrapped)) {
 
1031
                widthExceeded = true;
 
1032
                largeFont = scaledFontSize - 1;
 
1033
                if (smallFont > largeFont)
 
1034
                    break;
 
1035
                scaledFontSize = (smallFont + largeFont) / 2;
 
1036
                if (pixelSize)
 
1037
                    scaledFont.setPixelSize(scaledFontSize);
 
1038
                else
 
1039
                    scaledFont.setPointSize(scaledFontSize);
 
1040
                continue;
 
1041
            } else if (!verticalFit) {
 
1042
                smallFont = scaledFontSize;
 
1043
                if (smallFont == largeFont)
 
1044
                    break;
 
1045
                scaledFontSize = (smallFont + largeFont + 1) / 2;
 
1046
            }
 
1047
        }
 
1048
 
 
1049
        if (verticalFit) {
 
1050
            if (truncateHeight || unelidedRect.height() > maxHeight) {
 
1051
                heightExceeded = true;
 
1052
                largeFont = scaledFontSize - 1;
 
1053
                if (smallFont > largeFont)
 
1054
                    break;
 
1055
                scaledFontSize = (smallFont + largeFont) / 2;
 
1056
 
 
1057
            } else {
 
1058
                smallFont = scaledFontSize;
 
1059
                if (smallFont == largeFont)
 
1060
                    break;
 
1061
                scaledFontSize = (smallFont + largeFont + 1) / 2;
 
1062
            }
 
1063
        }
 
1064
    }
 
1065
 
 
1066
    implicitWidthValid = true;
 
1067
    implicitHeightValid = true;
 
1068
 
 
1069
    if (eos != multilengthEos)
 
1070
        truncated = true;
 
1071
 
 
1072
    if (elide) {
 
1073
        if (!elideLayout) {
 
1074
            elideLayout = new QTextLayout;
 
1075
            elideLayout->setCacheEnabled(true);
 
1076
        }
 
1077
        if (styledText) {
 
1078
            QList<QTextLayout::FormatRange> formats;
 
1079
            switch (elideMode) {
 
1080
            case QQuickText::ElideRight:
 
1081
                elideFormats(elideStart, elideText.length() - 1, 0, &formats);
 
1082
                break;
 
1083
            case QQuickText::ElideLeft:
 
1084
                elideFormats(elideEnd - elideText.length() + 1, elideText.length() - 1, 1, &formats);
 
1085
                break;
 
1086
            case QQuickText::ElideMiddle: {
 
1087
                const int index = elideText.indexOf(elideChar);
 
1088
                if (index != -1) {
 
1089
                    elideFormats(elideStart, index, 0, &formats);
 
1090
                    elideFormats(
 
1091
                            elideEnd - elideText.length() + index + 1,
 
1092
                            elideText.length() - index - 1,
 
1093
                            index + 1,
 
1094
                            &formats);
 
1095
                }
 
1096
                break;
 
1097
            }
 
1098
            default:
 
1099
                break;
 
1100
            }
 
1101
            elideLayout->setAdditionalFormats(formats);
 
1102
        }
 
1103
 
 
1104
        elideLayout->setFont(layout.font());
 
1105
        elideLayout->setTextOption(layout.textOption());
 
1106
        elideLayout->setText(elideText);
 
1107
        elideLayout->beginLayout();
 
1108
 
 
1109
        QTextLine elidedLine = elideLayout->createLine();
 
1110
        elidedLine.setPosition(QPointF(0, height));
 
1111
        if (customLayout) {
 
1112
            setupCustomLineGeometry(elidedLine, height, visibleCount - 1);
 
1113
        } else {
 
1114
            setLineGeometry(elidedLine, lineWidth, height);
 
1115
        }
 
1116
        elideLayout->endLayout();
 
1117
 
 
1118
        br = br.united(elidedLine.naturalTextRect());
 
1119
 
 
1120
        if (visibleCount == 1)
 
1121
            layout.clearLayout();
 
1122
    } else {
 
1123
        delete elideLayout;
 
1124
        elideLayout = 0;
 
1125
    }
 
1126
 
 
1127
    QTextLine firstLine = visibleCount == 1 && elideLayout
 
1128
            ? elideLayout->lineAt(0)
 
1129
            : layout.lineAt(0);
 
1130
    Q_ASSERT(firstLine.isValid());
 
1131
    *baseline = firstLine.y() + firstLine.ascent();
 
1132
 
 
1133
    if (!customLayout)
 
1134
        br.setHeight(height);
 
1135
 
 
1136
    //Update the number of visible lines
 
1137
    if (lineCount != visibleCount) {
 
1138
        lineCount = visibleCount;
 
1139
        emit q->lineCountChanged();
 
1140
    }
 
1141
 
 
1142
    if (truncated != wasTruncated)
 
1143
        emit q->truncatedChanged();
 
1144
 
 
1145
    return br;
 
1146
}
 
1147
 
 
1148
void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal &height)
 
1149
{
 
1150
    Q_Q(QQuickText);
 
1151
    line.setLineWidth(lineWidth);
 
1152
 
 
1153
    if (imgTags.isEmpty()) {
 
1154
        line.setPosition(QPointF(line.position().x(), height));
 
1155
        height += (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : line.height() * lineHeight();
 
1156
        return;
 
1157
    }
 
1158
 
 
1159
    qreal textTop = 0;
 
1160
    qreal textHeight = line.height();
 
1161
    qreal totalLineHeight = textHeight;
 
1162
 
 
1163
    QList<QQuickStyledTextImgTag *> imagesInLine;
 
1164
 
 
1165
    foreach (QQuickStyledTextImgTag *image, imgTags) {
 
1166
        if (image->position >= line.textStart() &&
 
1167
            image->position < line.textStart() + line.textLength()) {
 
1168
 
 
1169
            if (!image->pix) {
 
1170
                QUrl url = q->baseUrl().resolved(image->url);
 
1171
                image->pix = new QQuickPixmap(qmlEngine(q), url, image->size);
 
1172
                if (image->pix->isLoading()) {
 
1173
                    image->pix->connectFinished(q, SLOT(imageDownloadFinished()));
 
1174
                    if (!extra.isAllocated() || !extra->nbActiveDownloads)
 
1175
                        extra.value().nbActiveDownloads = 0;
 
1176
                    extra->nbActiveDownloads++;
 
1177
                } else if (image->pix->isReady()) {
 
1178
                    if (!image->size.isValid()) {
 
1179
                        image->size = image->pix->implicitSize();
 
1180
                        // if the size of the image was not explicitly set, we need to
 
1181
                        // call updateLayout() once again.
 
1182
                        needToUpdateLayout = true;
 
1183
                    }
 
1184
                } else if (image->pix->isError()) {
 
1185
                    qmlInfo(q) << image->pix->error();
 
1186
                }
 
1187
            }
 
1188
 
 
1189
            qreal ih = qreal(image->size.height());
 
1190
            if (image->align == QQuickStyledTextImgTag::Top)
 
1191
                image->pos.setY(0);
 
1192
            else if (image->align == QQuickStyledTextImgTag::Middle)
 
1193
                image->pos.setY((textHeight / 2.0) - (ih / 2.0));
 
1194
            else
 
1195
                image->pos.setY(textHeight - ih);
 
1196
            imagesInLine << image;
 
1197
            textTop = qMax(textTop, qAbs(image->pos.y()));
 
1198
        }
 
1199
    }
 
1200
 
 
1201
    foreach (QQuickStyledTextImgTag *image, imagesInLine) {
 
1202
        totalLineHeight = qMax(totalLineHeight, textTop + image->pos.y() + image->size.height());
 
1203
        image->pos.setX(line.cursorToX(image->position));
 
1204
        image->pos.setY(image->pos.y() + height + textTop);
 
1205
        visibleImgTags << image;
 
1206
    }
 
1207
 
 
1208
    line.setPosition(QPointF(line.position().x(), height + textTop));
 
1209
    height += (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : totalLineHeight * lineHeight();
 
1210
}
 
1211
 
 
1212
/*!
 
1213
    Ensures the QQuickTextPrivate::doc variable is set to a valid text document
 
1214
*/
 
1215
void QQuickTextPrivate::ensureDoc()
 
1216
{
 
1217
    if (!extra.isAllocated() || !extra->doc) {
 
1218
        Q_Q(QQuickText);
 
1219
        extra.value().doc = new QQuickTextDocumentWithImageResources(q);
 
1220
        extra->doc->setPageSize(QSizeF(0, 0));
 
1221
        extra->doc->setDocumentMargin(0);
 
1222
        extra->doc->setBaseUrl(q->baseUrl());
 
1223
        qmlobject_connect(extra->doc, QQuickTextDocumentWithImageResources, SIGNAL(imagesLoaded()),
 
1224
                          q, QQuickText, SLOT(q_imagesLoaded()));
 
1225
    }
 
1226
}
 
1227
 
 
1228
/*!
 
1229
    \qmltype Text
 
1230
    \instantiates QQuickText
 
1231
    \inqmlmodule QtQuick 2
 
1232
    \ingroup qtquick-visual
 
1233
    \inherits Item
 
1234
    \brief Specifies how to add formatted text to a scene
 
1235
 
 
1236
    Text items can display both plain and rich text. For example, red text with
 
1237
    a specific font and size can be defined like this:
 
1238
 
 
1239
    \qml
 
1240
    Text {
 
1241
        text: "Hello World!"
 
1242
        font.family: "Helvetica"
 
1243
        font.pointSize: 24
 
1244
        color: "red"
 
1245
    }
 
1246
    \endqml
 
1247
 
 
1248
    Rich text is defined using HTML-style markup:
 
1249
 
 
1250
    \qml
 
1251
    Text {
 
1252
        text: "<b>Hello</b> <i>World!</i>"
 
1253
    }
 
1254
    \endqml
 
1255
 
 
1256
    \image declarative-text.png
 
1257
 
 
1258
    If height and width are not explicitly set, Text will attempt to determine how
 
1259
    much room is needed and set it accordingly. Unless \l wrapMode is set, it will always
 
1260
    prefer width to height (all text will be placed on a single line).
 
1261
 
 
1262
    The \l elide property can alternatively be used to fit a single line of
 
1263
    plain text to a set width.
 
1264
 
 
1265
    Note that the \l{Supported HTML Subset} is limited. Also, if the text contains
 
1266
    HTML img tags that load remote images, the text is reloaded.
 
1267
 
 
1268
    Text provides read-only text. For editable text, see \l TextEdit.
 
1269
 
 
1270
    \sa {declarative/text/fonts}{Fonts example}
 
1271
*/
 
1272
QQuickText::QQuickText(QQuickItem *parent)
 
1273
: QQuickImplicitSizeItem(*(new QQuickTextPrivate), parent)
 
1274
{
 
1275
    Q_D(QQuickText);
 
1276
    d->init();
 
1277
}
 
1278
 
 
1279
QQuickText::~QQuickText()
 
1280
{
 
1281
}
 
1282
 
 
1283
/*!
 
1284
  \qmlproperty bool QtQuick2::Text::clip
 
1285
  This property holds whether the text is clipped.
 
1286
 
 
1287
  Note that if the text does not fit in the bounding rectangle it will be abruptly chopped.
 
1288
 
 
1289
  If you want to display potentially long text in a limited space, you probably want to use \c elide instead.
 
1290
*/
 
1291
 
 
1292
/*!
 
1293
    \qmlsignal QtQuick2::Text::onLineLaidOut(object line)
 
1294
 
 
1295
    This handler is called for each line of text that is laid out during the layout
 
1296
    process. The specified \a line object provides more details about the line that
 
1297
    is currently being laid out.
 
1298
 
 
1299
    This gives the opportunity to position and resize a line as it is being laid out.
 
1300
    It can for example be used to create columns or lay out text around objects.
 
1301
 
 
1302
    The properties of the specified \a line object are:
 
1303
    \list
 
1304
    \li number (read-only)
 
1305
    \li x
 
1306
    \li y
 
1307
    \li width
 
1308
    \li height
 
1309
    \endlist
 
1310
 
 
1311
    For example, this will move the first 5 lines of a Text item by 100 pixels to the right:
 
1312
    \code
 
1313
    onLineLaidOut: {
 
1314
        if (line.number < 5) {
 
1315
            line.x = line.x + 100
 
1316
            line.width = line.width - 100
 
1317
        }
 
1318
    }
 
1319
    \endcode
 
1320
*/
 
1321
 
 
1322
/*!
 
1323
    \qmlsignal QtQuick2::Text::onLinkActivated(string link)
 
1324
 
 
1325
    This handler is called when the user clicks on a link embedded in the text.
 
1326
    The link must be in rich text or HTML format and the
 
1327
    \a link string provides access to the particular link.
 
1328
 
 
1329
    \snippet qml/text/onLinkActivated.qml 0
 
1330
 
 
1331
    The example code will display the text
 
1332
    "The main website is at \l{http://qt.nokia.com}{Nokia Qt DF}."
 
1333
 
 
1334
    Clicking on the highlighted link will output
 
1335
    \tt{http://qt.nokia.com link activated} to the console.
 
1336
*/
 
1337
 
 
1338
/*!
 
1339
    \qmlproperty string QtQuick2::Text::font.family
 
1340
 
 
1341
    Sets the family name of the font.
 
1342
 
 
1343
    The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
 
1344
    If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
 
1345
    If the family isn't available a family will be set using the font matching algorithm.
 
1346
*/
 
1347
 
 
1348
/*!
 
1349
    \qmlproperty bool QtQuick2::Text::font.bold
 
1350
 
 
1351
    Sets whether the font weight is bold.
 
1352
*/
 
1353
 
 
1354
/*!
 
1355
    \qmlproperty enumeration QtQuick2::Text::font.weight
 
1356
 
 
1357
    Sets the font's weight.
 
1358
 
 
1359
    The weight can be one of:
 
1360
    \list
 
1361
    \li Font.Light
 
1362
    \li Font.Normal - the default
 
1363
    \li Font.DemiBold
 
1364
    \li Font.Bold
 
1365
    \li Font.Black
 
1366
    \endlist
 
1367
 
 
1368
    \qml
 
1369
    Text { text: "Hello"; font.weight: Font.DemiBold }
 
1370
    \endqml
 
1371
*/
 
1372
 
 
1373
/*!
 
1374
    \qmlproperty bool QtQuick2::Text::font.italic
 
1375
 
 
1376
    Sets whether the font has an italic style.
 
1377
*/
 
1378
 
 
1379
/*!
 
1380
    \qmlproperty bool QtQuick2::Text::font.underline
 
1381
 
 
1382
    Sets whether the text is underlined.
 
1383
*/
 
1384
 
 
1385
/*!
 
1386
    \qmlproperty bool QtQuick2::Text::font.strikeout
 
1387
 
 
1388
    Sets whether the font has a strikeout style.
 
1389
*/
 
1390
 
 
1391
/*!
 
1392
    \qmlproperty real QtQuick2::Text::font.pointSize
 
1393
 
 
1394
    Sets the font size in points. The point size must be greater than zero.
 
1395
*/
 
1396
 
 
1397
/*!
 
1398
    \qmlproperty int QtQuick2::Text::font.pixelSize
 
1399
 
 
1400
    Sets the font size in pixels.
 
1401
 
 
1402
    Using this function makes the font device dependent.
 
1403
    Use \c pointSize to set the size of the font in a device independent manner.
 
1404
*/
 
1405
 
 
1406
/*!
 
1407
    \qmlproperty real QtQuick2::Text::font.letterSpacing
 
1408
 
 
1409
    Sets the letter spacing for the font.
 
1410
 
 
1411
    Letter spacing changes the default spacing between individual letters in the font.
 
1412
    A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
 
1413
*/
 
1414
 
 
1415
/*!
 
1416
    \qmlproperty real QtQuick2::Text::font.wordSpacing
 
1417
 
 
1418
    Sets the word spacing for the font.
 
1419
 
 
1420
    Word spacing changes the default spacing between individual words.
 
1421
    A positive value increases the word spacing by a corresponding amount of pixels,
 
1422
    while a negative value decreases the inter-word spacing accordingly.
 
1423
*/
 
1424
 
 
1425
/*!
 
1426
    \qmlproperty enumeration QtQuick2::Text::font.capitalization
 
1427
 
 
1428
    Sets the capitalization for the text.
 
1429
 
 
1430
    \list
 
1431
    \li Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
 
1432
    \li Font.AllUppercase - This alters the text to be rendered in all uppercase type.
 
1433
    \li Font.AllLowercase - This alters the text to be rendered in all lowercase type.
 
1434
    \li Font.SmallCaps - This alters the text to be rendered in small-caps type.
 
1435
    \li Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
 
1436
    \endlist
 
1437
 
 
1438
    \qml
 
1439
    Text { text: "Hello"; font.capitalization: Font.AllLowercase }
 
1440
    \endqml
 
1441
*/
 
1442
QFont QQuickText::font() const
 
1443
{
 
1444
    Q_D(const QQuickText);
 
1445
    return d->sourceFont;
 
1446
}
 
1447
 
 
1448
void QQuickText::setFont(const QFont &font)
 
1449
{
 
1450
    Q_D(QQuickText);
 
1451
    if (d->sourceFont == font)
 
1452
        return;
 
1453
 
 
1454
    d->sourceFont = font;
 
1455
    QFont oldFont = d->font;
 
1456
    d->font = font;
 
1457
 
 
1458
    if (d->font.pointSizeF() != -1) {
 
1459
        // 0.5pt resolution
 
1460
        qreal size = qRound(d->font.pointSizeF()*2.0);
 
1461
        d->font.setPointSizeF(size/2.0);
 
1462
    }
 
1463
 
 
1464
    if (oldFont != d->font) {
 
1465
        // if the format changes the size of the text
 
1466
        // with headings or <font> tag, we need to re-parse
 
1467
        if (d->formatModifiesFontSize)
 
1468
            d->textHasChanged = true;
 
1469
        d->implicitWidthValid = false;
 
1470
        d->implicitHeightValid = false;
 
1471
        d->updateLayout();
 
1472
    }
 
1473
 
 
1474
    emit fontChanged(d->sourceFont);
 
1475
}
 
1476
 
 
1477
/*!
 
1478
    \qmlproperty string QtQuick2::Text::text
 
1479
 
 
1480
    The text to display. Text supports both plain and rich text strings.
 
1481
 
 
1482
    The item will try to automatically determine whether the text should
 
1483
    be treated as styled text. This determination is made using Qt::mightBeRichText().
 
1484
*/
 
1485
QString QQuickText::text() const
 
1486
{
 
1487
    Q_D(const QQuickText);
 
1488
    return d->text;
 
1489
}
 
1490
 
 
1491
void QQuickText::setText(const QString &n)
 
1492
{
 
1493
    Q_D(QQuickText);
 
1494
    if (d->text == n)
 
1495
        return;
 
1496
 
 
1497
    d->richText = d->format == RichText;
 
1498
    d->styledText = d->format == StyledText || (d->format == AutoText && Qt::mightBeRichText(n));
 
1499
    d->text = n;
 
1500
    if (isComponentComplete()) {
 
1501
        if (d->richText) {
 
1502
            d->ensureDoc();
 
1503
            d->extra->doc->setText(n);
 
1504
            d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
 
1505
        } else {
 
1506
            d->rightToLeftText = d->text.isRightToLeft();
 
1507
        }
 
1508
        d->determineHorizontalAlignment();
 
1509
    }
 
1510
    d->textHasChanged = true;
 
1511
    d->implicitWidthValid = false;
 
1512
    d->implicitHeightValid = false;
 
1513
    qDeleteAll(d->imgTags);
 
1514
    d->imgTags.clear();
 
1515
    d->updateLayout();
 
1516
    emit textChanged(d->text);
 
1517
}
 
1518
 
 
1519
/*!
 
1520
    \qmlproperty color QtQuick2::Text::color
 
1521
 
 
1522
    The text color.
 
1523
 
 
1524
    An example of green text defined using hexadecimal notation:
 
1525
    \qml
 
1526
    Text {
 
1527
        color: "#00FF00"
 
1528
        text: "green text"
 
1529
    }
 
1530
    \endqml
 
1531
 
 
1532
    An example of steel blue text defined using an SVG color name:
 
1533
    \qml
 
1534
    Text {
 
1535
        color: "steelblue"
 
1536
        text: "blue text"
 
1537
    }
 
1538
    \endqml
 
1539
*/
 
1540
QColor QQuickText::color() const
 
1541
{
 
1542
    Q_D(const QQuickText);
 
1543
    return QColor::fromRgba(d->color);
 
1544
}
 
1545
 
 
1546
void QQuickText::setColor(const QColor &color)
 
1547
{
 
1548
    Q_D(QQuickText);
 
1549
    QRgb rgb = color.rgba();
 
1550
    if (d->color == rgb)
 
1551
        return;
 
1552
 
 
1553
    d->color = rgb;
 
1554
    if (isComponentComplete())  {
 
1555
        d->updateType = QQuickTextPrivate::UpdatePaintNode;
 
1556
        update();
 
1557
    }
 
1558
    emit colorChanged();
 
1559
}
 
1560
 
 
1561
/*!
 
1562
    \qmlproperty color QtQuick2::Text::linkColor
 
1563
 
 
1564
    The color of links in the text.
 
1565
 
 
1566
    This property works with the StyledText \l textFormat, but not with RichText.
 
1567
    Link color in RichText can be specified by including CSS style tags in the
 
1568
    text.
 
1569
*/
 
1570
 
 
1571
QColor QQuickText::linkColor() const
 
1572
{
 
1573
    Q_D(const QQuickText);
 
1574
    return QColor::fromRgba(d->linkColor);
 
1575
}
 
1576
 
 
1577
void QQuickText::setLinkColor(const QColor &color)
 
1578
{
 
1579
    Q_D(QQuickText);
 
1580
    QRgb rgb = color.rgba();
 
1581
    if (d->linkColor == rgb)
 
1582
        return;
 
1583
 
 
1584
    d->linkColor = rgb;
 
1585
    update();
 
1586
    emit linkColorChanged();
 
1587
}
 
1588
 
 
1589
/*!
 
1590
    \qmlproperty enumeration QtQuick2::Text::style
 
1591
 
 
1592
    Set an additional text style.
 
1593
 
 
1594
    Supported text styles are:
 
1595
    \list
 
1596
    \li Text.Normal - the default
 
1597
    \li Text.Outline
 
1598
    \li Text.Raised
 
1599
    \li Text.Sunken
 
1600
    \endlist
 
1601
 
 
1602
    \qml
 
1603
    Row {
 
1604
        Text { font.pointSize: 24; text: "Normal" }
 
1605
        Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" }
 
1606
        Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" }
 
1607
        Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" }
 
1608
    }
 
1609
    \endqml
 
1610
 
 
1611
    \image declarative-textstyle.png
 
1612
*/
 
1613
QQuickText::TextStyle QQuickText::style() const
 
1614
{
 
1615
    Q_D(const QQuickText);
 
1616
    return d->style;
 
1617
}
 
1618
 
 
1619
void QQuickText::setStyle(QQuickText::TextStyle style)
 
1620
{
 
1621
    Q_D(QQuickText);
 
1622
    if (d->style == style)
 
1623
        return;
 
1624
 
 
1625
    d->style = style;
 
1626
    if (isComponentComplete()) {
 
1627
        d->updateType = QQuickTextPrivate::UpdatePaintNode;
 
1628
        update();
 
1629
    }
 
1630
    emit styleChanged(d->style);
 
1631
}
 
1632
 
 
1633
/*!
 
1634
    \qmlproperty color QtQuick2::Text::styleColor
 
1635
 
 
1636
    Defines the secondary color used by text styles.
 
1637
 
 
1638
    \c styleColor is used as the outline color for outlined text, and as the
 
1639
    shadow color for raised or sunken text. If no style has been set, it is not
 
1640
    used at all.
 
1641
 
 
1642
    \qml
 
1643
    Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" }
 
1644
    \endqml
 
1645
 
 
1646
    \sa style
 
1647
 */
 
1648
QColor QQuickText::styleColor() const
 
1649
{
 
1650
    Q_D(const QQuickText);
 
1651
    return QColor::fromRgba(d->styleColor);
 
1652
}
 
1653
 
 
1654
void QQuickText::setStyleColor(const QColor &color)
 
1655
{
 
1656
    Q_D(QQuickText);
 
1657
    QRgb rgb = color.rgba();
 
1658
    if (d->styleColor == rgb)
 
1659
        return;
 
1660
 
 
1661
    d->styleColor = rgb;
 
1662
    if (isComponentComplete()) {
 
1663
        d->updateType = QQuickTextPrivate::UpdatePaintNode;
 
1664
        update();
 
1665
    }
 
1666
    emit styleColorChanged();
 
1667
}
 
1668
 
 
1669
/*!
 
1670
    \qmlproperty enumeration QtQuick2::Text::horizontalAlignment
 
1671
    \qmlproperty enumeration QtQuick2::Text::verticalAlignment
 
1672
    \qmlproperty enumeration QtQuick2::Text::effectiveHorizontalAlignment
 
1673
 
 
1674
    Sets the horizontal and vertical alignment of the text within the Text items
 
1675
    width and height. By default, the text is vertically aligned to the top. Horizontal
 
1676
    alignment follows the natural alignment of the text, for example text that is read
 
1677
    from left to right will be aligned to the left.
 
1678
 
 
1679
    The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight, \c Text.AlignHCenter and
 
1680
    \c Text.AlignJustify.  The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom
 
1681
    and \c Text.AlignVCenter.
 
1682
 
 
1683
    Note that for a single line of text, the size of the text is the area of the text. In this common case,
 
1684
    all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will
 
1685
    need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to
 
1686
    that of the parent.
 
1687
 
 
1688
    When using the attached property LayoutMirroring::enabled to mirror application
 
1689
    layouts, the horizontal alignment of text will also be mirrored. However, the property
 
1690
    \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
 
1691
    of Text, use the read-only property \c effectiveHorizontalAlignment.
 
1692
*/
 
1693
QQuickText::HAlignment QQuickText::hAlign() const
 
1694
{
 
1695
    Q_D(const QQuickText);
 
1696
    return d->hAlign;
 
1697
}
 
1698
 
 
1699
void QQuickText::setHAlign(HAlignment align)
 
1700
{
 
1701
    Q_D(QQuickText);
 
1702
    bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
 
1703
    d->hAlignImplicit = false;
 
1704
    if (d->setHAlign(align, forceAlign) && isComponentComplete())
 
1705
        d->updateLayout();
 
1706
}
 
1707
 
 
1708
void QQuickText::resetHAlign()
 
1709
{
 
1710
    Q_D(QQuickText);
 
1711
    d->hAlignImplicit = true;
 
1712
    if (isComponentComplete() && d->determineHorizontalAlignment())
 
1713
        d->updateLayout();
 
1714
}
 
1715
 
 
1716
QQuickText::HAlignment QQuickText::effectiveHAlign() const
 
1717
{
 
1718
    Q_D(const QQuickText);
 
1719
    QQuickText::HAlignment effectiveAlignment = d->hAlign;
 
1720
    if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
 
1721
        switch (d->hAlign) {
 
1722
        case QQuickText::AlignLeft:
 
1723
            effectiveAlignment = QQuickText::AlignRight;
 
1724
            break;
 
1725
        case QQuickText::AlignRight:
 
1726
            effectiveAlignment = QQuickText::AlignLeft;
 
1727
            break;
 
1728
        default:
 
1729
            break;
 
1730
        }
 
1731
    }
 
1732
    return effectiveAlignment;
 
1733
}
 
1734
 
 
1735
bool QQuickTextPrivate::setHAlign(QQuickText::HAlignment alignment, bool forceAlign)
 
1736
{
 
1737
    Q_Q(QQuickText);
 
1738
    if (hAlign != alignment || forceAlign) {
 
1739
        QQuickText::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
 
1740
        hAlign = alignment;
 
1741
 
 
1742
        emit q->horizontalAlignmentChanged(hAlign);
 
1743
        if (oldEffectiveHAlign != q->effectiveHAlign())
 
1744
            emit q->effectiveHorizontalAlignmentChanged();
 
1745
        return true;
 
1746
    }
 
1747
    return false;
 
1748
}
 
1749
 
 
1750
bool QQuickTextPrivate::determineHorizontalAlignment()
 
1751
{
 
1752
    if (hAlignImplicit) {
 
1753
#ifndef QT_NO_IM
 
1754
        bool alignToRight = text.isEmpty() ? qApp->inputMethod()->inputDirection() == Qt::RightToLeft : rightToLeftText;
 
1755
#else
 
1756
        bool alignToRight = rightToLeftText;
 
1757
#endif
 
1758
        return setHAlign(alignToRight ? QQuickText::AlignRight : QQuickText::AlignLeft);
 
1759
    }
 
1760
    return false;
 
1761
}
 
1762
 
 
1763
void QQuickTextPrivate::mirrorChange()
 
1764
{
 
1765
    Q_Q(QQuickText);
 
1766
    if (q->isComponentComplete()) {
 
1767
        if (!hAlignImplicit && (hAlign == QQuickText::AlignRight || hAlign == QQuickText::AlignLeft)) {
 
1768
            updateLayout();
 
1769
            emit q->effectiveHorizontalAlignmentChanged();
 
1770
        }
 
1771
    }
 
1772
}
 
1773
 
 
1774
QQuickText::VAlignment QQuickText::vAlign() const
 
1775
{
 
1776
    Q_D(const QQuickText);
 
1777
    return d->vAlign;
 
1778
}
 
1779
 
 
1780
void QQuickText::setVAlign(VAlignment align)
 
1781
{
 
1782
    Q_D(QQuickText);
 
1783
    if (d->vAlign == align)
 
1784
        return;
 
1785
 
 
1786
    d->vAlign = align;
 
1787
    emit verticalAlignmentChanged(align);
 
1788
}
 
1789
 
 
1790
/*!
 
1791
    \qmlproperty enumeration QtQuick2::Text::wrapMode
 
1792
 
 
1793
    Set this property to wrap the text to the Text item's width.  The text will only
 
1794
    wrap if an explicit width has been set.  wrapMode can be one of:
 
1795
 
 
1796
    \list
 
1797
    \li Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l contentWidth will exceed a set width.
 
1798
    \li Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l contentWidth will exceed a set width.
 
1799
    \li Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
 
1800
    \li 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.
 
1801
    \endlist
 
1802
*/
 
1803
QQuickText::WrapMode QQuickText::wrapMode() const
 
1804
{
 
1805
    Q_D(const QQuickText);
 
1806
    return d->wrapMode;
 
1807
}
 
1808
 
 
1809
void QQuickText::setWrapMode(WrapMode mode)
 
1810
{
 
1811
    Q_D(QQuickText);
 
1812
    if (mode == d->wrapMode)
 
1813
        return;
 
1814
 
 
1815
    d->wrapMode = mode;
 
1816
    d->updateLayout();
 
1817
 
 
1818
    emit wrapModeChanged();
 
1819
}
 
1820
 
 
1821
/*!
 
1822
    \qmlproperty int QtQuick2::Text::lineCount
 
1823
 
 
1824
    Returns the number of lines visible in the text item.
 
1825
 
 
1826
    This property is not supported for rich text.
 
1827
 
 
1828
    \sa maximumLineCount
 
1829
*/
 
1830
int QQuickText::lineCount() const
 
1831
{
 
1832
    Q_D(const QQuickText);
 
1833
    return d->lineCount;
 
1834
}
 
1835
 
 
1836
/*!
 
1837
    \qmlproperty bool QtQuick2::Text::truncated
 
1838
 
 
1839
    Returns true if the text has been truncated due to \l maximumLineCount
 
1840
    or \l elide.
 
1841
 
 
1842
    This property is not supported for rich text.
 
1843
 
 
1844
    \sa maximumLineCount, elide
 
1845
*/
 
1846
bool QQuickText::truncated() const
 
1847
{
 
1848
    Q_D(const QQuickText);
 
1849
    return d->truncated;
 
1850
}
 
1851
 
 
1852
/*!
 
1853
    \qmlproperty int QtQuick2::Text::maximumLineCount
 
1854
 
 
1855
    Set this property to limit the number of lines that the text item will show.
 
1856
    If elide is set to Text.ElideRight, the text will be elided appropriately.
 
1857
    By default, this is the value of the largest possible integer.
 
1858
 
 
1859
    This property is not supported for rich text.
 
1860
 
 
1861
    \sa lineCount, elide
 
1862
*/
 
1863
int QQuickText::maximumLineCount() const
 
1864
{
 
1865
    Q_D(const QQuickText);
 
1866
    return d->maximumLineCount();
 
1867
}
 
1868
 
 
1869
void QQuickText::setMaximumLineCount(int lines)
 
1870
{
 
1871
    Q_D(QQuickText);
 
1872
 
 
1873
    d->maximumLineCountValid = lines==INT_MAX ? false : true;
 
1874
    if (d->maximumLineCount() != lines) {
 
1875
        d->extra.value().maximumLineCount = lines;
 
1876
        d->implicitHeightValid = false;
 
1877
        d->updateLayout();
 
1878
        emit maximumLineCountChanged();
 
1879
    }
 
1880
}
 
1881
 
 
1882
void QQuickText::resetMaximumLineCount()
 
1883
{
 
1884
    Q_D(QQuickText);
 
1885
    setMaximumLineCount(INT_MAX);
 
1886
    if (d->truncated != false) {
 
1887
        d->truncated = false;
 
1888
        emit truncatedChanged();
 
1889
    }
 
1890
}
 
1891
 
 
1892
/*!
 
1893
    \qmlproperty enumeration QtQuick2::Text::textFormat
 
1894
 
 
1895
    The way the text property should be displayed.
 
1896
 
 
1897
    Supported text formats are:
 
1898
 
 
1899
    \list
 
1900
    \li Text.AutoText (default)
 
1901
    \li Text.PlainText
 
1902
    \li Text.StyledText
 
1903
    \li Text.RichText
 
1904
    \endlist
 
1905
 
 
1906
    If the text format is \c Text.AutoText the Text item
 
1907
    will automatically determine whether the text should be treated as
 
1908
    styled text.  This determination is made using Qt::mightBeRichText()
 
1909
    which uses a fast and therefore simple heuristic. It mainly checks
 
1910
    whether there is something that looks like a tag before the first
 
1911
    line break. Although the result may be correct for common cases,
 
1912
    there is no guarantee.
 
1913
 
 
1914
    Text.StyledText is an optimized format supporting some basic text
 
1915
    styling markup, in the style of HTML 3.2:
 
1916
 
 
1917
    \code
 
1918
    <b></b> - bold
 
1919
    <strong></strong> - bold
 
1920
    <i></i> - italic
 
1921
    <br> - new line
 
1922
    <p> - paragraph
 
1923
    <u> - underlined text
 
1924
    <font color="color_name" size="1-7"></font>
 
1925
    <h1> to <h6> - headers
 
1926
    <a href=""> - anchor
 
1927
    <img src="" align="top,middle,bottom" width="" height=""> - inline images
 
1928
    <ol type="">, <ul type=""> and <li> - ordered and unordered lists
 
1929
    <pre></pre> - preformatted
 
1930
    &gt; &lt; &amp;
 
1931
    \endcode
 
1932
 
 
1933
    \c Text.StyledText parser is strict, requiring tags to be correctly nested.
 
1934
 
 
1935
    \table
 
1936
    \row
 
1937
    \li
 
1938
    \qml
 
1939
Column {
 
1940
    Text {
 
1941
        font.pointSize: 24
 
1942
        text: "<b>Hello</b> <i>World!</i>"
 
1943
    }
 
1944
    Text {
 
1945
        font.pointSize: 24
 
1946
        textFormat: Text.RichText
 
1947
        text: "<b>Hello</b> <i>World!</i>"
 
1948
    }
 
1949
    Text {
 
1950
        font.pointSize: 24
 
1951
        textFormat: Text.PlainText
 
1952
        text: "<b>Hello</b> <i>World!</i>"
 
1953
    }
 
1954
}
 
1955
    \endqml
 
1956
    \li \image declarative-textformat.png
 
1957
    \endtable
 
1958
 
 
1959
    Text.RichText supports a larger subset of HTML 4, as described on the
 
1960
    \l {Supported HTML Subset} page. You should prefer using Text.PlainText
 
1961
    or Text.StyledText instead, as they offer better performance.
 
1962
*/
 
1963
QQuickText::TextFormat QQuickText::textFormat() const
 
1964
{
 
1965
    Q_D(const QQuickText);
 
1966
    return d->format;
 
1967
}
 
1968
 
 
1969
void QQuickText::setTextFormat(TextFormat format)
 
1970
{
 
1971
    Q_D(QQuickText);
 
1972
    if (format == d->format)
 
1973
        return;
 
1974
    d->format = format;
 
1975
    bool wasRich = d->richText;
 
1976
    d->richText = format == RichText;
 
1977
    d->styledText = format == StyledText || (format == AutoText && Qt::mightBeRichText(d->text));
 
1978
 
 
1979
    if (isComponentComplete()) {
 
1980
        if (!wasRich && d->richText) {
 
1981
            d->ensureDoc();
 
1982
            d->extra->doc->setText(d->text);
 
1983
            d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
 
1984
        } else {
 
1985
            d->rightToLeftText = d->text.isRightToLeft();
 
1986
        }
 
1987
        d->determineHorizontalAlignment();
 
1988
    }
 
1989
    d->updateLayout();
 
1990
 
 
1991
    emit textFormatChanged(d->format);
 
1992
}
 
1993
 
 
1994
/*!
 
1995
    \qmlproperty enumeration QtQuick2::Text::elide
 
1996
 
 
1997
    Set this property to elide parts of the text fit to the Text item's width.
 
1998
    The text will only elide if an explicit width has been set.
 
1999
 
 
2000
    This property cannot be used with rich text.
 
2001
 
 
2002
    Eliding can be:
 
2003
    \list
 
2004
    \li Text.ElideNone  - the default
 
2005
    \li Text.ElideLeft
 
2006
    \li Text.ElideMiddle
 
2007
    \li Text.ElideRight
 
2008
    \endlist
 
2009
 
 
2010
    If this property is set to Text.ElideRight, it can be used with \l {wrapMode}{wrapped}
 
2011
    text. The text will only elide if \c maximumLineCount, or \c height has been set.
 
2012
    If both \c maximumLineCount and \c height are set, \c maximumLineCount will
 
2013
    apply unless the lines do not fit in the height allowed.
 
2014
 
 
2015
    If the text is a multi-length string, and the mode is not \c Text.ElideNone,
 
2016
    the first string that fits will be used, otherwise the last will be elided.
 
2017
 
 
2018
    Multi-length strings are ordered from longest to shortest, separated by the
 
2019
    Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}).
 
2020
*/
 
2021
QQuickText::TextElideMode QQuickText::elideMode() const
 
2022
{
 
2023
    Q_D(const QQuickText);
 
2024
    return d->elideMode;
 
2025
}
 
2026
 
 
2027
void QQuickText::setElideMode(QQuickText::TextElideMode mode)
 
2028
{
 
2029
    Q_D(QQuickText);
 
2030
    if (mode == d->elideMode)
 
2031
        return;
 
2032
 
 
2033
    d->elideMode = mode;
 
2034
    d->updateLayout();
 
2035
 
 
2036
    emit elideModeChanged(mode);
 
2037
}
 
2038
 
 
2039
/*!
 
2040
    \qmlproperty url QtQuick2::Text::baseUrl
 
2041
 
 
2042
    This property specifies a base URL which is used to resolve relative URLs
 
2043
    within the text.
 
2044
 
 
2045
    Urls are resolved to be within the same directory as the target of the base
 
2046
    URL meaning any portion of the path after the last '/' will be ignored.
 
2047
 
 
2048
    \table
 
2049
    \header \li Base URL \li Relative URL \li Resolved URL
 
2050
    \row \li http://qt-project.org/ \li images/logo.png \li http://qt-project.org/images/logo.png
 
2051
    \row \li http://qt-project.org/index.html \li images/logo.png \li http://qt-project.org/images/logo.png
 
2052
    \row \li http://qt-project.org/content \li images/logo.png \li http://qt-project.org/content/images/logo.png
 
2053
    \row \li http://qt-project.org/content/ \li images/logo.png \li http://qt-project.org/content/images/logo.png
 
2054
    \row \li http://qt-project.org/content/index.html \li images/logo.png \li http://qt-project.org/content/images/logo.png
 
2055
    \row \li http://qt-project.org/content/index.html \li ../images/logo.png \li http://qt-project.org/images/logo.png
 
2056
    \row \li http://qt-project.org/content/index.html \li /images/logo.png \li http://qt-project.org/images/logo.png
 
2057
    \endtable
 
2058
 
 
2059
    The default value is the url of the QML file instantiating the Text item.
 
2060
*/
 
2061
 
 
2062
QUrl QQuickText::baseUrl() const
 
2063
{
 
2064
    Q_D(const QQuickText);
 
2065
    if (d->baseUrl.isEmpty()) {
 
2066
        if (QQmlContext *context = qmlContext(this))
 
2067
            const_cast<QQuickTextPrivate *>(d)->baseUrl = context->baseUrl();
 
2068
    }
 
2069
    return d->baseUrl;
 
2070
}
 
2071
 
 
2072
void QQuickText::setBaseUrl(const QUrl &url)
 
2073
{
 
2074
    Q_D(QQuickText);
 
2075
    if (baseUrl() != url) {
 
2076
        d->baseUrl = url;
 
2077
 
 
2078
        if (d->richText) {
 
2079
            d->ensureDoc();
 
2080
            d->extra->doc->setBaseUrl(url);
 
2081
        }
 
2082
        if (d->styledText) {
 
2083
            d->textHasChanged = true;
 
2084
            qDeleteAll(d->imgTags);
 
2085
            d->imgTags.clear();
 
2086
            d->updateLayout();
 
2087
        }
 
2088
        emit baseUrlChanged();
 
2089
    }
 
2090
}
 
2091
 
 
2092
void QQuickText::resetBaseUrl()
 
2093
{
 
2094
    if (QQmlContext *context = qmlContext(this))
 
2095
        setBaseUrl(context->baseUrl());
 
2096
    else
 
2097
        setBaseUrl(QUrl());
 
2098
}
 
2099
 
 
2100
/*! \internal */
 
2101
QRectF QQuickText::boundingRect() const
 
2102
{
 
2103
    Q_D(const QQuickText);
 
2104
 
 
2105
    QRectF rect = d->layedOutTextRect;
 
2106
    rect.moveLeft(QQuickTextUtil::alignedX(rect.width(), width(), d->hAlign));
 
2107
    rect.moveTop(QQuickTextUtil::alignedY(rect.height(), height(), d->vAlign));
 
2108
 
 
2109
    if (d->style != Normal)
 
2110
        rect.adjust(-1, 0, 1, 2);
 
2111
    // Could include font max left/right bearings to either side of rectangle.
 
2112
 
 
2113
    return rect;
 
2114
}
 
2115
 
 
2116
QRectF QQuickText::clipRect() const
 
2117
{
 
2118
    Q_D(const QQuickText);
 
2119
 
 
2120
    QRectF rect = QQuickImplicitSizeItem::clipRect();
 
2121
    if (d->style != Normal)
 
2122
        rect.adjust(-1, 0, 1, 2);
 
2123
    return rect;
 
2124
}
 
2125
 
 
2126
/*! \internal */
 
2127
void QQuickText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
 
2128
{
 
2129
    Q_D(QQuickText);
 
2130
    if (d->text.isEmpty()) {
 
2131
        QQuickItem::geometryChanged(newGeometry, oldGeometry);
 
2132
        return;
 
2133
    }
 
2134
 
 
2135
    bool widthChanged = newGeometry.width() != oldGeometry.width();
 
2136
    bool heightChanged = newGeometry.height() != oldGeometry.height();
 
2137
    bool wrapped = d->wrapMode != QQuickText::NoWrap;
 
2138
    bool elide = d->elideMode != QQuickText::ElideNone;
 
2139
    bool scaleFont = d->fontSizeMode() != QQuickText::FixedSize && (widthValid() || heightValid());
 
2140
    bool verticalScale = (d->fontSizeMode() & QQuickText::VerticalFit) && heightValid();
 
2141
 
 
2142
    bool widthMaximum = newGeometry.width() >= oldGeometry.width() && !d->widthExceeded;
 
2143
    bool heightMaximum = newGeometry.height() >= oldGeometry.height() && !d->heightExceeded;
 
2144
 
 
2145
    if ((!widthChanged && !heightChanged) || d->internalWidthUpdate)
 
2146
        goto geomChangeDone;
 
2147
 
 
2148
    if (effectiveHAlign() != QQuickText::AlignLeft && widthChanged) {
 
2149
        // If the width has changed and we're not left aligned do an update so the text is
 
2150
        // repositioned even if a full layout isn't required.
 
2151
        d->updateType = QQuickTextPrivate::UpdatePaintNode;
 
2152
        update();
 
2153
    }
 
2154
 
 
2155
    if (!wrapped && !elide && !scaleFont)
 
2156
        goto geomChangeDone; // left aligned unwrapped text without eliding never needs relayout
 
2157
 
 
2158
    if (elide // eliding and dimensions were and remain invalid;
 
2159
            && ((widthValid() && oldGeometry.width() <= 0 && newGeometry.width() <= 0)
 
2160
            || (heightValid() && oldGeometry.height() <= 0 && newGeometry.height() <= 0))) {
 
2161
        goto geomChangeDone;
 
2162
    }
 
2163
 
 
2164
    if (widthMaximum && heightMaximum && !d->isLineLaidOutConnected())  // Size is sufficient and growing.
 
2165
        goto geomChangeDone;
 
2166
 
 
2167
    if (!(widthChanged || widthMaximum) && !d->isLineLaidOutConnected()) { // only height has changed
 
2168
        if (newGeometry.height() > oldGeometry.height()) {
 
2169
            if (!d->heightExceeded) // Height is adequate and growing.
 
2170
                goto geomChangeDone;
 
2171
            if (d->lineCount == d->maximumLineCount())  // Reached maximum line and height is growing.
 
2172
                goto geomChangeDone;
 
2173
        } else if (newGeometry.height() < oldGeometry.height()) {
 
2174
            if (d->lineCount < 2 && !verticalScale && newGeometry.height() > 0)  // A single line won't be truncated until the text is 0 height.
 
2175
                goto geomChangeDone;
 
2176
 
 
2177
            if (!verticalScale // no scaling, no eliding, and either unwrapped, or no maximum line count.
 
2178
                    && d->elideMode != QQuickText::ElideRight
 
2179
                    && !(d->maximumLineCountValid && d->widthExceeded)) {
 
2180
                goto geomChangeDone;
 
2181
            }
 
2182
        }
 
2183
    } else if (!heightChanged && widthMaximum) {
 
2184
        goto geomChangeDone;
 
2185
    }
 
2186
 
 
2187
    if (d->updateOnComponentComplete || d->textHasChanged) {
 
2188
        // We need to re-elide
 
2189
        d->updateLayout();
 
2190
    } else {
 
2191
        // We just need to re-layout
 
2192
        d->updateSize();
 
2193
    }
 
2194
 
 
2195
geomChangeDone:
 
2196
    QQuickItem::geometryChanged(newGeometry, oldGeometry);
 
2197
}
 
2198
 
 
2199
void QQuickText::triggerPreprocess()
 
2200
{
 
2201
    Q_D(QQuickText);
 
2202
    if (d->updateType == QQuickTextPrivate::UpdateNone)
 
2203
        d->updateType = QQuickTextPrivate::UpdatePreprocess;
 
2204
    update();
 
2205
}
 
2206
 
 
2207
QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
 
2208
{
 
2209
    Q_UNUSED(data);
 
2210
    Q_D(QQuickText);
 
2211
 
 
2212
    if (d->text.isEmpty()) {
 
2213
        delete oldNode;
 
2214
        return 0;
 
2215
    }
 
2216
 
 
2217
    if (d->updateType != QQuickTextPrivate::UpdatePaintNode && oldNode != 0) {
 
2218
        // Update done in preprocess() in the nodes
 
2219
        d->updateType = QQuickTextPrivate::UpdateNone;
 
2220
        return oldNode;
 
2221
    }
 
2222
 
 
2223
    d->updateType = QQuickTextPrivate::UpdateNone;
 
2224
 
 
2225
    const qreal dy = QQuickTextUtil::alignedY(d->layedOutTextRect.height(), height(), d->vAlign);
 
2226
 
 
2227
    QQuickTextNode *node = 0;
 
2228
    if (!oldNode) {
 
2229
        node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext(), this);
 
2230
    } else {
 
2231
        node = static_cast<QQuickTextNode *>(oldNode);
 
2232
    }
 
2233
 
 
2234
    node->setUseNativeRenderer(d->renderType == NativeRendering);
 
2235
    node->deleteContent();
 
2236
    node->setMatrix(QMatrix4x4());
 
2237
 
 
2238
    const QColor color = QColor::fromRgba(d->color);
 
2239
    const QColor styleColor = QColor::fromRgba(d->styleColor);
 
2240
    const QColor linkColor = QColor::fromRgba(d->linkColor);
 
2241
 
 
2242
    if (d->richText) {
 
2243
        const qreal dx = QQuickTextUtil::alignedX(d->layedOutTextRect.width(), width(), d->hAlign);
 
2244
        d->ensureDoc();
 
2245
        node->addTextDocument(QPointF(dx, dy), d->extra->doc, color, d->style, styleColor, linkColor);
 
2246
    } else if (d->layedOutTextRect.width() > 0) {
 
2247
        const qreal dx = QQuickTextUtil::alignedX(d->lineWidth, width(), d->hAlign);
 
2248
        int unelidedLineCount = d->lineCount;
 
2249
        if (d->elideLayout)
 
2250
            unelidedLineCount -= 1;
 
2251
        if (unelidedLineCount > 0) {
 
2252
            node->addTextLayout(
 
2253
                        QPointF(dx, dy),
 
2254
                        &d->layout,
 
2255
                        color, d->style, styleColor, linkColor,
 
2256
                        QColor(), QColor(), -1, -1,
 
2257
                        0, unelidedLineCount);
 
2258
        }
 
2259
        if (d->elideLayout)
 
2260
            node->addTextLayout(QPointF(dx, dy), d->elideLayout, color, d->style, styleColor, linkColor);
 
2261
 
 
2262
        foreach (QQuickStyledTextImgTag *img, d->visibleImgTags) {
 
2263
            QQuickPixmap *pix = img->pix;
 
2264
            if (pix && pix->isReady())
 
2265
                node->addImage(QRectF(img->pos.x() + dx, img->pos.y() + dy, pix->width(), pix->height()), pix->image());
 
2266
        }
 
2267
    }
 
2268
    return node;
 
2269
}
 
2270
 
 
2271
void QQuickText::updatePolish()
 
2272
{
 
2273
    Q_D(QQuickText);
 
2274
    d->updateSize();
 
2275
}
 
2276
 
 
2277
/*!
 
2278
    \qmlproperty real QtQuick2::Text::contentWidth
 
2279
 
 
2280
    Returns the width of the text, including width past the width
 
2281
    which is covered due to insufficient wrapping if WrapMode is set.
 
2282
*/
 
2283
qreal QQuickText::contentWidth() const
 
2284
{
 
2285
    Q_D(const QQuickText);
 
2286
    return d->layedOutTextRect.width();
 
2287
}
 
2288
 
 
2289
/*!
 
2290
    \qmlproperty real QtQuick2::Text::contentHeight
 
2291
 
 
2292
    Returns the height of the text, including height past the height
 
2293
    which is covered due to there being more text than fits in the set height.
 
2294
*/
 
2295
qreal QQuickText::contentHeight() const
 
2296
{
 
2297
    Q_D(const QQuickText);
 
2298
    return d->layedOutTextRect.height();
 
2299
}
 
2300
 
 
2301
/*!
 
2302
    \qmlproperty real QtQuick2::Text::lineHeight
 
2303
 
 
2304
    Sets the line height for the text.
 
2305
    The value can be in pixels or a multiplier depending on lineHeightMode.
 
2306
 
 
2307
    The default value is a multiplier of 1.0.
 
2308
    The line height must be a positive value.
 
2309
*/
 
2310
qreal QQuickText::lineHeight() const
 
2311
{
 
2312
    Q_D(const QQuickText);
 
2313
    return d->lineHeight();
 
2314
}
 
2315
 
 
2316
void QQuickText::setLineHeight(qreal lineHeight)
 
2317
{
 
2318
    Q_D(QQuickText);
 
2319
 
 
2320
    if ((d->lineHeight() == lineHeight) || (lineHeight < 0.0))
 
2321
        return;
 
2322
 
 
2323
    d->extra.value().lineHeight = lineHeight;
 
2324
    d->implicitHeightValid = false;
 
2325
    d->updateLayout();
 
2326
    emit lineHeightChanged(lineHeight);
 
2327
}
 
2328
 
 
2329
/*!
 
2330
    \qmlproperty enumeration QtQuick2::Text::lineHeightMode
 
2331
 
 
2332
    This property determines how the line height is specified.
 
2333
    The possible values are:
 
2334
 
 
2335
    \list
 
2336
    \li Text.ProportionalHeight (default) - this sets the spacing proportional to the
 
2337
       line (as a multiplier). For example, set to 2 for double spacing.
 
2338
    \li Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
 
2339
    \endlist
 
2340
*/
 
2341
QQuickText::LineHeightMode QQuickText::lineHeightMode() const
 
2342
{
 
2343
    Q_D(const QQuickText);
 
2344
    return d->lineHeightMode();
 
2345
}
 
2346
 
 
2347
void QQuickText::setLineHeightMode(LineHeightMode mode)
 
2348
{
 
2349
    Q_D(QQuickText);
 
2350
    if (mode == d->lineHeightMode())
 
2351
        return;
 
2352
 
 
2353
    d->implicitHeightValid = false;
 
2354
    d->extra.value().lineHeightMode = mode;
 
2355
    d->updateLayout();
 
2356
 
 
2357
    emit lineHeightModeChanged(mode);
 
2358
}
 
2359
 
 
2360
/*!
 
2361
    \qmlproperty enumeration QtQuick2::Text::fontSizeMode
 
2362
 
 
2363
    This property specifies how the font size of the displayed text is determined.
 
2364
    The possible values are:
 
2365
 
 
2366
    \list
 
2367
    \li Text.FixedSize (default) - The size specified by \l font.pixelSize
 
2368
    or \l font.pointSize is used.
 
2369
    \li Text.HorizontalFit - The largest size up to the size specified that fits
 
2370
    within the width of the item without wrapping is used.
 
2371
    \li Text.VerticalFit - The largest size up to the size specified that fits
 
2372
    the height of the item is used.
 
2373
    \li Text.Fit - The largest size up to the size specified the fits within the
 
2374
    width and height of the item is used.
 
2375
    \endlist
 
2376
 
 
2377
    The font size of fitted text has a minimum bound specified by the
 
2378
    minimumPointSize or minimumPixelSize property and maximum bound specified
 
2379
    by either the \l font.pointSize or \l font.pixelSize properties.
 
2380
 
 
2381
    If the text does not fit within the item bounds with the minimum font size
 
2382
    the text will be elided as per the \l elide property.
 
2383
*/
 
2384
 
 
2385
QQuickText::FontSizeMode QQuickText::fontSizeMode() const
 
2386
{
 
2387
    Q_D(const QQuickText);
 
2388
    return d->fontSizeMode();
 
2389
}
 
2390
 
 
2391
void QQuickText::setFontSizeMode(FontSizeMode mode)
 
2392
{
 
2393
    Q_D(QQuickText);
 
2394
    if (d->fontSizeMode() == mode)
 
2395
        return;
 
2396
 
 
2397
    polish();
 
2398
 
 
2399
    d->extra.value().fontSizeMode = mode;
 
2400
    emit fontSizeModeChanged();
 
2401
}
 
2402
 
 
2403
/*!
 
2404
    \qmlproperty int QtQuick2::Text::minimumPixelSize
 
2405
 
 
2406
    This property specifies the minimum font pixel size of text scaled by the
 
2407
    fontSizeMode property.
 
2408
 
 
2409
    If the fontSizeMode is Text.FixedSize or the \l font.pixelSize is -1 this
 
2410
    property is ignored.
 
2411
*/
 
2412
 
 
2413
int QQuickText::minimumPixelSize() const
 
2414
{
 
2415
    Q_D(const QQuickText);
 
2416
    return d->minimumPixelSize();
 
2417
}
 
2418
 
 
2419
void QQuickText::setMinimumPixelSize(int size)
 
2420
{
 
2421
    Q_D(QQuickText);
 
2422
    if (d->minimumPixelSize() == size)
 
2423
        return;
 
2424
 
 
2425
    if (d->fontSizeMode() != FixedSize && (widthValid() || heightValid()))
 
2426
        polish();
 
2427
    d->extra.value().minimumPixelSize = size;
 
2428
    emit minimumPixelSizeChanged();
 
2429
}
 
2430
 
 
2431
/*!
 
2432
    \qmlproperty int QtQuick2::Text::minimumPointSize
 
2433
 
 
2434
    This property specifies the minimum font point \l size of text scaled by
 
2435
    the fontSizeMode property.
 
2436
 
 
2437
    If the fontSizeMode is Text.FixedSize or the \l font.pointSize is -1 this
 
2438
    property is ignored.
 
2439
*/
 
2440
 
 
2441
int QQuickText::minimumPointSize() const
 
2442
{
 
2443
    Q_D(const QQuickText);
 
2444
    return d->minimumPointSize();
 
2445
}
 
2446
 
 
2447
void QQuickText::setMinimumPointSize(int size)
 
2448
{
 
2449
    Q_D(QQuickText);
 
2450
    if (d->minimumPointSize() == size)
 
2451
        return;
 
2452
 
 
2453
    if (d->fontSizeMode() != FixedSize && (widthValid() || heightValid()))
 
2454
        polish();
 
2455
    d->extra.value().minimumPointSize = size;
 
2456
    emit minimumPointSizeChanged();
 
2457
}
 
2458
 
 
2459
/*!
 
2460
    Returns the number of resources (images) that are being loaded asynchronously.
 
2461
*/
 
2462
int QQuickText::resourcesLoading() const
 
2463
{
 
2464
    Q_D(const QQuickText);
 
2465
    if (d->richText && d->extra.isAllocated() && d->extra->doc)
 
2466
        return d->extra->doc->resourcesLoading();
 
2467
    return 0;
 
2468
}
 
2469
 
 
2470
/*! \internal */
 
2471
void QQuickText::componentComplete()
 
2472
{
 
2473
    Q_D(QQuickText);
 
2474
    if (d->updateOnComponentComplete) {
 
2475
        if (d->richText) {
 
2476
            d->ensureDoc();
 
2477
            d->extra->doc->setText(d->text);
 
2478
            d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
 
2479
        } else {
 
2480
            d->rightToLeftText = d->text.isRightToLeft();
 
2481
        }
 
2482
        d->determineHorizontalAlignment();
 
2483
    }
 
2484
    QQuickItem::componentComplete();
 
2485
    if (d->updateOnComponentComplete)
 
2486
        d->updateLayout();
 
2487
}
 
2488
 
 
2489
QString QQuickTextPrivate::anchorAt(const QTextLayout *layout, const QPointF &mousePos)
 
2490
{
 
2491
    for (int i = 0; i < layout->lineCount(); ++i) {
 
2492
        QTextLine line = layout->lineAt(i);
 
2493
        if (line.naturalTextRect().contains(mousePos)) {
 
2494
            int charPos = line.xToCursor(mousePos.x(), QTextLine::CursorOnCharacter);
 
2495
            foreach (const QTextLayout::FormatRange &formatRange, layout->additionalFormats()) {
 
2496
                if (formatRange.format.isAnchor()
 
2497
                        && charPos >= formatRange.start
 
2498
                        && charPos < formatRange.start + formatRange.length) {
 
2499
                    return formatRange.format.anchorHref();
 
2500
                }
 
2501
            }
 
2502
            break;
 
2503
        }
 
2504
    }
 
2505
    return QString();
 
2506
}
 
2507
 
 
2508
QString QQuickTextPrivate::anchorAt(const QPointF &mousePos) const
 
2509
{
 
2510
    Q_Q(const QQuickText);
 
2511
    QPointF translatedMousePos = mousePos;
 
2512
    translatedMousePos.ry() -= QQuickTextUtil::alignedY(layedOutTextRect.height(), q->height(), vAlign);
 
2513
    if (styledText) {
 
2514
        QString link = anchorAt(&layout, translatedMousePos);
 
2515
        if (link.isEmpty() && elideLayout)
 
2516
            link = anchorAt(elideLayout, translatedMousePos);
 
2517
        return link;
 
2518
    } else if (richText && extra.isAllocated() && extra->doc) {
 
2519
        translatedMousePos.rx() -= QQuickTextUtil::alignedX(layedOutTextRect.width(), q->width(), hAlign);
 
2520
        return extra->doc->documentLayout()->anchorAt(translatedMousePos);
 
2521
    }
 
2522
    return QString();
 
2523
}
 
2524
 
 
2525
bool QQuickTextPrivate::isLinkActivatedConnected()
 
2526
{
 
2527
    Q_Q(QQuickText);
 
2528
    IS_SIGNAL_CONNECTED(q, QQuickText, linkActivated, (const QString &));
 
2529
}
 
2530
 
 
2531
/*!  \internal */
 
2532
void QQuickText::mousePressEvent(QMouseEvent *event)
 
2533
{
 
2534
    Q_D(QQuickText);
 
2535
 
 
2536
    QString link;
 
2537
    if (d->isLinkActivatedConnected())
 
2538
        link = d->anchorAt(event->localPos());
 
2539
 
 
2540
    if (link.isEmpty()) {
 
2541
        event->setAccepted(false);
 
2542
    } else {
 
2543
        d->extra.value().activeLink = link;
 
2544
    }
 
2545
 
 
2546
    // ### may malfunction if two of the same links are clicked & dragged onto each other)
 
2547
 
 
2548
    if (!event->isAccepted())
 
2549
        QQuickItem::mousePressEvent(event);
 
2550
}
 
2551
 
 
2552
 
 
2553
/*! \internal */
 
2554
void QQuickText::mouseReleaseEvent(QMouseEvent *event)
 
2555
{
 
2556
    Q_D(QQuickText);
 
2557
 
 
2558
    // ### confirm the link, and send a signal out
 
2559
 
 
2560
    QString link;
 
2561
    if (d->isLinkActivatedConnected())
 
2562
        link = d->anchorAt(event->localPos());
 
2563
 
 
2564
    if (!link.isEmpty() && d->extra.isAllocated() && d->extra->activeLink == link)
 
2565
        emit linkActivated(d->extra->activeLink);
 
2566
    else
 
2567
        event->setAccepted(false);
 
2568
 
 
2569
    if (!event->isAccepted())
 
2570
        QQuickItem::mouseReleaseEvent(event);
 
2571
}
 
2572
 
 
2573
QT_END_NAMESPACE