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