~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/gui/text/qtextlayout.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
 
4
**
 
5
** This file is part of the text module of the Qt Toolkit.
 
6
**
 
7
** This file may be distributed under the terms of the Q Public License
 
8
** as defined by Trolltech AS of Norway and appearing in the file
 
9
** LICENSE.QPL included in the packaging of this file.
 
10
**
 
11
** This file may be distributed and/or modified under the terms of the
 
12
** GNU General Public License version 2 as published by the Free Software
 
13
** Foundation and appearing in the file LICENSE.GPL included in the
 
14
** packaging of this file.
 
15
**
 
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
 
17
**   information about Qt Commercial License Agreements.
 
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
 
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
 
20
**
 
21
** Contact info@trolltech.com if any conditions of this licensing are
 
22
** not clear to you.
 
23
**
 
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
26
**
 
27
****************************************************************************/
 
28
 
 
29
#include "qtextlayout.h"
 
30
#include "qtextengine_p.h"
 
31
 
 
32
#include <qfont.h>
 
33
#include <qapplication.h>
 
34
#include <qpainter.h>
 
35
#include <qvarlengtharray.h>
 
36
#include <qtextformat.h>
 
37
#include <qabstracttextdocumentlayout.h>
 
38
#include "qtextdocument_p.h"
 
39
#include "qtextformat_p.h"
 
40
#include "qstyleoption.h"
 
41
#include <limits.h>
 
42
 
 
43
#include <qdebug.h>
 
44
 
 
45
#include "qfontengine_p.h"
 
46
 
 
47
static qreal alignLine(QTextEngine *eng, const QScriptLine &line)
 
48
{
 
49
    qreal x = 0;
 
50
    eng->justify(line);
 
51
    if (!line.justified) {
 
52
        int align = eng->option.alignment();
 
53
        if (align & Qt::AlignJustify && eng->option.textDirection() == Qt::RightToLeft)
 
54
            align = Qt::AlignRight;
 
55
        if (align & Qt::AlignRight)
 
56
            x = line.width - line.textWidth;
 
57
        else if (align & Qt::AlignHCenter)
 
58
            x = (line.width - line.textWidth)/2;
 
59
    }
 
60
    return x;
 
61
}
 
62
 
 
63
/*!
 
64
    \class QTextInlineObject
 
65
    \brief The QTextInlineObject class represents an inline object in
 
66
    a QTextLayout.
 
67
 
 
68
    \ingroup text
 
69
 
 
70
    This class is only used if the text layout is used to lay out
 
71
    parts of a QTextDocument.
 
72
 
 
73
    The inline object has various attributes that can be set, for
 
74
    example using, setWidth(), setAscent(), and setDescent(). The
 
75
    rectangle it occupies is given by rect(), and its direction by
 
76
    isRightToLeft(). Its position in the text layout is given by at(),
 
77
    and its format is given by format().
 
78
*/
 
79
 
 
80
/*!
 
81
    \fn QTextInlineObject::QTextInlineObject(int i, QTextEngine *e)
 
82
 
 
83
    Creates a new inline object for the item at position \a i in the
 
84
    text engine \a e.
 
85
*/
 
86
 
 
87
/*!
 
88
    \fn QTextInlineObject::QTextInlineObject()
 
89
 
 
90
    \internal
 
91
*/
 
92
 
 
93
/*!
 
94
    \fn bool QTextInlineObject::isValid() const
 
95
 
 
96
    Returns true if this inline object is valid; otherwise returns
 
97
    false.
 
98
*/
 
99
 
 
100
/*!
 
101
    Returns the inline object's rectangle.
 
102
 
 
103
    \sa ascent() descent() width()
 
104
*/
 
105
QRectF QTextInlineObject::rect() const
 
106
{
 
107
    QScriptItem& si = eng->layoutData->items[itm];
 
108
    return QRectF(0, -si.ascent, si.width, si.height());
 
109
}
 
110
 
 
111
/*!
 
112
    Returns the inline object's width.
 
113
 
 
114
    \sa ascent() descent() rect()
 
115
*/
 
116
qreal QTextInlineObject::width() const
 
117
{
 
118
    return eng->layoutData->items[itm].width;
 
119
}
 
120
 
 
121
/*!
 
122
    Returns the inline object's ascent.
 
123
 
 
124
    \sa descent() width() rect()
 
125
*/
 
126
qreal QTextInlineObject::ascent() const
 
127
{
 
128
    return eng->layoutData->items[itm].ascent;
 
129
}
 
130
 
 
131
/*!
 
132
    Returns the inline object's descent.
 
133
 
 
134
    \sa ascent() width() rect()
 
135
*/
 
136
qreal QTextInlineObject::descent() const
 
137
{
 
138
    return eng->layoutData->items[itm].descent;
 
139
}
 
140
 
 
141
/*!
 
142
    Returns the inline object's total height. This is equal to
 
143
    ascent() + descent() + 1.
 
144
 
 
145
    \sa ascent() descent() width() rect()
 
146
*/
 
147
qreal QTextInlineObject::height() const
 
148
{
 
149
    return eng->layoutData->items[itm].height();
 
150
}
 
151
 
 
152
 
 
153
/*!
 
154
    Sets the inline object's width to \a w.
 
155
 
 
156
    \sa width() ascent() descent() rect()
 
157
*/
 
158
void QTextInlineObject::setWidth(qreal w)
 
159
{
 
160
    eng->layoutData->items[itm].width = w;
 
161
}
 
162
 
 
163
/*!
 
164
    Sets the inline object's ascent to \a a.
 
165
 
 
166
    \sa ascent() setDescent() width() rect()
 
167
*/
 
168
void QTextInlineObject::setAscent(qreal a)
 
169
{
 
170
    eng->layoutData->items[itm].ascent = a;
 
171
}
 
172
 
 
173
/*!
 
174
    Sets the inline object's decent to \a d.
 
175
 
 
176
    \sa descent() setAscent() width() rect()
 
177
*/
 
178
void QTextInlineObject::setDescent(qreal d)
 
179
{
 
180
    eng->layoutData->items[itm].descent = d;
 
181
}
 
182
 
 
183
/*!
 
184
  The position of the inline object within the text layout.
 
185
*/
 
186
int QTextInlineObject::textPosition() const
 
187
{
 
188
    return eng->layoutData->items[itm].position;
 
189
}
 
190
 
 
191
/*!
 
192
  Returns an integer describing the format of the inline object
 
193
  within the text layout.
 
194
*/
 
195
int QTextInlineObject::formatIndex() const
 
196
{
 
197
    return eng->formatIndex(&eng->layoutData->items[itm]);
 
198
}
 
199
 
 
200
/*!
 
201
  Returns format of the inline object within the text layout.
 
202
*/
 
203
QTextFormat QTextInlineObject::format() const
 
204
{
 
205
    if (!eng->block.docHandle())
 
206
        return QTextFormat();
 
207
    return eng->formats()->format(eng->formatIndex(&eng->layoutData->items[itm]));
 
208
}
 
209
 
 
210
/*!
 
211
  Returns if the object should be laid out right-to-left or left-to-right.
 
212
*/
 
213
Qt::LayoutDirection QTextInlineObject::textDirection() const
 
214
{
 
215
    return (eng->layoutData->items[itm].analysis.bidiLevel % 2 ? Qt::RightToLeft : Qt::LeftToRight);
 
216
}
 
217
 
 
218
/*!
 
219
    \class QTextLayout
 
220
    \brief The QTextLayout class is used to lay out and paint a single
 
221
    paragraph of text.
 
222
 
 
223
    \ingroup text
 
224
 
 
225
    It offers most features expected from a modern text layout
 
226
    engine, including Unicode compliant rendering, line breaking and
 
227
    handling of cursor positioning. It can also produce and render
 
228
    device independent layout, something that is important for WYSIWYG
 
229
    applications.
 
230
 
 
231
    The class has a rather low level API and unless you intend to
 
232
    implement your own text rendering for some specialized widget, you
 
233
    probably won't need to use it directly.
 
234
 
 
235
    QTextLayout can currently deal with plain text and rich text
 
236
    paragraphs that are part of a QTextDocument.
 
237
 
 
238
    QTextLayout can be used to create a sequence of QTextLine's with
 
239
    given widths and can position them independently on the screen.
 
240
    Once the layout is done, these lines can be drawn on a paint
 
241
    device.
 
242
 
 
243
    Here's some pseudo code that presents the layout phase:
 
244
    \code
 
245
        int leading = fontMetrics.leading();
 
246
        int height = 0;
 
247
        int widthUsed = 0;
 
248
        textLayout.beginLayout();
 
249
        while (1) {
 
250
            QTextLine line = textLayout.createLine();
 
251
            if (!line.isValid())
 
252
                break;
 
253
 
 
254
            line.layout(lineWidth);
 
255
            height += leading;
 
256
            line.setPosition(QPoint(0, height));
 
257
            height += line.height();
 
258
            widthUsed = qMax(widthUsed, line.naturalTextWidth());
 
259
        }
 
260
        textLayout.endLayout();
 
261
    \endcode
 
262
 
 
263
    And here's some pseudo code that presents the painting phase:
 
264
    \code
 
265
        for (int i = 0; i < textLayout.lineCount(); ++i) {
 
266
            QTextLine line = textLayout.lineAt(i);
 
267
            line.draw(painter, rect.x() + xoffset + line.x(), rect.y() + yoffset);
 
268
        }
 
269
    \endcode
 
270
 
 
271
    The text layout's text is set in the constructor or with
 
272
    setText(). The layout can be seen as a sequence of QTextLine
 
273
    objects; use lineAt() or lineForTextPosition() to get a QTextLine,
 
274
    createLine() to create one. For a given position in the text you
 
275
    can find a valid cursor position with isValidCursorPosition(),
 
276
    nextCursorPosition(), and previousCursorPosition(). The layout
 
277
    itself can be positioned with setPosition(); it has a
 
278
    boundingRect(), and a minimumWidth() and a maximumWidth(). A text
 
279
    layout can be drawn on a painter device using draw().
 
280
 
 
281
*/
 
282
 
 
283
/*!
 
284
    \enum QTextLayout::CursorMode
 
285
 
 
286
    \value SkipCharacters
 
287
    \value SkipWords
 
288
*/
 
289
 
 
290
/*!
 
291
    \fn QTextEngine *QTextLayout::engine() const
 
292
    \internal
 
293
 
 
294
    Returns the text engine used to render the text layout.
 
295
*/
 
296
 
 
297
/*!
 
298
    Constructs an empty text layout.
 
299
 
 
300
    \sa setText()
 
301
*/
 
302
QTextLayout::QTextLayout()
 
303
{ d = new QTextEngine(); }
 
304
 
 
305
/*!
 
306
    Constructs a text layout to lay out the given \a text.
 
307
*/
 
308
QTextLayout::QTextLayout(const QString& text)
 
309
{
 
310
    d = new QTextEngine();
 
311
    d->text = text;
 
312
}
 
313
 
 
314
/*!
 
315
    Constructs a text layout to lay out the given \a text with the specified
 
316
    \a font.
 
317
 
 
318
    All the metric and layout calculations will be done in terms of
 
319
    the paint device, \a paintdevice. If \a paintdevice is 0 the
 
320
    calculations will be done in screen metrics.
 
321
*/
 
322
QTextLayout::QTextLayout(const QString& text, const QFont &font, QPaintDevice *paintdevice)
 
323
{
 
324
    QFontPrivate *f = paintdevice ? QFont(font, paintdevice).d : font.d;
 
325
    d = new QTextEngine((text.isNull() ? (const QString&)QString::fromLatin1("") : text), f);
 
326
}
 
327
 
 
328
/*!
 
329
    Constructs a text layout to lay out the given \a block.
 
330
*/
 
331
QTextLayout::QTextLayout(const QTextBlock &block)
 
332
{
 
333
    d = new QTextEngine();
 
334
    d->block = block;
 
335
}
 
336
 
 
337
/*!
 
338
    Destructs the layout.
 
339
*/
 
340
QTextLayout::~QTextLayout()
 
341
{
 
342
    delete d;
 
343
}
 
344
 
 
345
/*!
 
346
    Sets the layout's font to the given \a font. The layout is
 
347
    invalidated and must be laid out again.
 
348
 
 
349
    \sa text()
 
350
*/
 
351
void QTextLayout::setFont(const QFont &font)
 
352
{
 
353
    if (d->fnt && !d->fnt->ref.deref())
 
354
        delete d->fnt;
 
355
    d->fnt = font.d;
 
356
    d->fnt->ref.ref();
 
357
}
 
358
 
 
359
/*!
 
360
    Returns the current font that is used for the layout, or a default
 
361
    font if none is set.
 
362
*/
 
363
QFont QTextLayout::font() const
 
364
{
 
365
    return d->fnt ? QFont(d->fnt) : QFont();
 
366
}
 
367
 
 
368
/*!
 
369
    Sets the layout's text to the given \a string. The layout is
 
370
    invalidated and must be laid out again.
 
371
 
 
372
    \sa text()
 
373
*/
 
374
void QTextLayout::setText(const QString& string)
 
375
{
 
376
    d->invalidate();
 
377
    d->text = string;
 
378
}
 
379
 
 
380
/*!
 
381
    Returns the layout's text.
 
382
 
 
383
    \sa setText()
 
384
*/
 
385
QString QTextLayout::text() const
 
386
{
 
387
    return d->text;
 
388
}
 
389
 
 
390
/*!
 
391
  Sets the text option structure that controls the layout process to the
 
392
  given \a option.
 
393
 
 
394
  \sa textOption() QTextOption
 
395
*/
 
396
void QTextLayout::setTextOption(const QTextOption &option)
 
397
{
 
398
    d->option = option;
 
399
}
 
400
 
 
401
/*!
 
402
  Returns the current text option used to control the layout process.
 
403
 
 
404
  \sa setTextOption() QTextOption
 
405
*/
 
406
QTextOption QTextLayout::textOption() const
 
407
{
 
408
    return d->option;
 
409
}
 
410
 
 
411
/*!
 
412
    Sets the \a position and \a text of the area in the layout that is
 
413
    processed before editing occurs.
 
414
*/
 
415
void QTextLayout::setPreeditArea(int position, const QString &text)
 
416
{
 
417
    if (text.isEmpty()) {
 
418
        if (!d->specialData)
 
419
            return;
 
420
        if (d->specialData->addFormats.isEmpty()) {
 
421
            delete d->specialData;
 
422
            d->specialData = 0;
 
423
        } else {
 
424
            d->specialData->preeditText = QString();
 
425
            d->specialData->preeditPosition = -1;
 
426
        }
 
427
    } else {
 
428
        if (!d->specialData)
 
429
            d->specialData = new QTextEngine::SpecialData;
 
430
        d->specialData->preeditPosition = position;
 
431
        d->specialData->preeditText = text;
 
432
    }
 
433
    d->invalidate();
 
434
    if (d->block.docHandle())
 
435
        d->block.docHandle()->documentChange(d->block.position(), d->block.length());
 
436
}
 
437
 
 
438
/*!
 
439
    Returns the position of the area in the text layout that will be
 
440
    processed before editing occurs.
 
441
*/
 
442
int QTextLayout::preeditAreaPosition() const
 
443
{
 
444
    return d->specialData ? d->specialData->preeditPosition : -1;
 
445
}
 
446
 
 
447
/*!
 
448
    Returns the text that is inserted in the layout before editing occurs.
 
449
*/
 
450
QString QTextLayout::preeditAreaText() const
 
451
{
 
452
    return d->specialData ? d->specialData->preeditText : QString();
 
453
}
 
454
 
 
455
 
 
456
/*!
 
457
    Sets the additional formats supported by the text layout to \a
 
458
    formatList.
 
459
 
 
460
    \sa additionalFormats(), clearAdditionalFormats()
 
461
*/
 
462
void QTextLayout::setAdditionalFormats(const QList<FormatRange> &formatList)
 
463
{
 
464
    if (formatList.isEmpty()) {
 
465
        if (!d->specialData)
 
466
            return;
 
467
        if (d->specialData->preeditText.isEmpty()) {
 
468
            delete d->specialData;
 
469
            d->specialData = 0;
 
470
        } else {
 
471
            d->specialData->addFormats = formatList;
 
472
        }
 
473
        return;
 
474
    }
 
475
    if (!d->specialData) {
 
476
        d->specialData = new QTextEngine::SpecialData;
 
477
        d->specialData->preeditPosition = -1;
 
478
    }
 
479
    d->specialData->addFormats = formatList;
 
480
    if (d->block.docHandle())
 
481
        d->block.docHandle()->documentChange(d->block.position(), d->block.length());
 
482
}
 
483
 
 
484
/*!
 
485
    Returns the list of additional formats supported by the text layout.
 
486
 
 
487
    \sa setAdditionalFormats(), clearAdditionalFormats()
 
488
*/
 
489
QList<QTextLayout::FormatRange> QTextLayout::additionalFormats() const
 
490
{
 
491
    return d->specialData ? d->specialData->addFormats : QList<FormatRange>();
 
492
}
 
493
 
 
494
/*!
 
495
    Clears the list of additional formats supported by the text layout.
 
496
 
 
497
    \sa additionalFormats(), setAdditionalFormats()
 
498
*/
 
499
void QTextLayout::clearAdditionalFormats()
 
500
{
 
501
    setAdditionalFormats(QList<FormatRange>());
 
502
}
 
503
 
 
504
/*!
 
505
    Enables caching of the complete layout information if \a enable is
 
506
    true; otherwise disables layout caching. Usually
 
507
    QTextLayout throws most of the layouting information away after a
 
508
    call to endLayout() to reduce memory consumption. If you however
 
509
    want to draw the layouted text directly afterwards enabling caching
 
510
    might speed up drawing significantly.
 
511
 
 
512
    \sa cacheEnabled()
 
513
*/
 
514
void QTextLayout::setCacheEnabled(bool enable)
 
515
{
 
516
    d->cacheGlyphs = enable;
 
517
}
 
518
 
 
519
/*!
 
520
    Returns true if the complete layout information is cached; otherwise
 
521
    returns false.
 
522
 
 
523
    \sa setCacheEnabled()
 
524
*/
 
525
bool QTextLayout::cacheEnabled() const
 
526
{
 
527
    return d->cacheGlyphs;
 
528
}
 
529
 
 
530
/*!
 
531
    Begins the layout process.
 
532
*/
 
533
void QTextLayout::beginLayout()
 
534
{
 
535
#ifndef QT_NO_DEBUG
 
536
    if (d->layoutData && d->layoutData->inLayout) {
 
537
        qWarning("QTextLayout::beginLayout() called while doing layout");
 
538
        return;
 
539
    }
 
540
#endif
 
541
    d->invalidate();
 
542
    d->itemize();
 
543
    d->layoutData->inLayout = true;
 
544
}
 
545
 
 
546
/*!
 
547
    Ends the layout process.
 
548
*/
 
549
void QTextLayout::endLayout()
 
550
{
 
551
#ifndef QT_NO_DEBUG
 
552
    if (!d->layoutData || !d->layoutData->inLayout) {
 
553
        qWarning("QTextLayout::endLayout() called without beginLayout()");
 
554
        return;
 
555
    }
 
556
#endif
 
557
    int l = d->lines.size();
 
558
    if (l && d->lines.at(l-1).length < 0) {
 
559
        QTextLine(l-1, d).setNumColumns(INT_MAX);
 
560
    }
 
561
    d->layoutData->inLayout = false;
 
562
    if (!d->cacheGlyphs)
 
563
        d->freeMemory();
 
564
}
 
565
 
 
566
/*!
 
567
    Returns the next valid cursor position after \a oldPos that
 
568
    respects the given cursor \a mode.
 
569
 
 
570
    \sa isValidCursorPosition() previousCursorPosition()
 
571
*/
 
572
int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const
 
573
{
 
574
//      qDebug("looking for next cursor pos for %d", oldPos);
 
575
    const QCharAttributes *attributes = d->attributes();
 
576
    if (!attributes)
 
577
        return 0;
 
578
    int len = d->layoutData->string.length();
 
579
    if (oldPos >= len)
 
580
        return oldPos;
 
581
    oldPos++;
 
582
    if (mode == SkipCharacters) {
 
583
        while (oldPos < len && !attributes[oldPos].charStop)
 
584
            oldPos++;
 
585
    } else {
 
586
        while (oldPos < len && attributes[oldPos].whiteSpace)
 
587
            oldPos++;
 
588
 
 
589
        while (oldPos < len && !attributes[oldPos].wordStop && !attributes[oldPos-1].whiteSpace
 
590
               && !d->atWordSeparator(oldPos))
 
591
            oldPos++;
 
592
    }
 
593
//      qDebug("  -> %d", oldPos);
 
594
    return oldPos;
 
595
}
 
596
 
 
597
/*!
 
598
    Returns the first valid cursor position before \a oldPos that
 
599
    respects the given cursor \a mode.
 
600
 
 
601
    \sa isValidCursorPosition() nextCursorPosition()
 
602
*/
 
603
int QTextLayout::previousCursorPosition(int oldPos, CursorMode mode) const
 
604
{
 
605
//     qDebug("looking for previous cursor pos for %d", oldPos);
 
606
    const QCharAttributes *attributes = d->attributes();
 
607
    if (!attributes || oldPos <= 0)
 
608
        return 0;
 
609
    oldPos--;
 
610
    if (mode == SkipCharacters) {
 
611
        while (oldPos && !attributes[oldPos].charStop)
 
612
            oldPos--;
 
613
    } else {
 
614
        while (oldPos && attributes[oldPos].whiteSpace)
 
615
            oldPos--;
 
616
 
 
617
        while (oldPos && !attributes[oldPos].wordStop && !attributes[oldPos-1].whiteSpace
 
618
               && !d->atWordSeparator(oldPos - 1))
 
619
            oldPos--;
 
620
    }
 
621
//     qDebug("  -> %d", oldPos);
 
622
    return oldPos;
 
623
}
 
624
 
 
625
/*!
 
626
    Returns true if position \a pos is a valid cursor position.
 
627
 
 
628
    In a Unicode context some positions in the text are not valid
 
629
    cursor positions, because the position is inside a Unicode
 
630
    surrogate or a grapheme cluster.
 
631
 
 
632
    A grapheme cluster is a sequence of two or more Unicode characters
 
633
    that form one indivisible entity on the screen. For example the
 
634
    latin character `ļæ½' can be represented in Unicode by two
 
635
    characters, `A' (0x41), and the combining diaresis (0x308). A text
 
636
    cursor can only validly be positioned before or after these two
 
637
    characters, never between them since that wouldn't make sense. In
 
638
    indic languages every syllable forms a grapheme cluster.
 
639
*/
 
640
bool QTextLayout::isValidCursorPosition(int pos) const
 
641
{
 
642
    const QCharAttributes *attributes = d->attributes();
 
643
    if (!attributes || pos < 0 || pos > (int)d->layoutData->string.length())
 
644
        return false;
 
645
    return attributes[pos].charStop;
 
646
}
 
647
 
 
648
 
 
649
// ### DOC: Don't know what this really does.
 
650
// added a bit more description
 
651
/*!
 
652
    Returns a new text line to be laid out if there is text to be
 
653
    inserted into the layout; otherwise returns an invalid text line.
 
654
 
 
655
    The text layout creates a new line object that starts after the
 
656
    last line in the layout, or at the beginning if the layout is empty.
 
657
    The layout maintains an internal cursor, and each line is filled
 
658
    with text from the cursor position onwards when the
 
659
    QTextLine::setLineWidth() function is called.
 
660
 
 
661
    Once QTextLine::setLineWidth() is called, a new line can be created and
 
662
    filled with text. Repeating this process will lay out the whole block
 
663
    of text contained in the QTextLayout. If there is no text left to be
 
664
    inserted into the layout, the QTextLine returned will not be valid
 
665
    (isValid() will return false).
 
666
*/
 
667
QTextLine QTextLayout::createLine()
 
668
{
 
669
#ifndef QT_NO_DEBUG
 
670
    if (!d->layoutData || !d->layoutData->inLayout) {
 
671
        qWarning("QTextLayout::createLine() called without layouting");
 
672
        return QTextLine();
 
673
    }
 
674
#endif
 
675
    int l = d->lines.size();
 
676
    if (l && d->lines.at(l-1).length < 0) {
 
677
        QTextLine(l-1, d).setNumColumns(INT_MAX);
 
678
    }
 
679
    int from = l > 0 ? d->lines.at(l-1).from + d->lines.at(l-1).length : 0;
 
680
    if (l && from >= d->layoutData->string.length())
 
681
        return QTextLine();
 
682
 
 
683
    QScriptLine line;
 
684
    line.from = from;
 
685
    line.length = -1;
 
686
    line.justified = false;
 
687
    line.gridfitted = false;
 
688
 
 
689
    d->lines.append(line);
 
690
    return QTextLine(l, d);
 
691
}
 
692
 
 
693
/*!
 
694
    Returns the number of lines in this text layout.
 
695
 
 
696
    \sa lineAt()
 
697
*/
 
698
int QTextLayout::lineCount() const
 
699
{
 
700
    return d->lines.size();
 
701
}
 
702
 
 
703
/*!
 
704
    Returns the \a{i}-th line of text in this text layout.
 
705
 
 
706
    \sa lineCount() lineForTextPosition()
 
707
*/
 
708
QTextLine QTextLayout::lineAt(int i) const
 
709
{
 
710
    return QTextLine(i, d);
 
711
}
 
712
 
 
713
/*!
 
714
    Returns the line that contains the cursor position specified by \a pos.
 
715
 
 
716
    \sa isValidCursorPosition() lineAt()
 
717
*/
 
718
QTextLine QTextLayout::lineForTextPosition(int pos) const
 
719
{
 
720
    for (int i = 0; i < d->lines.size(); ++i) {
 
721
        const QScriptLine& line = d->lines[i];
 
722
        if (line.from + (int)line.length > pos)
 
723
            return QTextLine(i, d);
 
724
    }
 
725
    if (!d->layoutData)
 
726
        d->itemize();
 
727
    if (pos == d->layoutData->string.length() && d->lines.size())
 
728
        return QTextLine(d->lines.size()-1, d);
 
729
    return QTextLine();
 
730
}
 
731
 
 
732
/*!
 
733
    The global position of the layout. This is independent of the
 
734
    bounding rectangle and of the layout process.
 
735
 
 
736
    \sa setPosition()
 
737
*/
 
738
QPointF QTextLayout::position() const
 
739
{
 
740
    return d->position;
 
741
}
 
742
 
 
743
/*!
 
744
    Moves the text layout to point \a p.
 
745
 
 
746
    \sa position()
 
747
*/
 
748
void QTextLayout::setPosition(const QPointF &p)
 
749
{
 
750
    d->position = p;
 
751
}
 
752
 
 
753
/*!
 
754
    The smallest rectangle that contains all the lines in the layout.
 
755
*/
 
756
QRectF QTextLayout::boundingRect() const
 
757
{
 
758
    qreal xmin = 0, xmax = 0, ymin = 0, ymax = 0;
 
759
    for (int i = 0; i < d->lines.size(); ++i) {
 
760
        const QScriptLine &si = d->lines[i];
 
761
        xmin = qMin(xmin, si.x);
 
762
        ymin = qMin(ymin, si.y);
 
763
        xmax = qMax(xmax, si.x+si.width);
 
764
        // ### shouldn't the ascent be used in ymin???
 
765
        ymax = qMax(ymax, si.y+si.ascent+si.descent+1);
 
766
    }
 
767
    return QRectF(xmin, ymin, xmax-xmin, ymax-ymin);
 
768
}
 
769
 
 
770
/*!
 
771
    The minimum width the layout needs. This is the width of the
 
772
    layout's smallest non-breakable substring.
 
773
 
 
774
    \warning This function only returns a valid value after the layout
 
775
    has been done.
 
776
 
 
777
    \sa maximumWidth()
 
778
*/
 
779
qreal QTextLayout::minimumWidth() const
 
780
{
 
781
    return d->minWidth;
 
782
}
 
783
 
 
784
/*!
 
785
    The maximum width the layout could expand to; this is essentially
 
786
    the width of the entire text.
 
787
 
 
788
    \warning This function only returns a valid value after the layout
 
789
    has been done.
 
790
 
 
791
    \sa minimumWidth()
 
792
*/
 
793
qreal QTextLayout::maximumWidth() const
 
794
{
 
795
    return d->maxWidth;
 
796
}
 
797
 
 
798
/*!
 
799
    Draws the whole layout on the painter \a p at the position specified by
 
800
    \a pos.
 
801
    The rendered layout includes the given \a selections and is clipped within
 
802
    the rectangle specified by \a clip.
 
803
*/
 
804
void QTextLayout::draw(QPainter *p, const QPointF &pos, const QVector<FormatRange> &selections, const QRectF &clip) const
 
805
{
 
806
    Q_ASSERT(lineCount() != 0);
 
807
 
 
808
    if (!d->layoutData)
 
809
        d->itemize();
 
810
 
 
811
    QPointF position = pos + d->position;
 
812
 
 
813
    qreal clipy = qreal(INT_MIN/256);
 
814
    qreal clipe = qreal(INT_MAX/256);
 
815
    if (clip.isValid()) {
 
816
        clipy = clip.y() - position.y();
 
817
        clipe = clipy + clip.height();
 
818
    }
 
819
 
 
820
    for (int i = 0; i < d->lines.size(); i++) {
 
821
        QTextLine l(i, d);
 
822
        const QScriptLine &sl = d->lines[i];
 
823
 
 
824
        if (sl.y > clipe || (sl.y + sl.height()) < clipy)
 
825
            continue;
 
826
 
 
827
        l.draw(p, position);
 
828
        for (int i = 0; i < selections.size(); ++i)
 
829
            l.draw(p, position, selections.constData()+i);
 
830
    }
 
831
 
 
832
    if (!d->cacheGlyphs)
 
833
        d->freeMemory();
 
834
}
 
835
 
 
836
/*!
 
837
  \fn void QTextLayout::drawCursor(QPainter *painter, const QPointF &position, int cursorPosition) const
 
838
 
 
839
  Draws a text cursor with the current pen at the given \a position using the
 
840
  \a painter specified.
 
841
  The corresponding position within the text is specified by \a cursorPosition.
 
842
*/
 
843
void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition) const
 
844
{
 
845
    if (!d->layoutData)
 
846
        d->itemize();
 
847
 
 
848
    QPointF position = pos + d->position;
 
849
 
 
850
    for (int i = 0; i < d->lines.size(); i++) {
 
851
        QTextLine l(i, d);
 
852
        const QScriptLine &sl = d->lines[i];
 
853
 
 
854
        if ((sl.from <= cursorPosition && sl.from + (int)sl.length > cursorPosition)
 
855
            || (sl.from + (int)sl.length == cursorPosition && cursorPosition == d->layoutData->string.length())) {
 
856
 
 
857
            const qreal x = position.x() + l.cursorToX(cursorPosition);
 
858
 
 
859
            int itm = d->findItem(cursorPosition - 1);
 
860
            qreal ascent = sl.ascent;
 
861
            qreal descent = sl.descent;
 
862
            bool rightToLeft = (d->option.textDirection() == Qt::RightToLeft);
 
863
            if (itm >= 0) {
 
864
                const QScriptItem &si = d->layoutData->items.at(itm);
 
865
                if (si.ascent > 0.0)
 
866
                    ascent = si.ascent;
 
867
                if (si.descent > 0.0)
 
868
                    descent = si.descent;
 
869
                rightToLeft = si.analysis.bidiLevel % 2;
 
870
            }
 
871
            qreal y = position.y() + sl.y + sl.ascent - ascent;
 
872
            p->drawLine(QLineF(x, y, x, y + ascent + descent));
 
873
            if (d->layoutData->hasBidi) {
 
874
                const int arrow_extent = 4;
 
875
                int sign = rightToLeft ? -1 : 1;
 
876
                p->drawLine(QLineF(x, y, x + (sign * arrow_extent/2), y + arrow_extent/2));
 
877
                p->drawLine(QLineF(x, y+arrow_extent, x + (sign * arrow_extent/2), y + arrow_extent/2));
 
878
            }
 
879
            return;
 
880
        }
 
881
    }
 
882
 
 
883
}
 
884
 
 
885
/*!
 
886
    \class QTextLine
 
887
    \brief The QTextLine class represents a line of text inside a QTextLayout.
 
888
 
 
889
    \ingroup text
 
890
 
 
891
    A text line is usually created by QTextLayout::createLine().
 
892
 
 
893
    After being created, the line can be filled using the layout()
 
894
    function. A line has a number of attributes including the
 
895
    rectangle it occupies, rect(), its coordinates, x() and y(), its
 
896
    textLength(), width() and naturalTextWidth(), and its ascent() and decent()
 
897
    relative to the text. The position of the cursor in terms of the
 
898
    line is available from cursorToX() and its inverse from
 
899
    xToCursor(). A line can be moved with setPosition().
 
900
*/
 
901
 
 
902
/*!
 
903
    \enum QTextLine::Edge
 
904
 
 
905
    \value Leading
 
906
    \value Trailing
 
907
*/
 
908
 
 
909
/*!
 
910
    \enum QTextLine::CursorPosition
 
911
 
 
912
    \value CursorBetweenCharacters
 
913
    \value CursorOnCharacter
 
914
*/
 
915
 
 
916
/*!
 
917
    \fn QTextLine::QTextLine(int line, QTextEngine *e)
 
918
    \internal
 
919
 
 
920
    Constructs a new text line using the line at position \a line in
 
921
    the text engine \a e.
 
922
*/
 
923
 
 
924
/*!
 
925
    \fn QTextLine::QTextLine()
 
926
 
 
927
    Creates an invalid line.
 
928
*/
 
929
 
 
930
/*!
 
931
    \fn bool QTextLine::isValid() const
 
932
 
 
933
    Returns true if this text line is valid; otherwise returns false.
 
934
*/
 
935
 
 
936
/*!
 
937
    \fn int QTextLine::lineNumber() const
 
938
 
 
939
    Returns the position of the line in the text engine.
 
940
*/
 
941
 
 
942
 
 
943
/*!
 
944
    Returns the line's bounding rectangle.
 
945
 
 
946
    \sa x() y() textLength() width()
 
947
*/
 
948
QRectF QTextLine::rect() const
 
949
{
 
950
    const QScriptLine& sl = eng->lines[i];
 
951
    return QRectF(sl.x, sl.y, sl.width, sl.height());
 
952
}
 
953
 
 
954
/*!
 
955
    Returns the rectangle covered by the line.
 
956
*/
 
957
QRectF QTextLine::naturalTextRect() const
 
958
{
 
959
    const QScriptLine& sl = eng->lines[i];
 
960
    qreal x = sl.x + alignLine(eng, sl);
 
961
 
 
962
    qreal width = sl.textWidth;
 
963
    if (sl.justified)
 
964
        width = sl.width;
 
965
 
 
966
    return QRectF(x, sl.y, width, sl.height());
 
967
}
 
968
 
 
969
/*!
 
970
    Returns the line's x position.
 
971
 
 
972
    \sa rect() y() textLength() width()
 
973
*/
 
974
qreal QTextLine::x() const
 
975
{
 
976
    return eng->lines[i].x;
 
977
}
 
978
 
 
979
/*!
 
980
    Returns the line's y position.
 
981
 
 
982
    \sa x() rect() textLength() width()
 
983
*/
 
984
qreal QTextLine::y() const
 
985
{
 
986
    return eng->lines[i].y;
 
987
}
 
988
 
 
989
/*!
 
990
    Returns the line's width as specified by the layout() function.
 
991
 
 
992
    \sa naturalTextWidth() x() y() textLength() rect()
 
993
*/
 
994
qreal QTextLine::width() const
 
995
{
 
996
    return eng->lines[i].width;
 
997
}
 
998
 
 
999
 
 
1000
/*!
 
1001
    Returns the line's ascent.
 
1002
 
 
1003
    \sa descent() height()
 
1004
*/
 
1005
qreal QTextLine::ascent() const
 
1006
{
 
1007
    return eng->lines[i].ascent;
 
1008
}
 
1009
 
 
1010
/*!
 
1011
    Returns the line's descent.
 
1012
 
 
1013
    \sa ascent() height()
 
1014
*/
 
1015
qreal QTextLine::descent() const
 
1016
{
 
1017
    return eng->lines[i].descent;
 
1018
}
 
1019
 
 
1020
/*!
 
1021
    Returns the line's height. This is equal to ascent() + descent() + 1.
 
1022
 
 
1023
    \sa ascent() descent()
 
1024
*/
 
1025
qreal QTextLine::height() const
 
1026
{
 
1027
    return eng->lines[i].height();
 
1028
}
 
1029
 
 
1030
/*!
 
1031
    Returns the width of the line that is occupied by text. This is
 
1032
    always \<= to width(), and is the minimum width that could be used
 
1033
    by layout() without changing the line break position.
 
1034
*/
 
1035
qreal QTextLine::naturalTextWidth() const
 
1036
{
 
1037
    return eng->lines[i].textWidth;
 
1038
}
 
1039
 
 
1040
/*!
 
1041
    Lays out the line with the given \a width. The line is filled from
 
1042
    it's starting position with as many characters as will fit into
 
1043
    the line.
 
1044
*/
 
1045
void QTextLine::setLineWidth(qreal width)
 
1046
{
 
1047
    QScriptLine &line = eng->lines[i];
 
1048
    line.width = width;
 
1049
    line.length = 0;
 
1050
    line.textWidth = 0;
 
1051
    layout_helper(INT_MAX);
 
1052
}
 
1053
 
 
1054
/*!
 
1055
    Lays out the line. The line is filled from it's starting position
 
1056
    with as many characters as are specified by \a numColumns.
 
1057
*/
 
1058
void QTextLine::setNumColumns(int numColumns)
 
1059
{
 
1060
    QScriptLine &line = eng->lines[i];
 
1061
    line.width = qreal(INT_MAX/256);
 
1062
    line.length = 0;
 
1063
    line.textWidth = 0;
 
1064
    layout_helper(numColumns);
 
1065
}
 
1066
 
 
1067
void QTextLine::layout_helper(int maxGlyphs)
 
1068
{
 
1069
    QScriptLine &line = eng->lines[i];
 
1070
 
 
1071
    if (!eng->layoutData->items.size()) {
 
1072
        line.setDefaultHeight(eng);
 
1073
        return;
 
1074
    }
 
1075
 
 
1076
    Q_ASSERT(line.from < eng->layoutData->string.length());
 
1077
 
 
1078
    bool breakany = (eng->option.wrapMode() == QTextOption::WrapAnywhere);
 
1079
 
 
1080
    // #### binary search!
 
1081
    int item;
 
1082
    for (item = eng->layoutData->items.size()-1; item > 0; --item) {
 
1083
        if (eng->layoutData->items[item].position <= line.from)
 
1084
            break;
 
1085
    }
 
1086
 
 
1087
    qreal minw = 0, spacew = 0;
 
1088
    int glyphCount = 0;
 
1089
 
 
1090
//     qDebug("from: %d:   item=%d, total %d width available %f", line.from, item, eng->layoutData->items.size(), line.width);
 
1091
 
 
1092
    while (item < eng->layoutData->items.size()) {
 
1093
        const QCharAttributes *attributes = eng->attributes();
 
1094
        const QScriptItem &current = eng->layoutData->items[item];
 
1095
        if (!current.num_glyphs)
 
1096
            eng->shape(item);
 
1097
 
 
1098
        if (current.isObject) {
 
1099
            QTextFormat format = eng->formats()->format(eng->formatIndex(&eng->layoutData->items[item]));
 
1100
            if (eng->block.docHandle())
 
1101
                eng->docLayout()->positionInlineObject(QTextInlineObject(item, eng), eng->block.position() + current.position, format);
 
1102
            if (line.length && eng->option.wrapMode() != QTextOption::ManualWrap) {
 
1103
                if (line.textWidth + current.width > line.width || glyphCount > maxGlyphs)
 
1104
                    goto found;
 
1105
            }
 
1106
 
 
1107
            line.length++;
 
1108
            // the width of the linesep doesn't count into the textwidth
 
1109
            if (eng->layoutData->string.at(current.position) == QChar::LineSeparator) {
 
1110
                // if the line consists only of the line separator make sure
 
1111
                // we have a sane height
 
1112
                if (line.length == 1)
 
1113
                    line.setDefaultHeight(eng);
 
1114
                goto found;
 
1115
            }
 
1116
            line.textWidth += current.width;
 
1117
 
 
1118
            ++item;
 
1119
            ++glyphCount;
 
1120
            line.ascent = qMax(line.ascent, current.ascent);
 
1121
            line.descent = qMax(line.descent, current.descent);
 
1122
            continue;
 
1123
        } else if (current.isTab &&
 
1124
                   (eng->option.alignment() & Qt::AlignLeft)) {
 
1125
            qreal x = line.x + line.textWidth;
 
1126
            qreal nx = eng->nextTab(&current, x);
 
1127
            line.textWidth += nx - x;
 
1128
            line.length++;
 
1129
            ++item;
 
1130
            ++glyphCount;
 
1131
            line.ascent = qMax(line.ascent, current.ascent);
 
1132
            line.descent = qMax(line.descent, current.descent);
 
1133
            continue;
 
1134
        }
 
1135
 
 
1136
        int length = eng->length(item);
 
1137
 
 
1138
        const QCharAttributes *itemAttrs = attributes + current.position;
 
1139
        QGlyphLayout *glyphs = eng->glyphs(&current);
 
1140
        unsigned short *logClusters = eng->logClusters(&current);
 
1141
 
 
1142
        int pos = qMax(0, line.from - current.position);
 
1143
 
 
1144
        do {
 
1145
            int next = pos;
 
1146
 
 
1147
            qreal tmpw = 0;
 
1148
            if (!itemAttrs[next].whiteSpace) {
 
1149
                tmpw = spacew;
 
1150
                spacew = 0;
 
1151
                do {
 
1152
                    int gp = logClusters[next];
 
1153
                    do {
 
1154
                        ++next;
 
1155
                    } while (next < length && logClusters[next] == gp);
 
1156
                    do {
 
1157
                        tmpw += glyphs[gp].advance.x();
 
1158
                        ++gp;
 
1159
                    } while (gp < current.num_glyphs && !glyphs[gp].attributes.clusterStart);
 
1160
 
 
1161
                    Q_ASSERT((next == length && gp == current.num_glyphs) || logClusters[next] == gp);
 
1162
 
 
1163
                    ++glyphCount;
 
1164
                } while (next < length && !itemAttrs[next].whiteSpace && !itemAttrs[next].softBreak && !(breakany && itemAttrs[next].charStop));
 
1165
                minw = qMax(tmpw, minw);
 
1166
            }
 
1167
 
 
1168
            if (itemAttrs[next].softBreak)
 
1169
                breakany = false;
 
1170
 
 
1171
            while (next < length && itemAttrs[next].whiteSpace) {
 
1172
                int gp = logClusters[next];
 
1173
                do {
 
1174
                    ++next;
 
1175
                } while (next < length && logClusters[next] == gp);
 
1176
                do {
 
1177
                    spacew += glyphs[gp].advance.x();
 
1178
                    ++gp;
 
1179
                } while (gp < current.num_glyphs && !glyphs[gp].attributes.clusterStart);
 
1180
 
 
1181
                ++glyphCount;
 
1182
                Q_ASSERT((next == length && gp == current.num_glyphs) || logClusters[next] == gp);
 
1183
            }
 
1184
 
 
1185
//             qDebug("possible break at %d, chars (%d-%d) / glyphs (%d-%d): width %f, spacew=%f",
 
1186
//                    current.position + next, pos, next, logClusters[pos], logClusters[next], tmpw, spacew);
 
1187
 
 
1188
            if (line.length && tmpw != qreal(0) && (line.textWidth + tmpw > line.width || glyphCount > maxGlyphs)
 
1189
                && eng->option.wrapMode() != QTextOption::ManualWrap)
 
1190
                goto found;
 
1191
 
 
1192
            line.textWidth += tmpw;
 
1193
            line.length += next - pos;
 
1194
            line.ascent = qMax(line.ascent, current.ascent);
 
1195
            line.descent = qMax(line.descent, current.descent);
 
1196
 
 
1197
            pos = next;
 
1198
        } while (pos < length);
 
1199
        ++item;
 
1200
    }
 
1201
 found:
 
1202
//     qDebug("line length = %d, ascent=%f, descent=%f, textWidth=%f (spacew=%f)", line.length, line.ascent,
 
1203
//            line.descent, line.textWidth, spacew);
 
1204
//     qDebug("        : '%s'", eng->layoutData->string.mid(line.from, line.length).toUtf8().data());
 
1205
 
 
1206
    eng->minWidth = qMax(eng->minWidth, minw);
 
1207
    eng->maxWidth += line.textWidth;
 
1208
    if (line.textWidth > 0 && item < eng->layoutData->items.size())
 
1209
        eng->maxWidth += spacew;
 
1210
    if (eng->option.flags() & QTextOption::IncludeTrailingSpaces)
 
1211
        line.textWidth += spacew;
 
1212
 
 
1213
    line.justified = false;
 
1214
    line.gridfitted = false;
 
1215
}
 
1216
 
 
1217
/*!
 
1218
    Moves the line to position \a pos.
 
1219
*/
 
1220
void QTextLine::setPosition(const QPointF &pos)
 
1221
{
 
1222
    eng->lines[i].x = pos.x();
 
1223
    eng->lines[i].y = pos.y();
 
1224
}
 
1225
 
 
1226
// ### DOC: I have no idea what this means/does.
 
1227
// You create a text layout with a string of text. Once you layouted
 
1228
// it, it contains a number of QTextLines. from() returns the position
 
1229
// inside the text string where this line starts. If you e.g. has a
 
1230
// text of "This is a string", layouted into two lines (the second
 
1231
// starting at the word 'a'), layout.lineAt(0).from() == 0 and
 
1232
// layout.lineAt(1).from() == 8.
 
1233
/*!
 
1234
    Returns the start of the line from the beginning of the string
 
1235
    passed to the QTextLayout.
 
1236
*/
 
1237
int QTextLine::textStart() const
 
1238
{
 
1239
    return eng->lines[i].from;
 
1240
}
 
1241
 
 
1242
/*!
 
1243
    Returns the length of the text in the line.
 
1244
 
 
1245
    \sa naturalTextWidth()
 
1246
*/
 
1247
int QTextLine::textLength() const
 
1248
{
 
1249
    return eng->lines[i].length;
 
1250
}
 
1251
 
 
1252
static void drawMenuText(QPainter *p, qreal x, qreal y, const QScriptItem &si, QTextItemInt &gf, QTextEngine *eng,
 
1253
                         int start, int glyph_start)
 
1254
{
 
1255
    int ge = glyph_start + gf.num_glyphs;
 
1256
    int gs = glyph_start;
 
1257
    int end = start + gf.num_chars;
 
1258
    unsigned short *logClusters = eng->logClusters(&si);
 
1259
    QGlyphLayout *glyphs = eng->glyphs(&si);
 
1260
    qreal orig_width = gf.width;
 
1261
 
 
1262
    int *ul = eng->underlinePositions;
 
1263
    if (ul)
 
1264
        while (*ul != -1 && *ul < start)
 
1265
            ++ul;
 
1266
    bool rtl = si.analysis.bidiLevel % 2;
 
1267
    if (rtl)
 
1268
        x += si.width;
 
1269
 
 
1270
    do {
 
1271
        int gtmp = ge;
 
1272
        int stmp = end;
 
1273
        if (ul && *ul != -1 && *ul < end) {
 
1274
            stmp = *ul;
 
1275
            gtmp = logClusters[*ul-si.position];
 
1276
        }
 
1277
 
 
1278
        gf.num_glyphs = gtmp - gs;
 
1279
        gf.glyphs = glyphs + gs;
 
1280
        gf.num_chars = stmp - start;
 
1281
        gf.chars = eng->layoutData->string.unicode() + start;
 
1282
        qreal w = 0;
 
1283
        while (gs < gtmp) {
 
1284
            w += glyphs[gs].advance.x() + qreal(glyphs[gs].space_18d6)/qreal(64);
 
1285
            ++gs;
 
1286
        }
 
1287
        start = stmp;
 
1288
        gf.width = w;
 
1289
        if (rtl)
 
1290
            x -= w;
 
1291
        if (gf.num_chars)
 
1292
            p->drawTextItem(QPointF(x, y), gf);
 
1293
        if (!rtl)
 
1294
            x += w;
 
1295
        if (ul && *ul != -1 && *ul < end) {
 
1296
            // draw underline
 
1297
            gtmp = (*ul == end-1) ? ge : logClusters[*ul+1-si.position];
 
1298
            ++stmp;
 
1299
            gf.num_glyphs = gtmp - gs;
 
1300
            gf.glyphs = glyphs + gs;
 
1301
            gf.num_chars = stmp - start;
 
1302
            gf.chars = eng->layoutData->string.unicode() + start;
 
1303
            w = 0;
 
1304
            while (gs < gtmp) {
 
1305
                w += glyphs[gs].advance.x() + qreal(glyphs[gs].space_18d6)/qreal(64);
 
1306
                ++gs;
 
1307
            }
 
1308
            ++start;
 
1309
            gf.width = w;
 
1310
            gf.flags |= QTextItem::Underline;
 
1311
            if (rtl)
 
1312
                x -= w;
 
1313
            p->drawTextItem(QPointF(x, y), gf);
 
1314
            if (!rtl)
 
1315
                x += w;
 
1316
            gf.flags &= ~QTextItem::Underline;
 
1317
            ++gf.chars;
 
1318
            ++ul;
 
1319
        }
 
1320
    } while (gs < ge);
 
1321
 
 
1322
    gf.width = orig_width;
 
1323
}
 
1324
 
 
1325
 
 
1326
static void setPenAndDrawBackground(QPainter *p, const QPen &defaultPen, const QTextCharFormat &chf, const QRectF &r)
 
1327
{
 
1328
    QBrush c = chf.foreground();
 
1329
    if (c == Qt::NoBrush)
 
1330
        p->setPen(defaultPen);
 
1331
 
 
1332
    QBrush bg = chf.background();
 
1333
    if (bg.style() != Qt::NoBrush)
 
1334
        p->fillRect(r, bg);
 
1335
    if (c != Qt::NoBrush)
 
1336
        p->setPen(QPen(c, 0));
 
1337
}
 
1338
 
 
1339
/*!
 
1340
    \fn void QTextLine::draw(QPainter *painter, const QPointF &position, const QTextLayout::FormatRange *selection) const
 
1341
 
 
1342
    Draws a line on the given \a painter at the specified \a position.
 
1343
    The \a selection is reserved for internal use.
 
1344
*/
 
1345
void QTextLine::draw(QPainter *p, const QPointF &pos, const QTextLayout::FormatRange *selection) const
 
1346
{
 
1347
    const QScriptLine &line = eng->lines[i];
 
1348
 
 
1349
    if (!line.length)
 
1350
        return;
 
1351
 
 
1352
    QPen pen = p->pen();
 
1353
 
 
1354
    int lineEnd = line.from + line.length;
 
1355
    // don't draw trailing spaces or take them into the layout.
 
1356
    if (!(eng->option.flags() & QTextOption::IncludeTrailingSpaces)) {
 
1357
        const QCharAttributes *attributes = eng->attributes();
 
1358
        while (lineEnd > line.from && attributes[lineEnd-1].whiteSpace)
 
1359
            --lineEnd;
 
1360
    }
 
1361
 
 
1362
    int firstItem = eng->findItem(line.from);
 
1363
    int lastItem = eng->findItem(lineEnd - 1);
 
1364
    int nItems = lastItem-firstItem+1;
 
1365
 
 
1366
    qreal x = pos.x();
 
1367
    qreal y = pos.y();
 
1368
    x += line.x;
 
1369
    y += line.y + line.ascent;
 
1370
 
 
1371
    x += alignLine(eng, line);
 
1372
 
 
1373
    QVarLengthArray<int> visualOrder(nItems);
 
1374
    QVarLengthArray<uchar> levels(nItems);
 
1375
    for (int i = 0; i < nItems; ++i)
 
1376
        levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
 
1377
    QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
 
1378
 
 
1379
    QRectF outlineRect;
 
1380
    QPen outlinePen(Qt::NoPen);
 
1381
    if (selection) {
 
1382
        QVariant outline = selection->format.property(QTextFormat::OutlinePen);
 
1383
        if (outline.type() == QVariant::Pen)
 
1384
            outlinePen = qVariantValue<QPen>(outline);
 
1385
    }
 
1386
 
 
1387
    QFont f = eng->font();
 
1388
    for (int i = 0; i < nItems; ++i) {
 
1389
        int item = visualOrder[i]+firstItem;
 
1390
        QScriptItem &si = eng->layoutData->items[item];
 
1391
        int si_len = eng->length(item);
 
1392
        if (!si.num_glyphs)
 
1393
            eng->shape(item);
 
1394
 
 
1395
        if (si.isObject || si.isTab) {
 
1396
            if (eng->block.docHandle() &&
 
1397
                (!selection || (si.position < selection->start + selection->length
 
1398
                                && si.position + si_len > selection->start))) {
 
1399
                p->save();
 
1400
                QTextCharFormat format = eng->format(&si).toCharFormat();
 
1401
                if (selection)
 
1402
                    format.merge(selection->format);
 
1403
                qreal width = si.width;
 
1404
                if (si.isTab) {
 
1405
                    width = eng->nextTab(&si, x - pos.x()) - (x - pos.x());
 
1406
                }
 
1407
                setPenAndDrawBackground(p, pen, format, QRectF(x, y - line.ascent, width, line.height()));
 
1408
                if (si.isObject) {
 
1409
                    QRectF itemRect(x, y-si.ascent, width, si.height());
 
1410
                    eng->docLayout()->drawInlineObject(p, itemRect,
 
1411
                                                       QTextInlineObject(item, eng),
 
1412
                                                       si.position + eng->block.position(),
 
1413
                                                       format);
 
1414
                    if (selection) {
 
1415
                        QBrush bg = format.background();
 
1416
                        if (bg.style() != Qt::NoBrush) {
 
1417
                            QColor c = bg.color();
 
1418
                            c.setAlpha(128);
 
1419
                            p->fillRect(itemRect, c);
 
1420
                        }
 
1421
                        if (outlinePen.style() != Qt::NoPen)
 
1422
                            outlineRect = outlineRect.unite(itemRect);
 
1423
                    }
 
1424
                }
 
1425
                p->restore();
 
1426
            }
 
1427
 
 
1428
            if (si.isTab)
 
1429
                x = eng->nextTab(&si, x - pos.x()) + pos.x();
 
1430
            else
 
1431
                x += si.width;
 
1432
            continue;
 
1433
        }
 
1434
 
 
1435
        unsigned short *logClusters = eng->logClusters(&si);
 
1436
        QGlyphLayout *glyphs = eng->glyphs(&si);
 
1437
 
 
1438
        int start = qMax(line.from, si.position);
 
1439
        int gs = logClusters[start-si.position];
 
1440
        int end;
 
1441
        int ge;
 
1442
        if (lineEnd < si.position + eng->length(item)) {
 
1443
            end = lineEnd;
 
1444
            ge = logClusters[end-si.position];
 
1445
        } else {
 
1446
            end = si.position + si_len;
 
1447
            ge = si.num_glyphs;
 
1448
        }
 
1449
 
 
1450
        qreal itemBaseLine = y;
 
1451
 
 
1452
        QTextItemInt gf;
 
1453
        if (si.analysis.bidiLevel %2)
 
1454
            gf.flags |= QTextItem::RightToLeft;
 
1455
        gf.ascent = si.ascent;
 
1456
        gf.descent = si.descent;
 
1457
        gf.num_glyphs = ge - gs;
 
1458
        gf.glyphs = glyphs + gs;
 
1459
        gf.chars = eng->layoutData->string.unicode() + start;
 
1460
        gf.num_chars = end - start;
 
1461
        gf.width = 0;
 
1462
        int g = gs;
 
1463
        while (g < ge) {
 
1464
            gf.width += glyphs[g].advance.x() + qreal(glyphs[g].space_18d6)/qreal(64);
 
1465
            ++g;
 
1466
        }
 
1467
 
 
1468
        if (selection) {
 
1469
            int from = qMax(start, selection->start) - si.position;
 
1470
            int to = qMin(end, selection->start + selection->length) - si.position;
 
1471
            if (from >= to) {
 
1472
                x += gf.width;
 
1473
                continue;
 
1474
            }
 
1475
            int start_glyph = logClusters[from];
 
1476
            int end_glyph = (to == eng->length(item)) ? si.num_glyphs : logClusters[to];
 
1477
            qreal soff = 0;
 
1478
            qreal swidth = 0;
 
1479
            if (si.analysis.bidiLevel %2) {
 
1480
                for (int g = ge - 1; g >= end_glyph; --g)
 
1481
                    soff += glyphs[g].advance.x() + qreal(glyphs[g].space_18d6)/qreal(64);
 
1482
                for (int g = end_glyph - 1; g >= start_glyph; --g)
 
1483
                    swidth += glyphs[g].advance.x() + qreal(glyphs[g].space_18d6)/qreal(64);
 
1484
            } else {
 
1485
                for (int g = gs; g < start_glyph; ++g)
 
1486
                    soff += glyphs[g].advance.x() + qreal(glyphs[g].space_18d6)/qreal(64);
 
1487
                for (int g = start_glyph; g < end_glyph; ++g)
 
1488
                    swidth += glyphs[g].advance.x() + qreal(glyphs[g].space_18d6)/qreal(64);
 
1489
            }
 
1490
 
 
1491
            QRectF rect(x + soff, y - line.ascent, swidth, line.height());
 
1492
            if (outlinePen.style() != Qt::NoPen)
 
1493
                outlineRect = outlineRect.unite(rect);
 
1494
            p->save();
 
1495
            p->setClipRect(rect);
 
1496
        }
 
1497
 
 
1498
 
 
1499
        if (eng->block.docHandle() || selection) {
 
1500
            QTextCharFormat chf;
 
1501
            if (eng->block.docHandle())
 
1502
                chf = eng->format(&si).toCharFormat();
 
1503
            if (selection)
 
1504
                chf.merge(selection->format);
 
1505
 
 
1506
            setPenAndDrawBackground(p, pen, chf, QRectF(x, y - line.ascent, gf.width, line.height()));
 
1507
 
 
1508
            QTextCharFormat::VerticalAlignment valign = chf.verticalAlignment();
 
1509
            if (valign == QTextCharFormat::AlignSubScript)
 
1510
                itemBaseLine += (si.ascent + si.descent + 1) / 6;
 
1511
            else if (valign == QTextCharFormat::AlignSuperScript)
 
1512
                itemBaseLine -= (si.ascent + si.descent + 1) / 2;
 
1513
 
 
1514
            f = eng->font(si);
 
1515
        }
 
1516
 
 
1517
        gf.fontEngine = f.d->engineForScript(si.analysis.script);
 
1518
        gf.f = &f;
 
1519
        if (f.d->underline)
 
1520
            gf.flags |= QTextItem::Underline;
 
1521
        if (f.d->overline)
 
1522
            gf.flags |= QTextItem::Overline;
 
1523
        if (f.d->strikeOut)
 
1524
            gf.flags |= QTextItem::StrikeOut;
 
1525
        Q_ASSERT(gf.fontEngine);
 
1526
 
 
1527
        if (eng->underlinePositions) {
 
1528
            // can't have selections in this case
 
1529
            drawMenuText(p, x, itemBaseLine, si, gf, eng, start, gs);
 
1530
        } else {
 
1531
            p->drawTextItem(QPointF(x, itemBaseLine), gf);
 
1532
        }
 
1533
        if (selection)
 
1534
            p->restore();
 
1535
 
 
1536
        x += gf.width;
 
1537
    }
 
1538
 
 
1539
    if (outlineRect.isValid()) {
 
1540
        p->setPen(outlinePen);
 
1541
        p->drawRect(outlineRect);
 
1542
    }
 
1543
 
 
1544
    p->setPen(pen);
 
1545
}
 
1546
 
 
1547
/*!
 
1548
  \fn int QTextLine::cursorToX(int cursorPos, Edge edge) const
 
1549
 
 
1550
  \overload
 
1551
*/
 
1552
 
 
1553
 
 
1554
/*!
 
1555
  Converts the cursor position \a cursorPos to the corresponding x position
 
1556
  inside the line, taking account of the \a edge.
 
1557
 
 
1558
  If \a cursorPos is not a valid cursor position, the nearest valid
 
1559
  cursor position will be used instead, and cpos will be modified to
 
1560
  point to this valid cursor position.
 
1561
 
 
1562
  \sa xToCursor()
 
1563
*/
 
1564
qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
 
1565
{
 
1566
    if (!eng->layoutData)
 
1567
        eng->itemize();
 
1568
 
 
1569
    const QScriptLine &line = eng->lines[i];
 
1570
 
 
1571
    qreal x = line.x;
 
1572
    x += alignLine(eng, line);
 
1573
 
 
1574
    if (!i && !eng->layoutData->items.size()) {
 
1575
        *cursorPos = 0;
 
1576
        return x;
 
1577
    }
 
1578
 
 
1579
    int pos = *cursorPos;
 
1580
    int itm = eng->findItem(pos);
 
1581
    if (pos == line.from + (int)line.length) {
 
1582
        // end of line ensure we have the last item on the line
 
1583
        itm = eng->findItem(pos-1);
 
1584
    }
 
1585
 
 
1586
    const QScriptItem *si = &eng->layoutData->items[itm];
 
1587
    if (!si->num_glyphs)
 
1588
        eng->shape(itm);
 
1589
    pos -= si->position;
 
1590
 
 
1591
    QGlyphLayout *glyphs = eng->glyphs(si);
 
1592
    unsigned short *logClusters = eng->logClusters(si);
 
1593
 
 
1594
    int l = eng->length(itm);
 
1595
    if (pos > l)
 
1596
        pos = l;
 
1597
    if (pos < 0)
 
1598
        pos = 0;
 
1599
 
 
1600
    int glyph_pos = pos == l ? si->num_glyphs : logClusters[pos];
 
1601
    if (edge == Trailing) {
 
1602
        // trailing edge is leading edge of next cluster
 
1603
        while (glyph_pos < si->num_glyphs && !glyphs[glyph_pos].attributes.clusterStart)
 
1604
            glyph_pos++;
 
1605
    }
 
1606
 
 
1607
    bool reverse = eng->layoutData->items[itm].analysis.bidiLevel % 2;
 
1608
 
 
1609
    int lineEnd = line.from + line.length;
 
1610
    // don't draw trailing spaces or take them into the layout.
 
1611
    if (!(eng->option.flags() & QTextOption::IncludeTrailingSpaces)) {
 
1612
        const QCharAttributes *attributes = eng->attributes();
 
1613
        while (lineEnd > line.from && attributes[lineEnd-1].whiteSpace)
 
1614
            --lineEnd;
 
1615
    }
 
1616
 
 
1617
    // add the items left of the cursor
 
1618
 
 
1619
    int firstItem = eng->findItem(line.from);
 
1620
    int lastItem = eng->findItem(lineEnd - 1);
 
1621
    int nItems = lastItem-firstItem+1;
 
1622
 
 
1623
    QVarLengthArray<int> visualOrder(nItems);
 
1624
    QVarLengthArray<uchar> levels(nItems);
 
1625
    for (int i = 0; i < nItems; ++i)
 
1626
        levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
 
1627
    QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
 
1628
 
 
1629
    for (int i = 0; i < nItems; ++i) {
 
1630
        int item = visualOrder[i]+firstItem;
 
1631
        if (item == itm)
 
1632
            break;
 
1633
        QScriptItem &si = eng->layoutData->items[item];
 
1634
        if (!si.num_glyphs)
 
1635
            eng->shape(item);
 
1636
 
 
1637
        if (si.isTab) {
 
1638
            x = eng->nextTab(&si, x);
 
1639
            continue;
 
1640
        } else if (si.isObject) {
 
1641
            x += si.width;
 
1642
            continue;
 
1643
        }
 
1644
        int start = qMax(line.from, si.position);
 
1645
        int end = qMin(lineEnd, si.position + eng->length(item));
 
1646
 
 
1647
        logClusters = eng->logClusters(&si);
 
1648
 
 
1649
        int gs = logClusters[start-si.position];
 
1650
        int ge = (end == si.position + eng->length(item)) ? si.num_glyphs-1 : logClusters[end-si.position-1];
 
1651
 
 
1652
        QGlyphLayout *glyphs = eng->glyphs(&si);
 
1653
 
 
1654
        while (gs <= ge) {
 
1655
            x += glyphs[gs].advance.x() + qreal(glyphs[gs].space_18d6)/qreal(64);
 
1656
            ++gs;
 
1657
        }
 
1658
    }
 
1659
 
 
1660
    logClusters = eng->logClusters(si);
 
1661
    glyphs = eng->glyphs(si);
 
1662
    if (si->isTab) {
 
1663
        if(pos == l)
 
1664
            x = eng->nextTab(si, x);
 
1665
    } else if (si->isObject) {
 
1666
        if(pos == l)
 
1667
            x += si->width;
 
1668
    } else {
 
1669
        if (reverse) {
 
1670
            int end = qMin(lineEnd, si->position + l) - si->position;
 
1671
            int glyph_end = end == l ? si->num_glyphs : logClusters[end];
 
1672
            for (int i = glyph_end - 1; i >= glyph_pos; i--)
 
1673
                x += glyphs[i].advance.x() + qreal(glyphs[i].space_18d6)/qreal(64);
 
1674
        } else {
 
1675
            int start = qMax(line.from - si->position, 0);
 
1676
            int glyph_start = logClusters[start];
 
1677
            for (int i = glyph_start; i < glyph_pos; i++)
 
1678
                x += glyphs[i].advance.x() + qreal(glyphs[i].space_18d6)/qreal(64);
 
1679
        }
 
1680
    }
 
1681
 
 
1682
    *cursorPos = pos + si->position;
 
1683
    return x;
 
1684
}
 
1685
 
 
1686
/*!
 
1687
  Converts the x-coordinate \a x, to the nearest matching cursor
 
1688
  position, depending on the cursor position type, \a cpos.
 
1689
 
 
1690
  \sa cursorToX()
 
1691
*/
 
1692
int QTextLine::xToCursor(qreal x, CursorPosition cpos) const
 
1693
{
 
1694
    const QScriptLine &line = eng->lines[i];
 
1695
 
 
1696
    if (!eng->layoutData)
 
1697
        eng->itemize();
 
1698
 
 
1699
    int line_length = line.length;
 
1700
 
 
1701
    if (line_length > 0 && eng->layoutData->string.at(line.from + line_length - 1) == QChar::LineSeparator)
 
1702
        --line_length;
 
1703
 
 
1704
    // don't draw trailing spaces or take them into the layout.
 
1705
    const QCharAttributes *a = eng->attributes() + line.from;
 
1706
    while (line_length && a[line_length-1].whiteSpace)
 
1707
        --line_length;
 
1708
 
 
1709
    if (!line_length)
 
1710
        return line.from;
 
1711
 
 
1712
    int firstItem = eng->findItem(line.from);
 
1713
    int lastItem = eng->findItem(line.from + line_length - 1);
 
1714
    int nItems = lastItem-firstItem+1;
 
1715
 
 
1716
    x -= line.x;
 
1717
    x -= alignLine(eng, line);
 
1718
//     qDebug("xToCursor: x=%f, cpos=%d", x, cpos);
 
1719
 
 
1720
    QVarLengthArray<int> visualOrder(nItems);
 
1721
    QVarLengthArray<unsigned char> levels(nItems);
 
1722
    for (int i = 0; i < nItems; ++i)
 
1723
        levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
 
1724
    QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
 
1725
 
 
1726
    if (x <= 0) {
 
1727
        // left of first item
 
1728
        int item = visualOrder[0]+firstItem;
 
1729
        QScriptItem &si = eng->layoutData->items[item];
 
1730
        if (!si.num_glyphs)
 
1731
            eng->shape(item);
 
1732
        int pos = si.position;
 
1733
        if (si.analysis.bidiLevel % 2)
 
1734
            pos += eng->length(item);
 
1735
        pos = qMax(line.from, pos);
 
1736
        pos = qMin(line.from + line_length, pos);
 
1737
        return pos;
 
1738
    } else if (x < line.textWidth
 
1739
               || (line.justified && x < line.width)) {
 
1740
        // has to be in one of the runs
 
1741
        qreal pos = 0;
 
1742
 
 
1743
        for (int i = 0; i < nItems; ++i) {
 
1744
            int item = visualOrder[i]+firstItem;
 
1745
            QScriptItem &si = eng->layoutData->items[item];
 
1746
            if (!si.num_glyphs)
 
1747
                eng->shape(item);
 
1748
            int item_length = eng->length(item);
 
1749
//             qDebug("    item %d, visual %d x_remain=%f", i, item, x);
 
1750
 
 
1751
            int start = qMax(line.from - si.position, 0);
 
1752
            int end = qMin(line.from + line_length - si.position, item_length);
 
1753
 
 
1754
            unsigned short *logClusters = eng->logClusters(&si);
 
1755
 
 
1756
            int gs = logClusters[start];
 
1757
            int ge = (end == item_length ? si.num_glyphs : logClusters[end]) - 1;
 
1758
            QGlyphLayout *glyphs = eng->glyphs(&si);
 
1759
 
 
1760
            qreal item_width = 0;
 
1761
            if (si.isTab) {
 
1762
                item_width = eng->nextTab(&si, pos) - pos;
 
1763
            } else if (si.isObject) {
 
1764
                item_width = si.width;
 
1765
            } else {
 
1766
                int g = gs;
 
1767
                while (g <= ge) {
 
1768
                    item_width += glyphs[g].advance.x() + qreal(glyphs[g].space_18d6)/qreal(64);
 
1769
                    ++g;
 
1770
                }
 
1771
            }
 
1772
//             qDebug("      start=%d, end=%d, gs=%d, ge=%d item_width=%f", start, end, gs, ge, item_width);
 
1773
 
 
1774
            if (pos + item_width < x) {
 
1775
                pos += item_width;
 
1776
                continue;
 
1777
            }
 
1778
//             qDebug("      inside run");
 
1779
            if (si.isTab || si.isObject) {
 
1780
                if (cpos == QTextLine::CursorOnCharacter)
 
1781
                    return si.position;
 
1782
                bool left_half = (x - pos) < item_width/2.;
 
1783
 
 
1784
                if (bool(si.analysis.bidiLevel % 2) ^ left_half)
 
1785
                    return si.position;
 
1786
                return si.position + 1;
 
1787
            }
 
1788
 
 
1789
            int glyph_pos = -1;
 
1790
            // has to be inside run
 
1791
            if (cpos == QTextLine::CursorOnCharacter) {
 
1792
                if (si.analysis.bidiLevel % 2) {
 
1793
                    pos += item_width;
 
1794
                    int last_glyph = gs;
 
1795
                    while (gs <= ge) {
 
1796
                        if (glyphs[gs].attributes.clusterStart && pos < x) {
 
1797
                            glyph_pos = last_glyph;
 
1798
                            break;
 
1799
                        }
 
1800
                        pos -= glyphs[gs].advance.x() + qreal(glyphs[gs].space_18d6)/qreal(64);
 
1801
                        ++gs;
 
1802
                    }
 
1803
                } else {
 
1804
                    glyph_pos = gs;
 
1805
                    while (gs <= ge) {
 
1806
                        if (glyphs[gs].attributes.clusterStart) {
 
1807
                            if (pos > x)
 
1808
                                break;
 
1809
                            glyph_pos = gs;
 
1810
                        }
 
1811
                        pos += glyphs[gs].advance.x() + qreal(glyphs[gs].space_18d6)/qreal(64);
 
1812
                        ++gs;
 
1813
                    }
 
1814
                }
 
1815
            } else {
 
1816
                qreal dist = qreal(INT_MAX/256);
 
1817
                if (si.analysis.bidiLevel % 2) {
 
1818
                    pos += item_width;
 
1819
                    while (gs <= ge) {
 
1820
                        if (glyphs[gs].attributes.clusterStart && qAbs(x-pos) < dist) {
 
1821
                            glyph_pos = gs;
 
1822
                            dist = qAbs(x-pos);
 
1823
                        }
 
1824
                        pos -= glyphs[gs].advance.x() + qreal(glyphs[gs].space_18d6)/qreal(64);
 
1825
                        ++gs;
 
1826
                    }
 
1827
                } else {
 
1828
                    while (gs <= ge) {
 
1829
                        if (glyphs[gs].attributes.clusterStart && qAbs(x-pos) < dist) {
 
1830
                            glyph_pos = gs;
 
1831
                            dist = qAbs(x-pos);
 
1832
                        }
 
1833
                        pos += glyphs[gs].advance.x() + qreal(glyphs[gs].space_18d6)/qreal(64);
 
1834
                        ++gs;
 
1835
                    }
 
1836
                }
 
1837
                if (qAbs(x-pos) < dist)
 
1838
                    return si.position + end;
 
1839
            }
 
1840
            Q_ASSERT(glyph_pos != -1);
 
1841
            int j;
 
1842
            for (j = 0; j < eng->length(item); ++j)
 
1843
                if (logClusters[j] == glyph_pos)
 
1844
                    break;
 
1845
//             qDebug("at pos %d (in run: %d)", si.position + j, j);
 
1846
            return si.position + j;
 
1847
        }
 
1848
    }
 
1849
    // right of last item
 
1850
    int item = visualOrder[nItems-1]+firstItem;
 
1851
    QScriptItem &si = eng->layoutData->items[item];
 
1852
    if (!si.num_glyphs)
 
1853
        eng->shape(item);
 
1854
    int pos = si.position;
 
1855
    if (!(si.analysis.bidiLevel % 2))
 
1856
        pos += eng->length(item);
 
1857
    pos = qMax(line.from, pos);
 
1858
    pos = qMin(line.from + line_length, pos);
 
1859
    return pos;
 
1860
}