93f71d3dbb6ac4dd41964a63b84a41962508f070
[profile/ivi/qtbase.git] / src / gui / text / qtextlayout.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtGui module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qtextlayout.h"
43 #include "qtextengine_p.h"
44
45 #include <qfont.h>
46 #include <qapplication.h>
47 #include <qpainter.h>
48 #include <qvarlengtharray.h>
49 #include <qtextformat.h>
50 #include <qabstracttextdocumentlayout.h>
51 #include "qtextdocument_p.h"
52 #include "qtextformat_p.h"
53 #include "qstyleoption.h"
54 #include "qpainterpath.h"
55 #include "qglyphs.h"
56 #include "qglyphs_p.h"
57 #include "qrawfont.h"
58 #include "qrawfont_p.h"
59 #include <limits.h>
60
61 #include <qdebug.h>
62
63 #include "qfontengine_p.h"
64
65 #if !defined(QT_NO_FREETYPE)
66 #  include "qfontengine_ft_p.h"
67 #endif
68
69 QT_BEGIN_NAMESPACE
70
71 #define ObjectSelectionBrush (QTextFormat::ForegroundBrush + 1)
72 #define SuppressText 0x5012
73 #define SuppressBackground 0x513
74
75 static QFixed alignLine(QTextEngine *eng, const QScriptLine &line)
76 {
77     QFixed x = 0;
78     eng->justify(line);
79     // if width is QFIXED_MAX that means we used setNumColumns() and that implicitly makes this line left aligned.
80     if (!line.justified && line.width != QFIXED_MAX) {
81         int align = eng->option.alignment();
82         if (align & Qt::AlignJustify && eng->isRightToLeft())
83             align = Qt::AlignRight;
84         if (align & Qt::AlignRight)
85             x = line.width - (line.textAdvance + eng->leadingSpaceWidth(line));
86         else if (align & Qt::AlignHCenter)
87             x = (line.width - line.textAdvance)/2;
88     }
89     return x;
90 }
91
92 /*!
93     \class QTextLayout::FormatRange
94     \reentrant
95
96     \brief The QTextLayout::FormatRange structure is used to apply extra formatting information
97     for a specified area in the text layout's content.
98
99     \sa QTextLayout::setAdditionalFormats(), QTextLayout::draw()
100 */
101
102 /*!
103     \variable QTextLayout::FormatRange::start
104     Specifies the beginning of the format range within the text layout's text.
105 */
106
107 /*!
108     \variable QTextLayout::FormatRange::length
109     Specifies the numer of characters the format range spans.
110 */
111
112 /*!
113     \variable QTextLayout::FormatRange::format
114     Specifies the format to apply.
115 */
116
117 /*!
118     \class QTextInlineObject
119     \reentrant
120
121     \brief The QTextInlineObject class represents an inline object in
122     a QTextLayout.
123
124     \ingroup richtext-processing
125
126     This class is only used if the text layout is used to lay out
127     parts of a QTextDocument.
128
129     The inline object has various attributes that can be set, for
130     example using, setWidth(), setAscent(), and setDescent(). The
131     rectangle it occupies is given by rect(), and its direction by
132     isRightToLeft(). Its position in the text layout is given by at(),
133     and its format is given by format().
134 */
135
136 /*!
137     \fn QTextInlineObject::QTextInlineObject(int i, QTextEngine *e)
138
139     Creates a new inline object for the item at position \a i in the
140     text engine \a e.
141 */
142
143 /*!
144     \fn QTextInlineObject::QTextInlineObject()
145
146     \internal
147 */
148
149 /*!
150     \fn bool QTextInlineObject::isValid() const
151
152     Returns true if this inline object is valid; otherwise returns
153     false.
154 */
155
156 /*!
157     Returns the inline object's rectangle.
158
159     \sa ascent(), descent(), width()
160 */
161 QRectF QTextInlineObject::rect() const
162 {
163     QScriptItem& si = eng->layoutData->items[itm];
164     return QRectF(0, -si.ascent.toReal(), si.width.toReal(), si.height().toReal());
165 }
166
167 /*!
168     Returns the inline object's width.
169
170     \sa ascent(), descent(), rect()
171 */
172 qreal QTextInlineObject::width() const
173 {
174     return eng->layoutData->items[itm].width.toReal();
175 }
176
177 /*!
178     Returns the inline object's ascent.
179
180     \sa descent(), width(), rect()
181 */
182 qreal QTextInlineObject::ascent() const
183 {
184     return eng->layoutData->items[itm].ascent.toReal();
185 }
186
187 /*!
188     Returns the inline object's descent.
189
190     \sa ascent(), width(), rect()
191 */
192 qreal QTextInlineObject::descent() const
193 {
194     return eng->layoutData->items[itm].descent.toReal();
195 }
196
197 /*!
198     Returns the inline object's total height. This is equal to
199     ascent() + descent() + 1.
200
201     \sa ascent(), descent(), width(), rect()
202 */
203 qreal QTextInlineObject::height() const
204 {
205     return eng->layoutData->items[itm].height().toReal();
206 }
207
208 /*!
209     Sets the inline object's width to \a w.
210
211     \sa width(), ascent(), descent(), rect()
212 */
213 void QTextInlineObject::setWidth(qreal w)
214 {
215     eng->layoutData->items[itm].width = QFixed::fromReal(w);
216 }
217
218 /*!
219     Sets the inline object's ascent to \a a.
220
221     \sa ascent(), setDescent(), width(), rect()
222 */
223 void QTextInlineObject::setAscent(qreal a)
224 {
225     eng->layoutData->items[itm].ascent = QFixed::fromReal(a);
226 }
227
228 /*!
229     Sets the inline object's decent to \a d.
230
231     \sa descent(), setAscent(), width(), rect()
232 */
233 void QTextInlineObject::setDescent(qreal d)
234 {
235     eng->layoutData->items[itm].descent = QFixed::fromReal(d);
236 }
237
238 /*!
239     The position of the inline object within the text layout.
240 */
241 int QTextInlineObject::textPosition() const
242 {
243     return eng->layoutData->items[itm].position;
244 }
245
246 /*!
247     Returns an integer describing the format of the inline object
248     within the text layout.
249 */
250 int QTextInlineObject::formatIndex() const
251 {
252     return eng->formatIndex(&eng->layoutData->items[itm]);
253 }
254
255 /*!
256     Returns format of the inline object within the text layout.
257 */
258 QTextFormat QTextInlineObject::format() const
259 {
260     if (!eng->block.docHandle())
261         return QTextFormat();
262     return eng->formats()->format(eng->formatIndex(&eng->layoutData->items[itm]));
263 }
264
265 /*!
266     Returns if the object should be laid out right-to-left or left-to-right.
267 */
268 Qt::LayoutDirection QTextInlineObject::textDirection() const
269 {
270     return (eng->layoutData->items[itm].analysis.bidiLevel % 2 ? Qt::RightToLeft : Qt::LeftToRight);
271 }
272
273 /*!
274     \class QTextLayout
275     \reentrant
276
277     \brief The QTextLayout class is used to lay out and render text.
278
279     \ingroup richtext-processing
280
281     It offers many features expected from a modern text layout
282     engine, including Unicode compliant rendering, line breaking and
283     handling of cursor positioning. It can also produce and render
284     device independent layout, something that is important for WYSIWYG
285     applications.
286
287     The class has a rather low level API and unless you intend to
288     implement your own text rendering for some specialized widget, you
289     probably won't need to use it directly.
290
291     QTextLayout can be used with both plain and rich text.
292
293     QTextLayout can be used to create a sequence of QTextLine
294     instances with given widths and can position them independently
295     on the screen. Once the layout is done, these lines can be drawn
296     on a paint device.
297
298     The text to be laid out can be provided in the constructor or set with
299     setText().
300
301     The layout can be seen as a sequence of QTextLine objects; use createLine()
302     to create a QTextLine instance, and lineAt() or lineForTextPosition() to retrieve
303     created lines.
304
305     Here is a code snippet that demonstrates the layout phase:
306     \snippet doc/src/snippets/code/src_gui_text_qtextlayout.cpp 0
307
308     The text can then be rendered by calling the layout's draw() function:
309     \snippet doc/src/snippets/code/src_gui_text_qtextlayout.cpp 1
310
311     For a given position in the text you can find a valid cursor position with
312     isValidCursorPosition(), nextCursorPosition(), and previousCursorPosition().
313
314     The QTextLayout itself can be positioned with setPosition(); it has a
315     boundingRect(), and a minimumWidth() and a maximumWidth().
316
317     \sa QStaticText
318 */
319
320 /*!
321     \enum QTextLayout::CursorMode
322
323     \value SkipCharacters
324     \value SkipWords
325 */
326
327 /*!
328     \fn QTextEngine *QTextLayout::engine() const
329     \internal
330
331     Returns the text engine used to render the text layout.
332 */
333
334 /*!
335     Constructs an empty text layout.
336
337     \sa setText()
338 */
339 QTextLayout::QTextLayout()
340 { d = new QTextEngine(); }
341
342 /*!
343     Constructs a text layout to lay out the given \a text.
344 */
345 QTextLayout::QTextLayout(const QString& text)
346 {
347     d = new QTextEngine();
348     d->text = text;
349 }
350
351 /*!
352     Constructs a text layout to lay out the given \a text with the specified
353     \a font.
354
355     All the metric and layout calculations will be done in terms of
356     the paint device, \a paintdevice. If \a paintdevice is 0 the
357     calculations will be done in screen metrics.
358 */
359 QTextLayout::QTextLayout(const QString& text, const QFont &font, QPaintDevice *paintdevice)
360 {
361     QFont f(font);
362     if (paintdevice)
363         f = QFont(font, paintdevice);
364     d = new QTextEngine((text.isNull() ? (const QString&)QString::fromLatin1("") : text), f.d.data());
365 }
366
367 /*!
368     \internal
369     Constructs a text layout to lay out the given \a block.
370 */
371 QTextLayout::QTextLayout(const QTextBlock &block)
372 {
373     d = new QTextEngine();
374     d->block = block;
375 }
376
377 /*!
378     Destructs the layout.
379 */
380 QTextLayout::~QTextLayout()
381 {
382     if (!d->stackEngine)
383         delete d;
384 }
385
386 /*!
387     Sets the layout's font to the given \a font. The layout is
388     invalidated and must be laid out again.
389
390     \sa font()
391 */
392 void QTextLayout::setFont(const QFont &font)
393 {
394     d->fnt = font;
395     d->feCache.reset();
396 }
397
398 /*!
399     Returns the current font that is used for the layout, or a default
400     font if none is set.
401
402     \sa setFont()
403 */
404 QFont QTextLayout::font() const
405 {
406     return d->font();
407 }
408
409 /*!
410     Sets the layout's text to the given \a string. The layout is
411     invalidated and must be laid out again.
412
413     Notice that when using this QTextLayout as part of a QTextDocument this
414     method will have no effect.
415
416     \sa text()
417 */
418 void QTextLayout::setText(const QString& string)
419 {
420     d->invalidate();
421     d->clearLineData();
422     d->text = string;
423 }
424
425 /*!
426     Returns the layout's text.
427
428     \sa setText()
429 */
430 QString QTextLayout::text() const
431 {
432     return d->text;
433 }
434
435 /*!
436     Sets the text option structure that controls the layout process to the
437     given \a option.
438
439     \sa textOption()
440 */
441 void QTextLayout::setTextOption(const QTextOption &option)
442 {
443     d->option = option;
444 }
445
446 /*!
447     Returns the current text option used to control the layout process.
448
449     \sa setTextOption()
450 */
451 QTextOption QTextLayout::textOption() const
452 {
453     return d->option;
454 }
455
456 /*!
457     Sets the \a position and \a text of the area in the layout that is
458     processed before editing occurs.
459
460     \sa preeditAreaPosition(), preeditAreaText()
461 */
462 void QTextLayout::setPreeditArea(int position, const QString &text)
463 {
464     if (text.isEmpty()) {
465         if (!d->specialData)
466             return;
467         if (d->specialData->addFormats.isEmpty()) {
468             delete d->specialData;
469             d->specialData = 0;
470         } else {
471             d->specialData->preeditText = QString();
472             d->specialData->preeditPosition = -1;
473         }
474     } else {
475         if (!d->specialData)
476             d->specialData = new QTextEngine::SpecialData;
477         d->specialData->preeditPosition = position;
478         d->specialData->preeditText = text;
479     }
480     d->invalidate();
481     d->clearLineData();
482     if (d->block.docHandle())
483         d->block.docHandle()->documentChange(d->block.position(), d->block.length());
484 }
485
486 /*!
487     Returns the position of the area in the text layout that will be
488     processed before editing occurs.
489
490     \sa preeditAreaText()
491 */
492 int QTextLayout::preeditAreaPosition() const
493 {
494     return d->specialData ? d->specialData->preeditPosition : -1;
495 }
496
497 /*!
498     Returns the text that is inserted in the layout before editing occurs.
499
500     \sa preeditAreaPosition()
501 */
502 QString QTextLayout::preeditAreaText() const
503 {
504     return d->specialData ? d->specialData->preeditText : QString();
505 }
506
507
508 /*!
509     Sets the additional formats supported by the text layout to \a formatList.
510
511     \sa additionalFormats(), clearAdditionalFormats()
512 */
513 void QTextLayout::setAdditionalFormats(const QList<FormatRange> &formatList)
514 {
515     if (formatList.isEmpty()) {
516         if (!d->specialData)
517             return;
518         if (d->specialData->preeditText.isEmpty()) {
519             delete d->specialData;
520             d->specialData = 0;
521         } else {
522             d->specialData->addFormats = formatList;
523             d->specialData->addFormatIndices.clear();
524         }
525     } else {
526         if (!d->specialData) {
527             d->specialData = new QTextEngine::SpecialData;
528             d->specialData->preeditPosition = -1;
529         }
530         d->specialData->addFormats = formatList;
531         d->indexAdditionalFormats();
532     }
533     if (d->block.docHandle())
534         d->block.docHandle()->documentChange(d->block.position(), d->block.length());
535     d->feCache.reset();
536 }
537
538 /*!
539     Returns the list of additional formats supported by the text layout.
540
541     \sa setAdditionalFormats(), clearAdditionalFormats()
542 */
543 QList<QTextLayout::FormatRange> QTextLayout::additionalFormats() const
544 {
545     QList<FormatRange> formats;
546     if (!d->specialData)
547         return formats;
548
549     formats = d->specialData->addFormats;
550
551     if (d->specialData->addFormatIndices.isEmpty())
552         return formats;
553
554     const QTextFormatCollection *collection = d->formats();
555
556     for (int i = 0; i < d->specialData->addFormatIndices.count(); ++i)
557         formats[i].format = collection->charFormat(d->specialData->addFormatIndices.at(i));
558
559     return formats;
560 }
561
562 /*!
563     Clears the list of additional formats supported by the text layout.
564
565     \sa additionalFormats(), setAdditionalFormats()
566 */
567 void QTextLayout::clearAdditionalFormats()
568 {
569     setAdditionalFormats(QList<FormatRange>());
570 }
571
572 /*!
573     Enables caching of the complete layout information if \a enable is
574     true; otherwise disables layout caching. Usually
575     QTextLayout throws most of the layouting information away after a
576     call to endLayout() to reduce memory consumption. If you however
577     want to draw the laid out text directly afterwards enabling caching
578     might speed up drawing significantly.
579
580     \sa cacheEnabled()
581 */
582 void QTextLayout::setCacheEnabled(bool enable)
583 {
584     d->cacheGlyphs = enable;
585 }
586
587 /*!
588     Returns true if the complete layout information is cached; otherwise
589     returns false.
590
591     \sa setCacheEnabled()
592 */
593 bool QTextLayout::cacheEnabled() const
594 {
595     return d->cacheGlyphs;
596 }
597
598 /*!
599     Begins the layout process.
600
601     \sa endLayout()
602 */
603 void QTextLayout::beginLayout()
604 {
605 #ifndef QT_NO_DEBUG
606     if (d->layoutData && d->layoutData->layoutState == QTextEngine::InLayout) {
607         qWarning("QTextLayout::beginLayout: Called while already doing layout");
608         return;
609     }
610 #endif
611     d->invalidate();
612     d->clearLineData();
613     d->itemize();
614     d->layoutData->layoutState = QTextEngine::InLayout;
615 }
616
617 /*!
618     Ends the layout process.
619
620     \sa beginLayout()
621 */
622 void QTextLayout::endLayout()
623 {
624 #ifndef QT_NO_DEBUG
625     if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) {
626         qWarning("QTextLayout::endLayout: Called without beginLayout()");
627         return;
628     }
629 #endif
630     int l = d->lines.size();
631     if (l && d->lines.at(l-1).length < 0) {
632         QTextLine(l-1, d).setNumColumns(INT_MAX);
633     }
634     d->layoutData->layoutState = QTextEngine::LayoutEmpty;
635     if (!d->cacheGlyphs)
636         d->freeMemory();
637 }
638
639 /*!
640     \since 4.4
641
642     Clears the line information in the layout. After having called
643     this function, lineCount() returns 0.
644 */
645 void QTextLayout::clearLayout()
646 {
647     d->clearLineData();
648 }
649
650 /*!
651     Returns the next valid cursor position after \a oldPos that
652     respects the given cursor \a mode.
653     Returns value of \a oldPos, if \a oldPos is not a valid cursor position.
654
655     \sa isValidCursorPosition(), previousCursorPosition()
656 */
657 int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const
658 {
659     const HB_CharAttributes *attributes = d->attributes();
660     int len = d->block.isValid() ? d->block.length() - 1
661                                  : d->layoutData->string.length();
662     Q_ASSERT(len <= d->layoutData->string.length());
663     if (!attributes || oldPos < 0 || oldPos >= len)
664         return oldPos;
665
666     if (mode == SkipCharacters) {
667         oldPos++;
668         while (oldPos < len && !attributes[oldPos].charStop)
669             oldPos++;
670     } else {
671         if (oldPos < len && d->atWordSeparator(oldPos)) {
672             oldPos++;
673             while (oldPos < len && d->atWordSeparator(oldPos))
674                 oldPos++;
675         } else {
676             while (oldPos < len && !d->atSpace(oldPos) && !d->atWordSeparator(oldPos))
677                 oldPos++;
678         }
679         while (oldPos < len && d->atSpace(oldPos))
680             oldPos++;
681     }
682
683     return oldPos;
684 }
685
686 /*!
687     Returns the first valid cursor position before \a oldPos that
688     respects the given cursor \a mode.
689     Returns value of \a oldPos, if \a oldPos is not a valid cursor position.
690
691     \sa isValidCursorPosition(), nextCursorPosition()
692 */
693 int QTextLayout::previousCursorPosition(int oldPos, CursorMode mode) const
694 {
695     const HB_CharAttributes *attributes = d->attributes();
696     if (!attributes || oldPos <= 0 || oldPos > d->layoutData->string.length())
697         return oldPos;
698
699     if (mode == SkipCharacters) {
700         oldPos--;
701         while (oldPos && !attributes[oldPos].charStop)
702             oldPos--;
703     } else {
704         while (oldPos && d->atSpace(oldPos-1))
705             oldPos--;
706
707         if (oldPos && d->atWordSeparator(oldPos-1)) {
708             oldPos--;
709             while (oldPos && d->atWordSeparator(oldPos-1))
710                 oldPos--;
711         } else {
712             while (oldPos && !d->atSpace(oldPos-1) && !d->atWordSeparator(oldPos-1))
713                 oldPos--;
714         }
715     }
716
717     return oldPos;
718 }
719
720 /*!
721     Returns true if position \a pos is a valid cursor position.
722
723     In a Unicode context some positions in the text are not valid
724     cursor positions, because the position is inside a Unicode
725     surrogate or a grapheme cluster.
726
727     A grapheme cluster is a sequence of two or more Unicode characters
728     that form one indivisible entity on the screen. For example the
729     latin character `\Auml' can be represented in Unicode by two
730     characters, `A' (0x41), and the combining diaresis (0x308). A text
731     cursor can only validly be positioned before or after these two
732     characters, never between them since that wouldn't make sense. In
733     indic languages every syllable forms a grapheme cluster.
734 */
735 bool QTextLayout::isValidCursorPosition(int pos) const
736 {
737     const HB_CharAttributes *attributes = d->attributes();
738     if (!attributes || pos < 0 || pos > (int)d->layoutData->string.length())
739         return false;
740     return attributes[pos].charStop;
741 }
742
743 /*!
744     Returns a new text line to be laid out if there is text to be
745     inserted into the layout; otherwise returns an invalid text line.
746
747     The text layout creates a new line object that starts after the
748     last line in the layout, or at the beginning if the layout is empty.
749     The layout maintains an internal cursor, and each line is filled
750     with text from the cursor position onwards when the
751     QTextLine::setLineWidth() function is called.
752
753     Once QTextLine::setLineWidth() is called, a new line can be created and
754     filled with text. Repeating this process will lay out the whole block
755     of text contained in the QTextLayout. If there is no text left to be
756     inserted into the layout, the QTextLine returned will not be valid
757     (isValid() will return false).
758 */
759 QTextLine QTextLayout::createLine()
760 {
761 #ifndef QT_NO_DEBUG
762     if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) {
763         qWarning("QTextLayout::createLine: Called without layouting");
764         return QTextLine();
765     }
766 #endif
767     if (d->layoutData->layoutState == QTextEngine::LayoutFailed)
768         return QTextLine();
769
770     int l = d->lines.size();
771     if (l && d->lines.at(l-1).length < 0) {
772         QTextLine(l-1, d).setNumColumns(INT_MAX);
773     }
774     int from = l > 0 ? d->lines.at(l-1).from + d->lines.at(l-1).length : 0;
775     int strlen = d->layoutData->string.length();
776     if (l && from >= strlen) {
777         if (!d->lines.at(l-1).length || d->layoutData->string.at(strlen - 1) != QChar::LineSeparator)
778             return QTextLine();
779     }
780
781     QScriptLine line;
782     line.from = from;
783     line.length = -1;
784     line.justified = false;
785     line.gridfitted = false;
786
787     d->lines.append(line);
788     return QTextLine(l, d);
789 }
790
791 /*!
792     Returns the number of lines in this text layout.
793
794     \sa lineAt()
795 */
796 int QTextLayout::lineCount() const
797 {
798     return d->lines.size();
799 }
800
801 /*!
802     Returns the \a{i}-th line of text in this text layout.
803
804     \sa lineCount(), lineForTextPosition()
805 */
806 QTextLine QTextLayout::lineAt(int i) const
807 {
808     return QTextLine(i, d);
809 }
810
811 /*!
812     Returns the line that contains the cursor position specified by \a pos.
813
814     \sa isValidCursorPosition(), lineAt()
815 */
816 QTextLine QTextLayout::lineForTextPosition(int pos) const
817 {
818     for (int i = 0; i < d->lines.size(); ++i) {
819         const QScriptLine& line = d->lines[i];
820         if (line.from + (int)line.length > pos)
821             return QTextLine(i, d);
822     }
823     if (!d->layoutData)
824         d->itemize();
825     if (pos == d->layoutData->string.length() && d->lines.size())
826         return QTextLine(d->lines.size()-1, d);
827     return QTextLine();
828 }
829
830 /*!
831     \since 4.2
832
833     The global position of the layout. This is independent of the
834     bounding rectangle and of the layout process.
835
836     \sa setPosition()
837 */
838 QPointF QTextLayout::position() const
839 {
840     return d->position;
841 }
842
843 /*!
844     Moves the text layout to point \a p.
845
846     \sa position()
847 */
848 void QTextLayout::setPosition(const QPointF &p)
849 {
850     d->position = p;
851 }
852
853 /*!
854     The smallest rectangle that contains all the lines in the layout.
855 */
856 QRectF QTextLayout::boundingRect() const
857 {
858     if (d->lines.isEmpty())
859         return QRectF();
860
861     QFixed xmax, ymax;
862     QFixed xmin = d->lines.at(0).x;
863     QFixed ymin = d->lines.at(0).y;
864
865     for (int i = 0; i < d->lines.size(); ++i) {
866         const QScriptLine &si = d->lines[i];
867         xmin = qMin(xmin, si.x);
868         ymin = qMin(ymin, si.y);
869         QFixed lineWidth = si.width < QFIXED_MAX ? qMax(si.width, si.textWidth) : si.textWidth;
870         xmax = qMax(xmax, si.x+lineWidth);
871         // ### shouldn't the ascent be used in ymin???
872         ymax = qMax(ymax, si.y+si.height());
873     }
874     return QRectF(xmin.toReal(), ymin.toReal(), (xmax-xmin).toReal(), (ymax-ymin).toReal());
875 }
876
877 /*!
878     The minimum width the layout needs. This is the width of the
879     layout's smallest non-breakable substring.
880
881     \warning This function only returns a valid value after the layout
882     has been done.
883
884     \sa maximumWidth()
885 */
886 qreal QTextLayout::minimumWidth() const
887 {
888     return d->minWidth.toReal();
889 }
890
891 /*!
892     The maximum width the layout could expand to; this is essentially
893     the width of the entire text.
894
895     \warning This function only returns a valid value after the layout
896     has been done.
897
898     \sa minimumWidth()
899 */
900 qreal QTextLayout::maximumWidth() const
901 {
902     return d->maxWidth.toReal();
903 }
904
905
906 /*!
907     \internal
908 */
909 void QTextLayout::setFlags(int flags)
910 {
911     if (flags & Qt::TextJustificationForced) {
912         d->option.setAlignment(Qt::AlignJustify);
913         d->forceJustification = true;
914     }
915
916     if (flags & (Qt::TextForceLeftToRight|Qt::TextForceRightToLeft)) {
917         d->ignoreBidi = true;
918         d->option.setTextDirection((flags & Qt::TextForceLeftToRight) ? Qt::LeftToRight : Qt::RightToLeft);
919     }
920 }
921
922 struct QTextLineItemIterator
923 {
924     QTextLineItemIterator(QTextEngine *eng, int lineNum, const QPointF &pos = QPointF(),
925                           const QTextLayout::FormatRange *_selection = 0);
926
927     inline bool atEnd() const { return logicalItem >= nItems - 1; }
928     QScriptItem &next();
929
930     bool getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const;
931     inline bool isOutsideSelection() const {
932         QFixed tmp1, tmp2;
933         return !getSelectionBounds(&tmp1, &tmp2);
934     }
935
936     QTextEngine *eng;
937
938     QFixed x;
939     QFixed pos_x;
940     const QScriptLine &line;
941     QScriptItem *si;
942
943     int lineEnd;
944     int firstItem;
945     int lastItem;
946     int nItems;
947     int logicalItem;
948     int item;
949     int itemLength;
950
951     int glyphsStart;
952     int glyphsEnd;
953     int itemStart;
954     int itemEnd;
955
956     QFixed itemWidth;
957
958     QVarLengthArray<int> visualOrder;
959     QVarLengthArray<uchar> levels;
960
961     const QTextLayout::FormatRange *selection;
962 };
963
964 QTextLineItemIterator::QTextLineItemIterator(QTextEngine *_eng, int lineNum, const QPointF &pos,
965                                              const QTextLayout::FormatRange *_selection)
966     : eng(_eng),
967       line(eng->lines[lineNum]),
968       si(0),
969       lineEnd(line.from + line.length),
970       firstItem(eng->findItem(line.from)),
971       lastItem(eng->findItem(lineEnd - 1)),
972       nItems((firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0),
973       logicalItem(-1),
974       item(-1),
975       visualOrder(nItems),
976       levels(nItems),
977       selection(_selection)
978 {
979     pos_x = x = QFixed::fromReal(pos.x());
980
981     x += line.x;
982
983     x += alignLine(eng, line);
984
985     for (int i = 0; i < nItems; ++i)
986         levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
987     QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
988
989     eng->shapeLine(line);
990 }
991
992 QScriptItem &QTextLineItemIterator::next()
993 {
994     x += itemWidth;
995
996     ++logicalItem;
997     item = visualOrder[logicalItem] + firstItem;
998     itemLength = eng->length(item);
999     si = &eng->layoutData->items[item];
1000     if (!si->num_glyphs)
1001         eng->shape(item);
1002
1003     if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
1004         itemWidth = si->width;
1005         return *si;
1006     }
1007
1008     unsigned short *logClusters = eng->logClusters(si);
1009     QGlyphLayout glyphs = eng->shapedGlyphs(si);
1010
1011     itemStart = qMax(line.from, si->position);
1012     glyphsStart = logClusters[itemStart - si->position];
1013     if (lineEnd < si->position + itemLength) {
1014         itemEnd = lineEnd;
1015         glyphsEnd = logClusters[itemEnd-si->position];
1016     } else {
1017         itemEnd = si->position + itemLength;
1018         glyphsEnd = si->num_glyphs;
1019     }
1020     // show soft-hyphen at line-break
1021     if (si->position + itemLength >= lineEnd
1022         && eng->layoutData->string.at(lineEnd - 1) == 0x00ad)
1023         glyphs.attributes[glyphsEnd - 1].dontPrint = false;
1024
1025     itemWidth = 0;
1026     for (int g = glyphsStart; g < glyphsEnd; ++g)
1027         itemWidth += glyphs.effectiveAdvance(g);
1028
1029     return *si;
1030 }
1031
1032 static QFixed offsetInLigature(const unsigned short *logClusters,
1033                                const QGlyphLayout &glyphs,
1034                                int pos, int max, int glyph_pos)
1035 {
1036     int offsetInCluster = 0;
1037     for (int i = pos - 1; i >= 0; i--) {
1038         if (logClusters[i] == glyph_pos)
1039             offsetInCluster++;
1040         else
1041             break;
1042     }
1043
1044     // in the case that the offset is inside a (multi-character) glyph,
1045     // interpolate the position.
1046     if (offsetInCluster > 0) {
1047         int clusterLength = 0;
1048         for (int i = pos - offsetInCluster; i < max; i++) {
1049             if (logClusters[i] == glyph_pos)
1050                 clusterLength++;
1051             else
1052                 break;
1053         }
1054         if (clusterLength)
1055             return glyphs.advances_x[glyph_pos] * offsetInCluster / clusterLength;
1056     }
1057
1058     return 0;
1059 }
1060
1061 bool QTextLineItemIterator::getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const
1062 {
1063     *selectionX = *selectionWidth = 0;
1064
1065     if (!selection)
1066         return false;
1067
1068     if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
1069         if (si->position >= selection->start + selection->length
1070             || si->position + itemLength <= selection->start)
1071             return false;
1072
1073         *selectionX = x;
1074         *selectionWidth = itemWidth;
1075     } else {
1076         unsigned short *logClusters = eng->logClusters(si);
1077         QGlyphLayout glyphs = eng->shapedGlyphs(si);
1078
1079         int from = qMax(itemStart, selection->start) - si->position;
1080         int to = qMin(itemEnd, selection->start + selection->length) - si->position;
1081         if (from >= to)
1082             return false;
1083
1084         int start_glyph = logClusters[from];
1085         int end_glyph = (to == eng->length(item)) ? si->num_glyphs : logClusters[to];
1086         QFixed soff;
1087         QFixed swidth;
1088         if (si->analysis.bidiLevel %2) {
1089             for (int g = glyphsEnd - 1; g >= end_glyph; --g)
1090                 soff += glyphs.effectiveAdvance(g);
1091             for (int g = end_glyph - 1; g >= start_glyph; --g)
1092                 swidth += glyphs.effectiveAdvance(g);
1093         } else {
1094             for (int g = glyphsStart; g < start_glyph; ++g)
1095                 soff += glyphs.effectiveAdvance(g);
1096             for (int g = start_glyph; g < end_glyph; ++g)
1097                 swidth += glyphs.effectiveAdvance(g);
1098         }
1099
1100         // If the starting character is in the middle of a ligature,
1101         // selection should only contain the right part of that ligature
1102         // glyph, so we need to get the width of the left part here and
1103         // add it to *selectionX
1104         QFixed leftOffsetInLigature = offsetInLigature(logClusters, glyphs, from,
1105                                                        to, start_glyph);
1106         *selectionX = x + soff + leftOffsetInLigature;
1107         *selectionWidth = swidth - leftOffsetInLigature;
1108         // If the ending character is also part of a ligature, swidth does
1109         // not contain that part yet, we also need to find out the width of
1110         // that left part
1111         *selectionWidth += offsetInLigature(logClusters, glyphs, to,
1112                                             eng->length(item), end_glyph);
1113     }
1114     return true;
1115 }
1116
1117 static void addSelectedRegionsToPath(QTextEngine *eng, int lineNumber, const QPointF &pos, QTextLayout::FormatRange *selection,
1118                                      QPainterPath *region, QRectF boundingRect)
1119 {
1120     const QScriptLine &line = eng->lines[lineNumber];
1121
1122     QTextLineItemIterator iterator(eng, lineNumber, pos, selection);
1123
1124
1125
1126     const qreal selectionY = pos.y() + line.y.toReal();
1127     const qreal lineHeight = line.height().toReal();
1128
1129     QFixed lastSelectionX = iterator.x;
1130     QFixed lastSelectionWidth;
1131
1132     while (!iterator.atEnd()) {
1133         iterator.next();
1134
1135         QFixed selectionX, selectionWidth;
1136         if (iterator.getSelectionBounds(&selectionX, &selectionWidth)) {
1137             if (selectionX == lastSelectionX + lastSelectionWidth) {
1138                 lastSelectionWidth += selectionWidth;
1139                 continue;
1140             }
1141
1142             if (lastSelectionWidth > 0)
1143                 region->addRect(boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight));
1144
1145             lastSelectionX = selectionX;
1146             lastSelectionWidth = selectionWidth;
1147         }
1148     }
1149     if (lastSelectionWidth > 0)
1150         region->addRect(boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight));
1151 }
1152
1153 static inline QRectF clipIfValid(const QRectF &rect, const QRectF &clip)
1154 {
1155     return clip.isValid() ? (rect & clip) : rect;
1156 }
1157
1158
1159 /*!
1160     Returns the glyph indexes and positions for all glyphs in this QTextLayout. This is an
1161     expensive function, and should not be called in a time sensitive context.
1162
1163     \since 4.8
1164
1165     \sa draw(), QPainter::drawGlyphs()
1166 */
1167 #if !defined(QT_NO_RAWFONT)
1168 QList<QGlyphs> QTextLayout::glyphs() const
1169 {
1170     QList<QGlyphs> glyphs;
1171     for (int i=0; i<d->lines.size(); ++i)
1172         glyphs += QTextLine(i, d).glyphs(-1, -1);
1173
1174     return glyphs;
1175 }
1176 #endif // QT_NO_RAWFONT
1177
1178 /*!
1179     Draws the whole layout on the painter \a p at the position specified by \a pos.
1180     The rendered layout includes the given \a selections and is clipped within
1181     the rectangle specified by \a clip.
1182 */
1183 void QTextLayout::draw(QPainter *p, const QPointF &pos, const QVector<FormatRange> &selections, const QRectF &clip) const
1184 {
1185     if (d->lines.isEmpty())
1186         return;
1187
1188     if (!d->layoutData)
1189         d->itemize();
1190
1191     QPointF position = pos + d->position;
1192
1193     QFixed clipy = (INT_MIN/256);
1194     QFixed clipe = (INT_MAX/256);
1195     if (clip.isValid()) {
1196         clipy = QFixed::fromReal(clip.y() - position.y());
1197         clipe = clipy + QFixed::fromReal(clip.height());
1198     }
1199
1200     int firstLine = 0;
1201     int lastLine = d->lines.size();
1202     for (int i = 0; i < d->lines.size(); ++i) {
1203         QTextLine l(i, d);
1204         const QScriptLine &sl = d->lines[i];
1205
1206         if (sl.y > clipe) {
1207             lastLine = i;
1208             break;
1209         }
1210         if ((sl.y + sl.height()) < clipy) {
1211             firstLine = i;
1212             continue;
1213         }
1214     }
1215
1216     QPainterPath excludedRegion;
1217     QPainterPath textDoneRegion;
1218     for (int i = 0; i < selections.size(); ++i) {
1219         FormatRange selection = selections.at(i);
1220         const QBrush bg = selection.format.background();
1221
1222         QPainterPath region;
1223         region.setFillRule(Qt::WindingFill);
1224
1225         for (int line = firstLine; line < lastLine; ++line) {
1226             const QScriptLine &sl = d->lines[line];
1227             QTextLine tl(line, d);
1228
1229             QRectF lineRect(tl.naturalTextRect());
1230             lineRect.translate(position);
1231
1232             bool isLastLineInBlock = (line == d->lines.size()-1);
1233             int sl_length = sl.length + (isLastLineInBlock? 1 : 0); // the infamous newline
1234
1235
1236             if (sl.from > selection.start + selection.length || sl.from + sl_length <= selection.start)
1237                 continue; // no actual intersection
1238
1239             const bool selectionStartInLine = sl.from <= selection.start;
1240             const bool selectionEndInLine = selection.start + selection.length < sl.from + sl_length;
1241
1242             if (sl.length && (selectionStartInLine || selectionEndInLine)) {
1243                 addSelectedRegionsToPath(d, line, position, &selection, &region, clipIfValid(lineRect, clip));
1244             } else {
1245                 region.addRect(clipIfValid(lineRect, clip));
1246             }
1247
1248             if (selection.format.boolProperty(QTextFormat::FullWidthSelection)) {
1249                 QRectF fullLineRect(tl.rect());
1250                 fullLineRect.translate(position);
1251                 fullLineRect.setRight(QFIXED_MAX);
1252                 if (!selectionEndInLine)
1253                     region.addRect(clipIfValid(QRectF(lineRect.topRight(), fullLineRect.bottomRight()), clip));
1254                 if (!selectionStartInLine)
1255                     region.addRect(clipIfValid(QRectF(fullLineRect.topLeft(), lineRect.bottomLeft()), clip));
1256             } else if (!selectionEndInLine
1257                 && isLastLineInBlock
1258                 &&!(d->option.flags() & QTextOption::ShowLineAndParagraphSeparators)) {
1259                 region.addRect(clipIfValid(QRectF(lineRect.right(), lineRect.top(),
1260                                                   lineRect.height()/4, lineRect.height()), clip));
1261             }
1262
1263         }
1264         {
1265             const QPen oldPen = p->pen();
1266             const QBrush oldBrush = p->brush();
1267
1268             p->setPen(selection.format.penProperty(QTextFormat::OutlinePen));
1269             p->setBrush(selection.format.brushProperty(QTextFormat::BackgroundBrush));
1270             p->drawPath(region);
1271
1272             p->setPen(oldPen);
1273             p->setBrush(oldBrush);
1274         }
1275
1276
1277
1278         bool hasText = (selection.format.foreground().style() != Qt::NoBrush);
1279         bool hasBackground= (selection.format.background().style() != Qt::NoBrush);
1280
1281         if (hasBackground) {
1282             selection.format.setProperty(ObjectSelectionBrush, selection.format.property(QTextFormat::BackgroundBrush));
1283             // don't just clear the property, set an empty brush that overrides a potential
1284             // background brush specified in the text
1285             selection.format.setProperty(QTextFormat::BackgroundBrush, QBrush());
1286             selection.format.clearProperty(QTextFormat::OutlinePen);
1287         }
1288
1289         selection.format.setProperty(SuppressText, !hasText);
1290
1291         if (hasText && !hasBackground && !(textDoneRegion & region).isEmpty())
1292             continue;
1293
1294         p->save();
1295         p->setClipPath(region, Qt::IntersectClip);
1296
1297         for (int line = firstLine; line < lastLine; ++line) {
1298             QTextLine l(line, d);
1299             l.draw(p, position, &selection);
1300         }
1301         p->restore();
1302
1303         if (hasText) {
1304             textDoneRegion += region;
1305         } else {
1306             if (hasBackground)
1307                 textDoneRegion -= region;
1308         }
1309
1310         excludedRegion += region;
1311     }
1312
1313     QPainterPath needsTextButNoBackground = excludedRegion - textDoneRegion;
1314     if (!needsTextButNoBackground.isEmpty()){
1315         p->save();
1316         p->setClipPath(needsTextButNoBackground, Qt::IntersectClip);
1317         FormatRange selection;
1318         selection.start = 0;
1319         selection.length = INT_MAX;
1320         selection.format.setProperty(SuppressBackground, true);
1321         for (int line = firstLine; line < lastLine; ++line) {
1322             QTextLine l(line, d);
1323             l.draw(p, position, &selection);
1324         }
1325         p->restore();
1326     }
1327
1328     if (!excludedRegion.isEmpty()) {
1329         p->save();
1330         QPainterPath path;
1331         QRectF br = boundingRect().translated(position);
1332         br.setRight(QFIXED_MAX);
1333         if (!clip.isNull())
1334             br = br.intersected(clip);
1335         path.addRect(br);
1336         path -= excludedRegion;
1337         p->setClipPath(path, Qt::IntersectClip);
1338     }
1339
1340     for (int i = firstLine; i < lastLine; ++i) {
1341         QTextLine l(i, d);
1342         l.draw(p, position);
1343     }
1344     if (!excludedRegion.isEmpty())
1345         p->restore();
1346
1347
1348     if (!d->cacheGlyphs)
1349         d->freeMemory();
1350 }
1351
1352 /*!
1353     \fn void QTextLayout::drawCursor(QPainter *painter, const QPointF &position, int cursorPosition) const
1354     \overload
1355
1356     Draws a text cursor with the current pen at the given \a position using the
1357     \a painter specified.
1358     The corresponding position within the text is specified by \a cursorPosition.
1359 */
1360 void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition) const
1361 {
1362     drawCursor(p, pos, cursorPosition, 1);
1363 }
1364
1365 /*!
1366     \fn void QTextLayout::drawCursor(QPainter *painter, const QPointF &position, int cursorPosition, int width) const
1367
1368     Draws a text cursor with the current pen and the specified \a width at the given \a position using the
1369     \a painter specified.
1370     The corresponding position within the text is specified by \a cursorPosition.
1371 */
1372 void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition, int width) const
1373 {
1374     if (d->lines.isEmpty())
1375         return;
1376
1377     if (!d->layoutData)
1378         d->itemize();
1379
1380     QPointF position = pos + d->position;
1381     QFixed pos_x = QFixed::fromReal(position.x());
1382     QFixed pos_y = QFixed::fromReal(position.y());
1383
1384     cursorPosition = qBound(0, cursorPosition, d->layoutData->string.length());
1385     int line = 0;
1386     if (cursorPosition == d->layoutData->string.length()) {
1387         line = d->lines.size() - 1;
1388     } else {
1389         // ### binary search
1390         for (line = 0; line < d->lines.size(); line++) {
1391             const QScriptLine &sl = d->lines[line];
1392             if (sl.from <= cursorPosition && sl.from + (int)sl.length > cursorPosition)
1393                 break;
1394         }
1395     }
1396
1397     if (line >= d->lines.size())
1398         return;
1399
1400     QTextLine l(line, d);
1401     const QScriptLine &sl = d->lines[line];
1402
1403     qreal x = position.x() + l.cursorToX(cursorPosition);
1404
1405     int itm = d->findItem(cursorPosition - 1);
1406     QFixed base = sl.base();
1407     QFixed descent = sl.descent;
1408     bool rightToLeft = d->isRightToLeft();
1409     if (itm >= 0) {
1410         const QScriptItem &si = d->layoutData->items.at(itm);
1411         if (si.ascent > 0)
1412             base = si.ascent;
1413         if (si.descent > 0)
1414             descent = si.descent;
1415         rightToLeft = si.analysis.bidiLevel % 2;
1416     }
1417     qreal y = position.y() + (sl.y + sl.base() - base).toReal();
1418     bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing)
1419                               && (p->transform().type() > QTransform::TxTranslate);
1420     if (toggleAntialiasing)
1421         p->setRenderHint(QPainter::Antialiasing);
1422 #if defined(QT_MAC_USE_COCOA)
1423     // Always draw the cursor aligned to pixel boundary.
1424     x = qRound(x);
1425 #endif
1426     p->fillRect(QRectF(x, y, qreal(width), (base + descent + 1).toReal()), p->pen().brush());
1427     if (toggleAntialiasing)
1428         p->setRenderHint(QPainter::Antialiasing, false);
1429     if (d->layoutData->hasBidi) {
1430         const int arrow_extent = 4;
1431         int sign = rightToLeft ? -1 : 1;
1432         p->drawLine(QLineF(x, y, x + (sign * arrow_extent/2), y + arrow_extent/2));
1433         p->drawLine(QLineF(x, y+arrow_extent, x + (sign * arrow_extent/2), y + arrow_extent/2));
1434     }
1435     return;
1436 }
1437
1438 /*!
1439     \class QTextLine
1440     \reentrant
1441
1442     \brief The QTextLine class represents a line of text inside a QTextLayout.
1443
1444     \ingroup richtext-processing
1445
1446     A text line is usually created by QTextLayout::createLine().
1447
1448     After being created, the line can be filled using the setLineWidth()
1449     or setNumColumns() functions. A line has a number of attributes including the
1450     rectangle it occupies, rect(), its coordinates, x() and y(), its
1451     textLength(), width() and naturalTextWidth(), and its ascent() and decent()
1452     relative to the text. The position of the cursor in terms of the
1453     line is available from cursorToX() and its inverse from
1454     xToCursor(). A line can be moved with setPosition().
1455 */
1456
1457 /*!
1458     \enum QTextLine::Edge
1459
1460     \value Leading
1461     \value Trailing
1462 */
1463
1464 /*!
1465     \enum QTextLine::CursorPosition
1466
1467     \value CursorBetweenCharacters
1468     \value CursorOnCharacter
1469 */
1470
1471 /*!
1472     \fn QTextLine::QTextLine(int line, QTextEngine *e)
1473     \internal
1474
1475     Constructs a new text line using the line at position \a line in
1476     the text engine \a e.
1477 */
1478
1479 /*!
1480     \fn QTextLine::QTextLine()
1481
1482     Creates an invalid line.
1483 */
1484
1485 /*!
1486     \fn bool QTextLine::isValid() const
1487
1488     Returns true if this text line is valid; otherwise returns false.
1489 */
1490
1491 /*!
1492     \fn int QTextLine::lineNumber() const
1493
1494     Returns the position of the line in the text engine.
1495 */
1496
1497
1498 /*!
1499     Returns the line's bounding rectangle.
1500
1501     \sa x(), y(), textLength(), width()
1502 */
1503 QRectF QTextLine::rect() const
1504 {
1505     const QScriptLine& sl = eng->lines[i];
1506     return QRectF(sl.x.toReal(), sl.y.toReal(), sl.width.toReal(), sl.height().toReal());
1507 }
1508
1509 /*!
1510     Returns the rectangle covered by the line.
1511 */
1512 QRectF QTextLine::naturalTextRect() const
1513 {
1514     const QScriptLine& sl = eng->lines[i];
1515     QFixed x = sl.x + alignLine(eng, sl);
1516
1517     QFixed width = sl.textWidth;
1518     if (sl.justified)
1519         width = sl.width;
1520
1521     return QRectF(x.toReal(), sl.y.toReal(), width.toReal(), sl.height().toReal());
1522 }
1523
1524 /*!
1525     Returns the line's x position.
1526
1527     \sa rect(), y(), textLength(), width()
1528 */
1529 qreal QTextLine::x() const
1530 {
1531     return eng->lines[i].x.toReal();
1532 }
1533
1534 /*!
1535     Returns the line's y position.
1536
1537     \sa x(), rect(), textLength(), width()
1538 */
1539 qreal QTextLine::y() const
1540 {
1541     return eng->lines[i].y.toReal();
1542 }
1543
1544 /*!
1545     Returns the line's width as specified by the layout() function.
1546
1547     \sa naturalTextWidth(), x(), y(), textLength(), rect()
1548 */
1549 qreal QTextLine::width() const
1550 {
1551     return eng->lines[i].width.toReal();
1552 }
1553
1554
1555 /*!
1556     Returns the line's ascent.
1557
1558     \sa descent(), height()
1559 */
1560 qreal QTextLine::ascent() const
1561 {
1562     return eng->lines[i].ascent.toReal();
1563 }
1564
1565 /*!
1566     Returns the line's descent.
1567
1568     \sa ascent(), height()
1569 */
1570 qreal QTextLine::descent() const
1571 {
1572     return eng->lines[i].descent.toReal();
1573 }
1574
1575 /*!
1576     Returns the line's height. This is equal to ascent() + descent() + 1
1577     if leading is not included. If leading is included, this equals to
1578     ascent() + descent() + leading() + 1.
1579
1580     \sa ascent(), descent(), leading(), setLeadingIncluded()
1581 */
1582 qreal QTextLine::height() const
1583 {
1584     return eng->lines[i].height().toReal();
1585 }
1586
1587 /*!
1588     \since 4.6
1589
1590     Returns the line's leading.
1591
1592     \sa ascent(), descent(), height()
1593 */
1594 qreal QTextLine::leading() const
1595 {
1596     return eng->lines[i].leading.toReal();
1597 }
1598
1599 /*!
1600     \since 4.6
1601
1602     Includes positive leading into the line's height if \a included is true;
1603     otherwise does not include leading.
1604
1605     By default, leading is not included.
1606
1607     Note that negative leading is ignored, it must be handled
1608     in the code using the text lines by letting the lines overlap.
1609
1610     \sa leadingIncluded()
1611
1612 */
1613 void QTextLine::setLeadingIncluded(bool included)
1614 {
1615     eng->lines[i].leadingIncluded= included;
1616
1617 }
1618
1619 /*!
1620     \since 4.6
1621
1622     Returns true if positive leading is included into the line's height;
1623     otherwise returns false.
1624
1625     By default, leading is not included.
1626
1627     \sa setLeadingIncluded()
1628 */
1629 bool QTextLine::leadingIncluded() const
1630 {
1631     return eng->lines[i].leadingIncluded;
1632 }
1633
1634 /*!
1635     Returns the width of the line that is occupied by text. This is
1636     always \<= to width(), and is the minimum width that could be used
1637     by layout() without changing the line break position.
1638 */
1639 qreal QTextLine::naturalTextWidth() const
1640 {
1641     return eng->lines[i].textWidth.toReal();
1642 }
1643
1644 /*!
1645     \since 4.7
1646     Returns the horizontal advance of the text. The advance of the text
1647     is the distance from its position to the next position at which
1648     text would naturally be drawn.
1649
1650     By adding the advance to the position of the text line and using this
1651     as the position of a second text line, you will be able to position
1652     the two lines side-by-side without gaps in-between.
1653 */
1654 qreal QTextLine::horizontalAdvance() const
1655 {
1656     return eng->lines[i].textAdvance.toReal();
1657 }
1658
1659 /*!
1660     Lays out the line with the given \a width. The line is filled from
1661     its starting position with as many characters as will fit into
1662     the line. In case the text cannot be split at the end of the line,
1663     it will be filled with additional characters to the next whitespace
1664     or end of the text.
1665 */
1666 void QTextLine::setLineWidth(qreal width)
1667 {
1668     QScriptLine &line = eng->lines[i];
1669     if (!eng->layoutData) {
1670         qWarning("QTextLine: Can't set a line width while not layouting.");
1671         return;
1672     }
1673
1674     if (width > QFIXED_MAX)
1675         width = QFIXED_MAX;
1676
1677     line.width = QFixed::fromReal(width);
1678     if (line.length
1679         && line.textWidth <= line.width
1680         && line.from + line.length == eng->layoutData->string.length())
1681         // no need to do anything if the line is already layouted and the last one. This optimization helps
1682         // when using things in a single line layout.
1683         return;
1684     line.length = 0;
1685     line.textWidth = 0;
1686
1687     layout_helper(INT_MAX);
1688 }
1689
1690 /*!
1691     Lays out the line. The line is filled from its starting position
1692     with as many characters as are specified by \a numColumns. In case
1693     the text cannot be split until \a numColumns characters, the line
1694     will be filled with as many characters to the next whitespace or
1695     end of the text.
1696 */
1697 void QTextLine::setNumColumns(int numColumns)
1698 {
1699     QScriptLine &line = eng->lines[i];
1700     line.width = QFIXED_MAX;
1701     line.length = 0;
1702     line.textWidth = 0;
1703     layout_helper(numColumns);
1704 }
1705
1706 /*!
1707     Lays out the line. The line is filled from its starting position
1708     with as many characters as are specified by \a numColumns. In case
1709     the text cannot be split until \a numColumns characters, the line
1710     will be filled with as many characters to the next whitespace or
1711     end of the text. The provided \a alignmentWidth is used as reference
1712     width for alignment.
1713 */
1714 void QTextLine::setNumColumns(int numColumns, qreal alignmentWidth)
1715 {
1716     QScriptLine &line = eng->lines[i];
1717     line.width = QFixed::fromReal(alignmentWidth);
1718     line.length = 0;
1719     line.textWidth = 0;
1720     layout_helper(numColumns);
1721 }
1722
1723 #if 0
1724 #define LB_DEBUG qDebug
1725 #else
1726 #define LB_DEBUG if (0) qDebug
1727 #endif
1728
1729 namespace {
1730
1731     struct LineBreakHelper
1732     {
1733         LineBreakHelper()
1734             : glyphCount(0), maxGlyphs(0), currentPosition(0), fontEngine(0), logClusters(0),
1735               manualWrap(false), whiteSpaceOrObject(true)
1736         {
1737         }
1738
1739
1740         QScriptLine tmpData;
1741         QScriptLine spaceData;
1742
1743         QGlyphLayout glyphs;
1744
1745         int glyphCount;
1746         int maxGlyphs;
1747         int currentPosition;
1748         glyph_t previousGlyph;
1749
1750         QFixed minw;
1751         QFixed softHyphenWidth;
1752         QFixed rightBearing;
1753         QFixed minimumRightBearing;
1754
1755         QFontEngine *fontEngine;
1756         const unsigned short *logClusters;
1757
1758         bool manualWrap;
1759         bool whiteSpaceOrObject;
1760
1761         bool checkFullOtherwiseExtend(QScriptLine &line);
1762
1763         QFixed calculateNewWidth(const QScriptLine &line) const {
1764             return line.textWidth + tmpData.textWidth + spaceData.textWidth + softHyphenWidth
1765                     - qMin(rightBearing, QFixed());
1766         }
1767
1768         inline glyph_t currentGlyph() const
1769         {            
1770             Q_ASSERT(currentPosition > 0);
1771             Q_ASSERT(logClusters[currentPosition - 1] < glyphs.numGlyphs);
1772
1773             return glyphs.glyphs[logClusters[currentPosition - 1]];
1774         }
1775
1776         inline void saveCurrentGlyph()
1777         {
1778             previousGlyph = 0;
1779             if (currentPosition > 0 &&
1780                 logClusters[currentPosition - 1] < glyphs.numGlyphs) {
1781                 previousGlyph = currentGlyph(); // needed to calculate right bearing later
1782             }
1783         }
1784
1785         inline void adjustRightBearing(glyph_t glyph)
1786         {
1787             qreal rb;
1788             fontEngine->getGlyphBearings(glyph, 0, &rb);
1789             rightBearing = qMin(QFixed(), QFixed::fromReal(rb));
1790         }
1791
1792         inline void adjustRightBearing()
1793         {
1794             if (currentPosition <= 0)
1795                 return;
1796             adjustRightBearing(currentGlyph());
1797         }
1798
1799         inline void adjustPreviousRightBearing()
1800         {
1801             if (previousGlyph > 0)
1802                 adjustRightBearing(previousGlyph);
1803         }
1804
1805         inline void resetRightBearing()
1806         {
1807             rightBearing = QFixed(1); // Any positive number is defined as invalid since only
1808                                       // negative right bearings are interesting to us.
1809         }
1810     };
1811
1812 inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line)
1813 {
1814     LB_DEBUG("possible break width %f, spacew=%f", tmpData.textWidth.toReal(), spaceData.textWidth.toReal());
1815
1816     QFixed newWidth = calculateNewWidth(line);
1817     if (line.length && !manualWrap && (newWidth > line.width || glyphCount > maxGlyphs))
1818         return true;
1819
1820     minw = qMax(minw, tmpData.textWidth);
1821     line += tmpData;
1822     line.textWidth += spaceData.textWidth;
1823
1824     line.length += spaceData.length;
1825     tmpData.textWidth = 0;
1826     tmpData.length = 0;
1827     spaceData.textWidth = 0;
1828     spaceData.length = 0;
1829
1830     return false;
1831 }
1832
1833 } // anonymous namespace
1834
1835
1836 static inline void addNextCluster(int &pos, int end, QScriptLine &line, int &glyphCount,
1837                                   const QScriptItem &current, const unsigned short *logClusters,
1838                                   const QGlyphLayout &glyphs)
1839 {
1840     int glyphPosition = logClusters[pos];
1841     do { // got to the first next cluster
1842         ++pos;
1843         ++line.length;
1844     } while (pos < end && logClusters[pos] == glyphPosition);
1845     do { // calculate the textWidth for the rest of the current cluster.
1846         if (!glyphs.attributes[glyphPosition].dontPrint)
1847             line.textWidth += glyphs.advances_x[glyphPosition];
1848         ++glyphPosition;
1849     } while (glyphPosition < current.num_glyphs && !glyphs.attributes[glyphPosition].clusterStart);
1850
1851     Q_ASSERT((pos == end && glyphPosition == current.num_glyphs) || logClusters[pos] == glyphPosition);
1852
1853     ++glyphCount;
1854 }
1855
1856
1857 // fill QScriptLine
1858 void QTextLine::layout_helper(int maxGlyphs)
1859 {
1860     QScriptLine &line = eng->lines[i];
1861     line.length = 0;
1862     line.textWidth = 0;
1863     line.hasTrailingSpaces = false;
1864
1865     if (!eng->layoutData->items.size() || line.from >= eng->layoutData->string.length()) {
1866         line.setDefaultHeight(eng);
1867         return;
1868     }
1869
1870     Q_ASSERT(line.from < eng->layoutData->string.length());
1871
1872     LineBreakHelper lbh;
1873
1874     lbh.maxGlyphs = maxGlyphs;
1875
1876     QTextOption::WrapMode wrapMode = eng->option.wrapMode();
1877     bool breakany = (wrapMode == QTextOption::WrapAnywhere);
1878     lbh.manualWrap = (wrapMode == QTextOption::ManualWrap || wrapMode == QTextOption::NoWrap);
1879
1880     int item = -1;
1881     int newItem = eng->findItem(line.from);
1882
1883     LB_DEBUG("from: %d: item=%d, total %d, width available %f", line.from, newItem, eng->layoutData->items.size(), line.width.toReal());
1884
1885     Qt::Alignment alignment = eng->option.alignment();
1886
1887     const HB_CharAttributes *attributes = eng->attributes();
1888     if (!attributes)
1889         return;
1890     lbh.currentPosition = line.from;
1891     int end = 0;
1892     lbh.logClusters = eng->layoutData->logClustersPtr;
1893     lbh.previousGlyph = 0;
1894
1895     while (newItem < eng->layoutData->items.size()) {
1896         lbh.resetRightBearing();
1897         lbh.softHyphenWidth = 0;
1898         if (newItem != item) {
1899             item = newItem;
1900             const QScriptItem &current = eng->layoutData->items[item];
1901             if (!current.num_glyphs) {
1902                 eng->shape(item);
1903                 attributes = eng->attributes();
1904                 if (!attributes)
1905                     return;
1906                 lbh.logClusters = eng->layoutData->logClustersPtr;
1907             }
1908             lbh.currentPosition = qMax(line.from, current.position);
1909             end = current.position + eng->length(item);
1910             lbh.glyphs = eng->shapedGlyphs(&current);
1911             QFontEngine *fontEngine = eng->fontEngine(current);
1912             if (lbh.fontEngine != fontEngine) {
1913                 lbh.fontEngine = fontEngine;
1914                 lbh.minimumRightBearing = qMin(QFixed(),
1915                                                QFixed::fromReal(fontEngine->minRightBearing()));
1916             }
1917         }
1918         const QScriptItem &current = eng->layoutData->items[item];
1919
1920         lbh.tmpData.leading = qMax(lbh.tmpData.leading + lbh.tmpData.ascent,
1921                                    current.leading + current.ascent) - qMax(lbh.tmpData.ascent,
1922                                                                             current.ascent);
1923         lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
1924         lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent);
1925
1926         if (current.analysis.flags == QScriptAnalysis::Tab && (alignment & (Qt::AlignLeft | Qt::AlignRight | Qt::AlignCenter | Qt::AlignJustify))) {
1927             lbh.whiteSpaceOrObject = true;
1928             if (lbh.checkFullOtherwiseExtend(line))
1929                 goto found;
1930
1931             QFixed x = line.x + line.textWidth + lbh.tmpData.textWidth + lbh.spaceData.textWidth;
1932             QFixed tabWidth = eng->calculateTabWidth(item, x);
1933
1934             lbh.spaceData.textWidth += tabWidth;
1935             lbh.spaceData.length++;
1936             newItem = item + 1;
1937
1938             QFixed averageCharWidth = eng->fontEngine(current)->averageCharWidth();
1939             lbh.glyphCount += qRound(tabWidth / averageCharWidth);
1940
1941             if (lbh.checkFullOtherwiseExtend(line))
1942                 goto found;
1943         } else if (current.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator) {
1944             lbh.whiteSpaceOrObject = true;
1945             // if the line consists only of the line separator make sure
1946             // we have a sane height
1947             if (!line.length && !lbh.tmpData.length)
1948                 line.setDefaultHeight(eng);
1949             if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
1950                 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
1951                                current, lbh.logClusters, lbh.glyphs);
1952             } else {
1953                 lbh.tmpData.length++;
1954                 lbh.adjustPreviousRightBearing();
1955             }
1956             line += lbh.tmpData;
1957             goto found;
1958         } else if (current.analysis.flags == QScriptAnalysis::Object) {
1959             lbh.whiteSpaceOrObject = true;
1960             lbh.tmpData.length++;
1961
1962             QTextFormat format = eng->formats()->format(eng->formatIndex(&eng->layoutData->items[item]));
1963             if (eng->block.docHandle())
1964                 eng->docLayout()->positionInlineObject(QTextInlineObject(item, eng), eng->block.position() + current.position, format);
1965
1966             lbh.tmpData.textWidth += current.width;
1967
1968             newItem = item + 1;
1969             ++lbh.glyphCount;
1970             if (lbh.checkFullOtherwiseExtend(line))
1971                 goto found;
1972         } else if (attributes[lbh.currentPosition].whiteSpace) {
1973             lbh.whiteSpaceOrObject = true;
1974             while (lbh.currentPosition < end && attributes[lbh.currentPosition].whiteSpace)
1975                 addNextCluster(lbh.currentPosition, end, lbh.spaceData, lbh.glyphCount,
1976                                current, lbh.logClusters, lbh.glyphs);
1977
1978             if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width) {
1979                 lbh.spaceData.textWidth = line.width; // ignore spaces that fall out of the line.
1980                 goto found;
1981             }
1982         } else {
1983             lbh.whiteSpaceOrObject = false;
1984             bool sb_or_ws = false;
1985             lbh.saveCurrentGlyph();
1986             do {
1987                 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
1988                                current, lbh.logClusters, lbh.glyphs);
1989
1990                 if (attributes[lbh.currentPosition].whiteSpace || attributes[lbh.currentPosition-1].lineBreakType != HB_NoBreak) {
1991                     sb_or_ws = true;
1992                     break;
1993                 } else if (breakany && attributes[lbh.currentPosition].charStop) {
1994                     break;
1995                 }
1996             } while (lbh.currentPosition < end);
1997             lbh.minw = qMax(lbh.tmpData.textWidth, lbh.minw);
1998
1999             if (lbh.currentPosition && attributes[lbh.currentPosition - 1].lineBreakType == HB_SoftHyphen) {
2000                 // if we are splitting up a word because of
2001                 // a soft hyphen then we ...
2002                 //
2003                 //  a) have to take the width of the soft hyphen into
2004                 //     account to see if the first syllable(s) /and/
2005                 //     the soft hyphen fit into the line
2006                 //
2007                 //  b) if we are so short of available width that the
2008                 //     soft hyphen is the first breakable position, then
2009                 //     we don't want to show it. However we initially
2010                 //     have to take the width for it into account so that
2011                 //     the text document layout sees the overflow and
2012                 //     switch to break-anywhere mode, in which we
2013                 //     want the soft-hyphen to slip into the next line
2014                 //     and thus become invisible again.
2015                 //
2016                 if (line.length)
2017                     lbh.softHyphenWidth = lbh.glyphs.advances_x[lbh.logClusters[lbh.currentPosition - 1]];
2018                 else if (breakany)
2019                     lbh.tmpData.textWidth += lbh.glyphs.advances_x[lbh.logClusters[lbh.currentPosition - 1]];
2020             }
2021
2022             // The actual width of the text needs to take the right bearing into account. The
2023             // right bearing is left-ward, which means that if the rightmost pixel is to the right
2024             // of the advance of the glyph, the bearing will be negative. We flip the sign
2025             // for the code to be more readable. Logic borrowed from qfontmetrics.cpp.
2026             // We ignore the right bearing if the minimum negative bearing is too little to
2027             // expand the text beyond the edge.
2028             if (sb_or_ws|breakany) {
2029                 QFixed rightBearing = lbh.rightBearing; // store previous right bearing
2030 #if !defined(Q_WS_MAC)
2031                 if (lbh.calculateNewWidth(line) - lbh.minimumRightBearing > line.width)
2032 #endif
2033                     lbh.adjustRightBearing();
2034                 if (lbh.checkFullOtherwiseExtend(line)) {
2035                     // we are too wide, fix right bearing
2036                     if (rightBearing <= 0)
2037                         lbh.rightBearing = rightBearing; // take from cache
2038                     else
2039                         lbh.adjustPreviousRightBearing();
2040
2041                     if (!breakany) {
2042                         line.textWidth += lbh.softHyphenWidth;
2043                     }
2044
2045                     goto found;
2046                 }
2047             }
2048             lbh.saveCurrentGlyph();
2049         }
2050         if (lbh.currentPosition == end)
2051             newItem = item + 1;
2052     }
2053     LB_DEBUG("reached end of line");
2054     lbh.checkFullOtherwiseExtend(line);
2055 found:
2056     if (lbh.rightBearing > 0 && !lbh.whiteSpaceOrObject) // If right bearing has not yet been adjusted
2057         lbh.adjustRightBearing();
2058     line.textAdvance = line.textWidth;
2059     line.textWidth -= qMin(QFixed(), lbh.rightBearing);
2060
2061     if (line.length == 0) {
2062         LB_DEBUG("no break available in line, adding temp: length %d, width %f, space: length %d, width %f",
2063                lbh.tmpData.length, lbh.tmpData.textWidth.toReal(),
2064                lbh.spaceData.length, lbh.spaceData.textWidth.toReal());
2065         line += lbh.tmpData;
2066     }
2067
2068     LB_DEBUG("line length = %d, ascent=%f, descent=%f, textWidth=%f (spacew=%f)", line.length, line.ascent.toReal(),
2069            line.descent.toReal(), line.textWidth.toReal(), lbh.spaceData.width.toReal());
2070     LB_DEBUG("        : '%s'", eng->layoutData->string.mid(line.from, line.length).toUtf8().data());
2071
2072     if (lbh.manualWrap) {
2073         eng->minWidth = qMax(eng->minWidth, line.textWidth);
2074         eng->maxWidth = qMax(eng->maxWidth, line.textWidth);
2075     } else {
2076         eng->minWidth = qMax(eng->minWidth, lbh.minw);
2077         eng->maxWidth += line.textWidth;
2078     }
2079
2080     if (line.textWidth > 0 && item < eng->layoutData->items.size())
2081         eng->maxWidth += lbh.spaceData.textWidth;
2082     if (eng->option.flags() & QTextOption::IncludeTrailingSpaces)
2083         line.textWidth += lbh.spaceData.textWidth;
2084     if (lbh.spaceData.length) {
2085         line.length += lbh.spaceData.length;
2086         line.hasTrailingSpaces = true;
2087     }
2088
2089     line.justified = false;
2090     line.gridfitted = false;
2091
2092     if (eng->option.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere) {
2093         if ((lbh.maxGlyphs != INT_MAX && lbh.glyphCount > lbh.maxGlyphs)
2094             || (lbh.maxGlyphs == INT_MAX && line.textWidth > line.width)) {
2095
2096             eng->option.setWrapMode(QTextOption::WrapAnywhere);
2097             line.length = 0;
2098             line.textWidth = 0;
2099             layout_helper(lbh.maxGlyphs);
2100             eng->option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
2101         }
2102     }
2103 }
2104
2105 /*!
2106     Moves the line to position \a pos.
2107 */
2108 void QTextLine::setPosition(const QPointF &pos)
2109 {
2110     eng->lines[i].x = QFixed::fromReal(pos.x());
2111     eng->lines[i].y = QFixed::fromReal(pos.y());
2112 }
2113
2114 /*!
2115     Returns the line's position relative to the text layout's position.
2116 */
2117 QPointF QTextLine::position() const
2118 {
2119     return QPointF(eng->lines[i].x.toReal(), eng->lines[i].y.toReal());
2120 }
2121
2122 // ### DOC: I have no idea what this means/does.
2123 // You create a text layout with a string of text. Once you laid
2124 // it out, it contains a number of QTextLines. from() returns the position
2125 // inside the text string where this line starts. If you e.g. has a
2126 // text of "This is a string", laid out into two lines (the second
2127 // starting at the word 'a'), layout.lineAt(0).from() == 0 and
2128 // layout.lineAt(1).from() == 8.
2129 /*!
2130     Returns the start of the line from the beginning of the string
2131     passed to the QTextLayout.
2132 */
2133 int QTextLine::textStart() const
2134 {
2135     return eng->lines[i].from;
2136 }
2137
2138 /*!
2139     Returns the length of the text in the line.
2140
2141     \sa naturalTextWidth()
2142 */
2143 int QTextLine::textLength() const
2144 {
2145     if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators
2146         && eng->block.isValid() && i == eng->lines.count()-1) {
2147         return eng->lines[i].length - 1;
2148     }
2149     return eng->lines[i].length;
2150 }
2151
2152 static void drawMenuText(QPainter *p, QFixed x, QFixed y, const QScriptItem &si, QTextItemInt &gf, QTextEngine *eng,
2153                          int start, int glyph_start)
2154 {
2155     int ge = glyph_start + gf.glyphs.numGlyphs;
2156     int gs = glyph_start;
2157     int end = start + gf.num_chars;
2158     unsigned short *logClusters = eng->logClusters(&si);
2159     QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2160     QFixed orig_width = gf.width;
2161
2162     int *ul = eng->underlinePositions;
2163     if (ul)
2164         while (*ul != -1 && *ul < start)
2165             ++ul;
2166     bool rtl = si.analysis.bidiLevel % 2;
2167     if (rtl)
2168         x += si.width;
2169
2170     do {
2171         int gtmp = ge;
2172         int stmp = end;
2173         if (ul && *ul != -1 && *ul < end) {
2174             stmp = *ul;
2175             gtmp = logClusters[*ul-si.position];
2176         }
2177
2178         gf.glyphs = glyphs.mid(gs, gtmp - gs);
2179         gf.num_chars = stmp - start;
2180         gf.chars = eng->layoutData->string.unicode() + start;
2181         QFixed w = 0;
2182         while (gs < gtmp) {
2183             w += glyphs.effectiveAdvance(gs);
2184             ++gs;
2185         }
2186         start = stmp;
2187         gf.width = w;
2188         if (rtl)
2189             x -= w;
2190         if (gf.num_chars)
2191             p->drawTextItem(QPointF(x.toReal(), y.toReal()), gf);
2192         if (!rtl)
2193             x += w;
2194         if (ul && *ul != -1 && *ul < end) {
2195             // draw underline
2196             gtmp = (*ul == end-1) ? ge : logClusters[*ul+1-si.position];
2197             ++stmp;
2198             gf.glyphs = glyphs.mid(gs, gtmp - gs);
2199             gf.num_chars = stmp - start;
2200             gf.chars = eng->layoutData->string.unicode() + start;
2201             gf.logClusters = logClusters + start - si.position;
2202             w = 0;
2203             while (gs < gtmp) {
2204                 w += glyphs.effectiveAdvance(gs);
2205                 ++gs;
2206             }
2207             ++start;
2208             gf.width = w;
2209             gf.underlineStyle = QTextCharFormat::SingleUnderline;
2210             if (rtl)
2211                 x -= w;
2212             p->drawTextItem(QPointF(x.toReal(), y.toReal()), gf);
2213             if (!rtl)
2214                 x += w;
2215             gf.underlineStyle = QTextCharFormat::NoUnderline;
2216             ++gf.chars;
2217             ++ul;
2218         }
2219     } while (gs < ge);
2220
2221     gf.width = orig_width;
2222 }
2223
2224
2225 static void setPenAndDrawBackground(QPainter *p, const QPen &defaultPen, const QTextCharFormat &chf, const QRectF &r)
2226 {
2227     QBrush c = chf.foreground();
2228     if (c.style() == Qt::NoBrush) {
2229         p->setPen(defaultPen);
2230     }
2231
2232     QBrush bg = chf.background();
2233     if (bg.style() != Qt::NoBrush && !chf.property(SuppressBackground).toBool())
2234         p->fillRect(r, bg);
2235     if (c.style() != Qt::NoBrush) {
2236         p->setPen(QPen(c, 0));
2237     }
2238
2239 }
2240
2241 namespace {
2242     struct GlyphInfo
2243     {
2244         GlyphInfo(const QGlyphLayout &layout, const QPointF &position,
2245                   const QTextItemInt::RenderFlags &renderFlags)
2246             : glyphLayout(layout), itemPosition(position), flags(renderFlags)
2247         {
2248         }
2249
2250         QGlyphLayout glyphLayout;
2251         QPointF itemPosition;
2252         QTextItem::RenderFlags flags;
2253     };
2254 }
2255
2256 /*!
2257     \internal
2258
2259     Returns the glyph indexes and positions for all glyphs in this QTextLine which reside in
2260     QScriptItems that overlap with the range defined by \a from and \a length. The arguments
2261     specify characters, relative to the text in the layout. Note that it is not possible to
2262     use this function to retrieve a subset of the glyphs in a QScriptItem.
2263
2264     \since 4.8
2265
2266     \sa QTextLayout::glyphs()
2267 */
2268 #if !defined(QT_NO_RAWFONT)
2269 QList<QGlyphs> QTextLine::glyphs(int from, int length) const
2270 {
2271     const QScriptLine &line = eng->lines[i];
2272
2273     if (line.length == 0)
2274         return QList<QGlyphs>();
2275
2276     QHash<QFontEngine *, GlyphInfo> glyphLayoutHash;
2277
2278     QTextLineItemIterator iterator(eng, i);
2279     qreal y = line.y.toReal() + line.base().toReal();
2280     while (!iterator.atEnd()) {
2281         QScriptItem &si = iterator.next();
2282         if (si.analysis.flags >= QScriptAnalysis::TabOrObject)
2283             continue;
2284
2285         QPointF pos(iterator.x.toReal(), y);
2286         if (from >= 0 && length >= 0 &&
2287             (from >= si.position + eng->length(&si) || from + length <= si.position))
2288             continue;
2289
2290         QFont font = eng->font(si);
2291
2292         QTextItem::RenderFlags flags;
2293         if (font.overline())
2294             flags |= QTextItem::Overline;
2295         if (font.underline())
2296             flags |= QTextItem::Underline;
2297         if (font.strikeOut())
2298             flags |= QTextItem::StrikeOut;
2299         if (si.analysis.bidiLevel % 2)
2300             flags |= QTextItem::RightToLeft;
2301
2302         QGlyphLayout glyphLayout = eng->shapedGlyphs(&si).mid(iterator.glyphsStart,
2303                                                               iterator.glyphsEnd - iterator.glyphsStart);
2304
2305         if (glyphLayout.numGlyphs > 0) {
2306             QFontEngine *mainFontEngine = font.d->engineForScript(si.analysis.script);
2307             if (mainFontEngine->type() == QFontEngine::Multi) {
2308                 QFontEngineMulti *multiFontEngine = static_cast<QFontEngineMulti *>(mainFontEngine);
2309                 int start = 0;
2310                 int end;
2311                 int which = glyphLayout.glyphs[0] >> 24;
2312                 for (end = 0; end < glyphLayout.numGlyphs; ++end) {
2313                     const int e = glyphLayout.glyphs[end] >> 24;
2314                     if (e == which)
2315                         continue;
2316
2317                     QGlyphLayout subLayout = glyphLayout.mid(start, end - start);
2318                     glyphLayoutHash.insertMulti(multiFontEngine->engine(which),
2319                                                 GlyphInfo(subLayout, pos, flags));
2320
2321                     start = end;
2322                     which = e;
2323                 }
2324
2325                 QGlyphLayout subLayout = glyphLayout.mid(start, end - start);
2326                 glyphLayoutHash.insertMulti(multiFontEngine->engine(which),
2327                                             GlyphInfo(subLayout, pos, flags));
2328
2329             } else {
2330                 glyphLayoutHash.insertMulti(mainFontEngine,
2331                                             GlyphInfo(glyphLayout, pos, flags));
2332             }
2333         }
2334     }
2335
2336     QHash<QPair<QFontEngine *, int>, QGlyphs> glyphsHash;
2337
2338     QList<QFontEngine *> keys = glyphLayoutHash.uniqueKeys();
2339     for (int i=0; i<keys.size(); ++i) {
2340         QFontEngine *fontEngine = keys.at(i);
2341
2342         // Make a font for this particular engine
2343         QRawFont font;
2344         QRawFontPrivate *fontD = QRawFontPrivate::get(font);
2345         fontD->fontEngine = fontEngine;
2346         fontD->fontEngine->ref.ref();
2347
2348 #if defined(Q_WS_WIN)
2349         if (fontEngine->supportsSubPixelPositions())
2350             fontD->hintingPreference = QFont::PreferVerticalHinting;
2351         else
2352             fontD->hintingPreference = QFont::PreferFullHinting;
2353 #elif defined(Q_WS_MAC)
2354         fontD->hintingPreference = QFont::PreferNoHinting;
2355 #elif !defined(QT_NO_FREETYPE)
2356         if (fontEngine->type() == QFontEngine::Freetype) {
2357             QFontEngineFT *freeTypeEngine = static_cast<QFontEngineFT *>(fontEngine);
2358             switch (freeTypeEngine->defaultHintStyle()) {
2359             case QFontEngineFT::HintNone:
2360                 fontD->hintingPreference = QFont::PreferNoHinting;
2361                 break;
2362             case QFontEngineFT::HintLight:
2363                 fontD->hintingPreference = QFont::PreferVerticalHinting;
2364                 break;
2365             case QFontEngineFT::HintMedium:
2366             case QFontEngineFT::HintFull:
2367                 fontD->hintingPreference = QFont::PreferFullHinting;
2368                 break;
2369             };
2370         }
2371 #endif
2372
2373         QList<GlyphInfo> glyphLayouts = glyphLayoutHash.values(fontEngine);
2374         for (int j=0; j<glyphLayouts.size(); ++j) {
2375             const QPointF &pos = glyphLayouts.at(j).itemPosition;
2376             const QGlyphLayout &glyphLayout = glyphLayouts.at(j).glyphLayout;
2377             const QTextItem::RenderFlags &flags = glyphLayouts.at(j).flags;            
2378
2379             QVarLengthArray<glyph_t> glyphsArray;
2380             QVarLengthArray<QFixedPoint> positionsArray;
2381
2382             fontEngine->getGlyphPositions(glyphLayout, QTransform(), flags, glyphsArray,
2383                                           positionsArray);
2384             Q_ASSERT(glyphsArray.size() == positionsArray.size());
2385
2386             QVector<quint32> glyphs;
2387             QVector<QPointF> positions;
2388             for (int i=0; i<glyphsArray.size(); ++i) {
2389                 glyphs.append(glyphsArray.at(i) & 0xffffff);
2390                 positions.append(positionsArray.at(i).toPointF() + pos);
2391             }
2392
2393             QGlyphs glyphIndexes;
2394             glyphIndexes.setGlyphIndexes(glyphs);
2395             glyphIndexes.setPositions(positions);
2396
2397             glyphIndexes.setOverline(flags.testFlag(QTextItem::Overline));
2398             glyphIndexes.setUnderline(flags.testFlag(QTextItem::Underline));
2399             glyphIndexes.setStrikeOut(flags.testFlag(QTextItem::StrikeOut));
2400             glyphIndexes.setFont(font);
2401
2402             QPair<QFontEngine *, int> key(fontEngine, int(flags));
2403             if (!glyphsHash.contains(key))
2404                 glyphsHash.insert(key, glyphIndexes);
2405             else
2406                 glyphsHash[key] += glyphIndexes;
2407         }
2408     }
2409
2410     return glyphsHash.values();
2411 }
2412 #endif // QT_NO_RAWFONT
2413
2414 /*!
2415     \fn void QTextLine::draw(QPainter *painter, const QPointF &position, const QTextLayout::FormatRange *selection) const
2416
2417     Draws a line on the given \a painter at the specified \a position.
2418     The \a selection is reserved for internal use.
2419 */
2420 void QTextLine::draw(QPainter *p, const QPointF &pos, const QTextLayout::FormatRange *selection) const
2421 {
2422     const QScriptLine &line = eng->lines[i];
2423     QPen pen = p->pen();
2424
2425     bool noText = (selection && selection->format.property(SuppressText).toBool());
2426
2427     if (!line.length) {
2428         if (selection
2429             && selection->start <= line.from
2430             && selection->start + selection->length > line.from) {
2431
2432             const qreal lineHeight = line.height().toReal();
2433             QRectF r(pos.x() + line.x.toReal(), pos.y() + line.y.toReal(),
2434                      lineHeight / 2, QFontMetrics(eng->font()).width(QLatin1Char(' ')));
2435             setPenAndDrawBackground(p, QPen(), selection->format, r);
2436             p->setPen(pen);
2437         }
2438         return;
2439     }
2440
2441
2442     QTextLineItemIterator iterator(eng, i, pos, selection);
2443     QFixed lineBase = line.base();
2444
2445     const QFixed y = QFixed::fromReal(pos.y()) + line.y + lineBase;
2446
2447     bool suppressColors = (eng->option.flags() & QTextOption::SuppressColors);
2448     while (!iterator.atEnd()) {
2449         QScriptItem &si = iterator.next();
2450
2451         if (selection && selection->start >= 0 && iterator.isOutsideSelection())
2452             continue;
2453
2454         if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator
2455             && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators))
2456             continue;
2457
2458         QFixed itemBaseLine = y;
2459         QFont f = eng->font(si);
2460         QTextCharFormat format;
2461
2462         if (eng->hasFormats() || selection) {
2463             format = eng->format(&si);
2464             if (suppressColors) {
2465                 format.clearForeground();
2466                 format.clearBackground();
2467                 format.clearProperty(QTextFormat::TextUnderlineColor);
2468             }
2469             if (selection)
2470                 format.merge(selection->format);
2471
2472             setPenAndDrawBackground(p, pen, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(),
2473                                                            iterator.itemWidth.toReal(), line.height().toReal()));
2474
2475             QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
2476             if (valign == QTextCharFormat::AlignSuperScript || valign == QTextCharFormat::AlignSubScript) {
2477                 QFontEngine *fe = f.d->engineForScript(si.analysis.script);
2478                 QFixed height = fe->ascent() + fe->descent();
2479                 if (valign == QTextCharFormat::AlignSubScript)
2480                     itemBaseLine += height / 6;
2481                 else if (valign == QTextCharFormat::AlignSuperScript)
2482                     itemBaseLine -= height / 2;
2483             }
2484         }
2485
2486         if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2487
2488             if (eng->hasFormats()) {
2489                 p->save();
2490                 if (si.analysis.flags == QScriptAnalysis::Object && eng->block.docHandle()) {
2491                     QFixed itemY = y - si.ascent;
2492                     if (format.verticalAlignment() == QTextCharFormat::AlignTop) {
2493                         itemY = y - lineBase;
2494                     }
2495
2496                     QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal());
2497
2498                     eng->docLayout()->drawInlineObject(p, itemRect,
2499                                                        QTextInlineObject(iterator.item, eng),
2500                                                        si.position + eng->block.position(),
2501                                                        format);
2502                     if (selection) {
2503                         QBrush bg = format.brushProperty(ObjectSelectionBrush);
2504                         if (bg.style() != Qt::NoBrush) {
2505                             QColor c = bg.color();
2506                             c.setAlpha(128);
2507                             p->fillRect(itemRect, c);
2508                         }
2509                     }
2510                 } else { // si.isTab
2511                     QFont f = eng->font(si);
2512                     QTextItemInt gf(si, &f, format);
2513                     gf.chars = 0;
2514                     gf.num_chars = 0;
2515                     gf.width = iterator.itemWidth;
2516                     p->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf);
2517                     if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) {
2518                         QChar visualTab(0x2192);
2519                         int w = QFontMetrics(f).width(visualTab);
2520                         qreal x = iterator.itemWidth.toReal() - w; // Right-aligned
2521                         if (x < 0)
2522                              p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(),
2523                                                    iterator.itemWidth.toReal(), line.height().toReal()),
2524                                             Qt::IntersectClip);
2525                         else
2526                              x /= 2; // Centered
2527                         p->drawText(QPointF(iterator.x.toReal() + x,
2528                                             y.toReal()), visualTab);
2529                     }
2530
2531                 }
2532                 p->restore();
2533             }
2534
2535             continue;
2536         }
2537
2538         unsigned short *logClusters = eng->logClusters(&si);
2539         QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2540
2541         QTextItemInt gf(si, &f, format);
2542         gf.glyphs = glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart);
2543         gf.chars = eng->layoutData->string.unicode() + iterator.itemStart;
2544         gf.logClusters = logClusters + iterator.itemStart - si.position;
2545         gf.num_chars = iterator.itemEnd - iterator.itemStart;
2546         gf.width = iterator.itemWidth;
2547         gf.justified = line.justified;
2548
2549         Q_ASSERT(gf.fontEngine);
2550
2551         if (eng->underlinePositions) {
2552             // can't have selections in this case
2553             drawMenuText(p, iterator.x, itemBaseLine, si, gf, eng, iterator.itemStart, iterator.glyphsStart);
2554         } else {
2555             QPointF pos(iterator.x.toReal(), itemBaseLine.toReal());
2556             if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) {
2557                 QPainterPath path;
2558                 path.setFillRule(Qt::WindingFill);
2559
2560                 if (gf.glyphs.numGlyphs)
2561                     gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags);
2562                 if (gf.flags) {
2563                     const QFontEngine *fe = gf.fontEngine;
2564                     const qreal lw = fe->lineThickness().toReal();
2565                     if (gf.flags & QTextItem::Underline) {
2566                         qreal offs = fe->underlinePosition().toReal();
2567                         path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw);
2568                     }
2569                     if (gf.flags & QTextItem::Overline) {
2570                         qreal offs = fe->ascent().toReal() + 1;
2571                         path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
2572                     }
2573                     if (gf.flags & QTextItem::StrikeOut) {
2574                         qreal offs = fe->ascent().toReal() / 3;
2575                         path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
2576                     }
2577                 }
2578
2579                 p->save();
2580                 p->setRenderHint(QPainter::Antialiasing);
2581                 //Currently QPen with a Qt::NoPen style still returns a default
2582                 //QBrush which != Qt::NoBrush so we need this specialcase to reset it
2583                 if (p->pen().style() == Qt::NoPen)
2584                     p->setBrush(Qt::NoBrush);
2585                 else
2586                     p->setBrush(p->pen().brush());
2587
2588                 p->setPen(format.textOutline());
2589                 p->drawPath(path);
2590                 p->restore();
2591             } else {
2592                 if (noText)
2593                     gf.glyphs.numGlyphs = 0; // slightly less elegant than it should be
2594                 p->drawTextItem(pos, gf);
2595             }
2596         }
2597         if (si.analysis.flags == QScriptAnalysis::Space
2598             && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) {
2599             QBrush c = format.foreground();
2600             if (c.style() != Qt::NoBrush)
2601                 p->setPen(c.color());
2602             QChar visualSpace((ushort)0xb7);
2603             p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace);
2604             p->setPen(pen);
2605         }
2606     }
2607
2608
2609     if (eng->hasFormats())
2610         p->setPen(pen);
2611 }
2612
2613 /*!
2614     \fn int QTextLine::cursorToX(int cursorPos, Edge edge) const
2615
2616     \overload
2617 */
2618
2619 /*!
2620     Converts the cursor position \a cursorPos to the corresponding x position
2621     inside the line, taking account of the \a edge.
2622
2623     If \a cursorPos is not a valid cursor position, the nearest valid
2624     cursor position will be used instead, and cpos will be modified to
2625     point to this valid cursor position.
2626
2627     \sa xToCursor()
2628 */
2629 qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
2630 {
2631     if (!eng->layoutData)
2632         eng->itemize();
2633
2634     const QScriptLine &line = eng->lines[i];
2635
2636     QFixed x = line.x;
2637     x += alignLine(eng, line);
2638
2639     if (!i && !eng->layoutData->items.size()) {
2640         *cursorPos = 0;
2641         return x.toReal();
2642     }
2643
2644     int pos = *cursorPos;
2645     int itm;
2646     if (pos == line.from + (int)line.length) {
2647         // end of line ensure we have the last item on the line
2648         itm = eng->findItem(pos-1);
2649     }
2650     else
2651         itm = eng->findItem(pos);
2652     eng->shapeLine(line);
2653
2654     const QScriptItem *si = &eng->layoutData->items[itm];
2655     if (!si->num_glyphs)
2656         eng->shape(itm);
2657     pos -= si->position;
2658
2659     QGlyphLayout glyphs = eng->shapedGlyphs(si);
2660     unsigned short *logClusters = eng->logClusters(si);
2661     Q_ASSERT(logClusters);
2662
2663     int l = eng->length(itm);
2664     if (pos > l)
2665         pos = l;
2666     if (pos < 0)
2667         pos = 0;
2668
2669     int glyph_pos = pos == l ? si->num_glyphs : logClusters[pos];
2670     if (edge == Trailing) {
2671         // trailing edge is leading edge of next cluster
2672         while (glyph_pos < si->num_glyphs && !glyphs.attributes[glyph_pos].clusterStart)
2673             glyph_pos++;
2674     }
2675
2676     bool reverse = eng->layoutData->items[itm].analysis.bidiLevel % 2;
2677
2678     int lineEnd = line.from + line.length;
2679
2680     // add the items left of the cursor
2681
2682     int firstItem = eng->findItem(line.from);
2683     int lastItem = eng->findItem(lineEnd - 1);
2684     int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
2685
2686     QVarLengthArray<int> visualOrder(nItems);
2687     QVarLengthArray<uchar> levels(nItems);
2688     for (int i = 0; i < nItems; ++i)
2689         levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
2690     QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
2691
2692     for (int i = 0; i < nItems; ++i) {
2693         int item = visualOrder[i]+firstItem;
2694         if (item == itm)
2695             break;
2696         QScriptItem &si = eng->layoutData->items[item];
2697         if (!si.num_glyphs)
2698             eng->shape(item);
2699
2700         if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2701             x += si.width;
2702             continue;
2703         }
2704         int start = qMax(line.from, si.position);
2705         int end = qMin(lineEnd, si.position + eng->length(item));
2706
2707         logClusters = eng->logClusters(&si);
2708
2709         int gs = logClusters[start-si.position];
2710         int ge = (end == si.position + eng->length(item)) ? si.num_glyphs-1 : logClusters[end-si.position-1];
2711
2712         QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2713
2714         while (gs <= ge) {
2715             x += glyphs.effectiveAdvance(gs);
2716             ++gs;
2717         }
2718     }
2719
2720     logClusters = eng->logClusters(si);
2721     glyphs = eng->shapedGlyphs(si);
2722     if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
2723         if(pos == l)
2724             x += si->width;
2725     } else {
2726         if (reverse) {
2727             int end = qMin(lineEnd, si->position + l) - si->position;
2728             int glyph_end = end == l ? si->num_glyphs : logClusters[end];
2729             for (int i = glyph_end - 1; i >= glyph_pos; i--)
2730                 x += glyphs.effectiveAdvance(i);
2731         } else {
2732             int start = qMax(line.from - si->position, 0);
2733             int glyph_start = logClusters[start];
2734             for (int i = glyph_start; i < glyph_pos; i++)
2735                 x += glyphs.effectiveAdvance(i);
2736         }
2737         x += offsetInLigature(logClusters, glyphs, pos, line.length, glyph_pos);
2738     }
2739
2740     *cursorPos = pos + si->position;
2741     return x.toReal();
2742 }
2743
2744 /*!
2745     \fn int QTextLine::xToCursor(qreal x, CursorPosition cpos) const
2746
2747     Converts the x-coordinate \a x, to the nearest matching cursor
2748     position, depending on the cursor position type, \a cpos.
2749
2750     \sa cursorToX()
2751 */
2752 int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
2753 {
2754     QFixed x = QFixed::fromReal(_x);
2755     const QScriptLine &line = eng->lines[i];
2756
2757     if (!eng->layoutData)
2758         eng->itemize();
2759
2760     int line_length = textLength();
2761
2762     if (!line_length)
2763         return line.from;
2764
2765     int firstItem = eng->findItem(line.from);
2766     int lastItem = eng->findItem(line.from + line_length - 1);
2767     int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
2768
2769     if (!nItems)
2770         return 0;
2771
2772     x -= line.x;
2773     x -= alignLine(eng, line);
2774 //     qDebug("xToCursor: x=%f, cpos=%d", x.toReal(), cpos);
2775
2776     QVarLengthArray<int> visualOrder(nItems);
2777     QVarLengthArray<unsigned char> levels(nItems);
2778     for (int i = 0; i < nItems; ++i)
2779         levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
2780     QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
2781
2782     if (x <= 0) {
2783         // left of first item
2784         int item = visualOrder[0]+firstItem;
2785         QScriptItem &si = eng->layoutData->items[item];
2786         if (!si.num_glyphs)
2787             eng->shape(item);
2788         int pos = si.position;
2789         if (si.analysis.bidiLevel % 2)
2790             pos += eng->length(item);
2791         pos = qMax(line.from, pos);
2792         pos = qMin(line.from + line_length, pos);
2793         return pos;
2794     } else if (x < line.textWidth
2795                || (line.justified && x < line.width)) {
2796         // has to be in one of the runs
2797         QFixed pos;
2798
2799         eng->shapeLine(line);
2800         for (int i = 0; i < nItems; ++i) {
2801             int item = visualOrder[i]+firstItem;
2802             QScriptItem &si = eng->layoutData->items[item];
2803             int item_length = eng->length(item);
2804 //             qDebug("    item %d, visual %d x_remain=%f", i, item, x.toReal());
2805
2806             int start = qMax(line.from - si.position, 0);
2807             int end = qMin(line.from + line_length - si.position, item_length);
2808
2809             unsigned short *logClusters = eng->logClusters(&si);
2810
2811             int gs = logClusters[start];
2812             int ge = (end == item_length ? si.num_glyphs : logClusters[end]) - 1;
2813             QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2814
2815             QFixed item_width = 0;
2816             if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2817                 item_width = si.width;
2818             } else {
2819                 int g = gs;
2820                 while (g <= ge) {
2821                     item_width += glyphs.effectiveAdvance(g);
2822                     ++g;
2823                 }
2824             }
2825 //             qDebug("      start=%d, end=%d, gs=%d, ge=%d item_width=%f", start, end, gs, ge, item_width.toReal());
2826
2827             if (pos + item_width < x) {
2828                 pos += item_width;
2829                 continue;
2830             }
2831 //             qDebug("      inside run");
2832             if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2833                 if (cpos == QTextLine::CursorOnCharacter)
2834                     return si.position;
2835                 bool left_half = (x - pos) < item_width/2;
2836
2837                 if (bool(si.analysis.bidiLevel % 2) != left_half)
2838                     return si.position;
2839                 return si.position + 1;
2840             }
2841
2842             int glyph_pos = -1;
2843             // has to be inside run
2844             if (cpos == QTextLine::CursorOnCharacter) {
2845                 if (si.analysis.bidiLevel % 2) {
2846                     pos += item_width;
2847                     glyph_pos = gs;
2848                     while (gs <= ge) {
2849                         if (glyphs.attributes[gs].clusterStart) {
2850                             if (pos < x)
2851                                 break;
2852                             glyph_pos = gs;
2853                             break;
2854                         }
2855                         pos -= glyphs.effectiveAdvance(gs);
2856                         ++gs;
2857                     }
2858                 } else {
2859                     glyph_pos = gs;
2860                     while (gs <= ge) {
2861                         if (glyphs.attributes[gs].clusterStart) {
2862                             if (pos > x)
2863                                 break;
2864                             glyph_pos = gs;
2865                         }
2866                         pos += glyphs.effectiveAdvance(gs);
2867                         ++gs;
2868                     }
2869                 }
2870             } else {
2871                 QFixed dist = INT_MAX/256;
2872                 if (si.analysis.bidiLevel % 2) {
2873                     pos += item_width;
2874                     while (gs <= ge) {
2875                         if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
2876                             glyph_pos = gs;
2877                             dist = qAbs(x-pos);
2878                         }
2879                         pos -= glyphs.effectiveAdvance(gs);
2880                         ++gs;
2881                     }
2882                 } else {
2883                     while (gs <= ge) {
2884                         if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
2885                             glyph_pos = gs;
2886                             dist = qAbs(x-pos);
2887                         }
2888                         pos += glyphs.effectiveAdvance(gs);
2889                         ++gs;
2890                     }
2891                 }
2892                 if (qAbs(x-pos) < dist)
2893                     return si.position + end;
2894             }
2895             Q_ASSERT(glyph_pos != -1);
2896             int j;
2897             for (j = 0; j < eng->length(item); ++j)
2898                 if (logClusters[j] == glyph_pos)
2899                     break;
2900 //             qDebug("at pos %d (in run: %d)", si.position + j, j);
2901             return si.position + j;
2902         }
2903     }
2904     // right of last item
2905 //     qDebug() << "right of last";
2906     int item = visualOrder[nItems-1]+firstItem;
2907     QScriptItem &si = eng->layoutData->items[item];
2908     if (!si.num_glyphs)
2909         eng->shape(item);
2910     int pos = si.position;
2911     if (!(si.analysis.bidiLevel % 2))
2912         pos += eng->length(item);
2913     pos = qMax(line.from, pos);
2914
2915     int maxPos = line.from + line_length;
2916
2917     // except for the last line we assume that the
2918     // character between lines is a space and we want
2919     // to position the cursor to the left of that
2920     // character.
2921     // ###### breaks with japanese for example
2922     if (this->i < eng->lines.count() - 1)
2923         --maxPos;
2924
2925     pos = qMin(pos, maxPos);
2926     return pos;
2927 }
2928
2929 QT_END_NAMESPACE