util/src/gui/text/qtextlayout.cpp
changeset 7 f7bc934e204c
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
       
     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.textWidth -= qMin(QFixed(), lbh.rightBearing);
       
  1932 
       
  1933     if (line.length == 0) {
       
  1934         LB_DEBUG("no break available in line, adding temp: length %d, width %f, space: length %d, width %f",
       
  1935                lbh.tmpData.length, lbh.tmpData.textWidth.toReal(),
       
  1936                lbh.spaceData.length, lbh.spaceData.textWidth.toReal());
       
  1937         line += lbh.tmpData;
       
  1938     }
       
  1939 
       
  1940     LB_DEBUG("line length = %d, ascent=%f, descent=%f, textWidth=%f (spacew=%f)", line.length, line.ascent.toReal(),
       
  1941            line.descent.toReal(), line.textWidth.toReal(), lbh.spaceData.width.toReal());
       
  1942     LB_DEBUG("        : '%s'", eng->layoutData->string.mid(line.from, line.length).toUtf8().data());
       
  1943 
       
  1944     if (lbh.manualWrap) {
       
  1945         eng->minWidth = qMax(eng->minWidth, line.textWidth);
       
  1946         eng->maxWidth = qMax(eng->maxWidth, line.textWidth);
       
  1947     } else {
       
  1948         eng->minWidth = qMax(eng->minWidth, lbh.minw);
       
  1949         eng->maxWidth += line.textWidth;
       
  1950     }
       
  1951 
       
  1952     if (line.textWidth > 0 && item < eng->layoutData->items.size())
       
  1953         eng->maxWidth += lbh.spaceData.textWidth;
       
  1954     if (eng->option.flags() & QTextOption::IncludeTrailingSpaces)
       
  1955         line.textWidth += lbh.spaceData.textWidth;
       
  1956     line.length += lbh.spaceData.length;
       
  1957     if (lbh.spaceData.length)
       
  1958         line.hasTrailingSpaces = true;
       
  1959 
       
  1960     line.justified = false;
       
  1961     line.gridfitted = false;
       
  1962 
       
  1963     if (eng->option.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere) {
       
  1964         if ((lbh.maxGlyphs != INT_MAX && lbh.glyphCount > lbh.maxGlyphs)
       
  1965             || (lbh.maxGlyphs == INT_MAX && line.textWidth > line.width)) {
       
  1966 
       
  1967             eng->option.setWrapMode(QTextOption::WrapAnywhere);
       
  1968             line.length = 0;
       
  1969             line.textWidth = 0;
       
  1970             layout_helper(lbh.maxGlyphs);
       
  1971             eng->option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
       
  1972         }
       
  1973     }
       
  1974 }
       
  1975 
       
  1976 /*!
       
  1977     Moves the line to position \a pos.
       
  1978 */
       
  1979 void QTextLine::setPosition(const QPointF &pos)
       
  1980 {
       
  1981     eng->lines[i].x = QFixed::fromReal(pos.x());
       
  1982     eng->lines[i].y = QFixed::fromReal(pos.y());
       
  1983 }
       
  1984 
       
  1985 /*!
       
  1986     Returns the line's position relative to the text layout's position.
       
  1987 */
       
  1988 QPointF QTextLine::position() const
       
  1989 {
       
  1990     return QPointF(eng->lines[i].x.toReal(), eng->lines[i].y.toReal());
       
  1991 }
       
  1992 
       
  1993 // ### DOC: I have no idea what this means/does.
       
  1994 // You create a text layout with a string of text. Once you laid
       
  1995 // it out, it contains a number of QTextLines. from() returns the position
       
  1996 // inside the text string where this line starts. If you e.g. has a
       
  1997 // text of "This is a string", laid out into two lines (the second
       
  1998 // starting at the word 'a'), layout.lineAt(0).from() == 0 and
       
  1999 // layout.lineAt(1).from() == 8.
       
  2000 /*!
       
  2001     Returns the start of the line from the beginning of the string
       
  2002     passed to the QTextLayout.
       
  2003 */
       
  2004 int QTextLine::textStart() const
       
  2005 {
       
  2006     return eng->lines[i].from;
       
  2007 }
       
  2008 
       
  2009 /*!
       
  2010     Returns the length of the text in the line.
       
  2011 
       
  2012     \sa naturalTextWidth()
       
  2013 */
       
  2014 int QTextLine::textLength() const
       
  2015 {
       
  2016     if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators
       
  2017         && eng->block.isValid() && i == eng->lines.count()-1) {
       
  2018         return eng->lines[i].length - 1;
       
  2019     }
       
  2020     return eng->lines[i].length;
       
  2021 }
       
  2022 
       
  2023 static void drawMenuText(QPainter *p, QFixed x, QFixed y, const QScriptItem &si, QTextItemInt &gf, QTextEngine *eng,
       
  2024                          int start, int glyph_start)
       
  2025 {
       
  2026     int ge = glyph_start + gf.glyphs.numGlyphs;
       
  2027     int gs = glyph_start;
       
  2028     int end = start + gf.num_chars;
       
  2029     unsigned short *logClusters = eng->logClusters(&si);
       
  2030     QGlyphLayout glyphs = eng->shapedGlyphs(&si);
       
  2031     QFixed orig_width = gf.width;
       
  2032 
       
  2033     int *ul = eng->underlinePositions;
       
  2034     if (ul)
       
  2035         while (*ul != -1 && *ul < start)
       
  2036             ++ul;
       
  2037     bool rtl = si.analysis.bidiLevel % 2;
       
  2038     if (rtl)
       
  2039         x += si.width;
       
  2040 
       
  2041     do {
       
  2042         int gtmp = ge;
       
  2043         int stmp = end;
       
  2044         if (ul && *ul != -1 && *ul < end) {
       
  2045             stmp = *ul;
       
  2046             gtmp = logClusters[*ul-si.position];
       
  2047         }
       
  2048 
       
  2049         gf.glyphs = glyphs.mid(gs, gtmp - gs);
       
  2050         gf.num_chars = stmp - start;
       
  2051         gf.chars = eng->layoutData->string.unicode() + start;
       
  2052         QFixed w = 0;
       
  2053         while (gs < gtmp) {
       
  2054             w += glyphs.effectiveAdvance(gs);
       
  2055             ++gs;
       
  2056         }
       
  2057         start = stmp;
       
  2058         gf.width = w;
       
  2059         if (rtl)
       
  2060             x -= w;
       
  2061         if (gf.num_chars)
       
  2062             p->drawTextItem(QPointF(x.toReal(), y.toReal()), gf);
       
  2063         if (!rtl)
       
  2064             x += w;
       
  2065         if (ul && *ul != -1 && *ul < end) {
       
  2066             // draw underline
       
  2067             gtmp = (*ul == end-1) ? ge : logClusters[*ul+1-si.position];
       
  2068             ++stmp;
       
  2069             gf.glyphs = glyphs.mid(gs, gtmp - gs);
       
  2070             gf.num_chars = stmp - start;
       
  2071             gf.chars = eng->layoutData->string.unicode() + start;
       
  2072             gf.logClusters = logClusters + start - si.position;
       
  2073             w = 0;
       
  2074             while (gs < gtmp) {
       
  2075                 w += glyphs.effectiveAdvance(gs);
       
  2076                 ++gs;
       
  2077             }
       
  2078             ++start;
       
  2079             gf.width = w;
       
  2080             gf.underlineStyle = QTextCharFormat::SingleUnderline;
       
  2081             if (rtl)
       
  2082                 x -= w;
       
  2083             p->drawTextItem(QPointF(x.toReal(), y.toReal()), gf);
       
  2084             if (!rtl)
       
  2085                 x += w;
       
  2086             gf.underlineStyle = QTextCharFormat::NoUnderline;
       
  2087             ++gf.chars;
       
  2088             ++ul;
       
  2089         }
       
  2090     } while (gs < ge);
       
  2091 
       
  2092     gf.width = orig_width;
       
  2093 }
       
  2094 
       
  2095 
       
  2096 static void setPenAndDrawBackground(QPainter *p, const QPen &defaultPen, const QTextCharFormat &chf, const QRectF &r)
       
  2097 {
       
  2098     QBrush c = chf.foreground();
       
  2099     if (c.style() == Qt::NoBrush) {
       
  2100         p->setPen(defaultPen);
       
  2101     }
       
  2102 
       
  2103     QBrush bg = chf.background();
       
  2104     if (bg.style() != Qt::NoBrush && !chf.property(SuppressBackground).toBool())
       
  2105         p->fillRect(r, bg);
       
  2106     if (c.style() != Qt::NoBrush) {
       
  2107         p->setPen(QPen(c, 0));
       
  2108     }
       
  2109 
       
  2110 }
       
  2111 
       
  2112 /*!
       
  2113     \fn void QTextLine::draw(QPainter *painter, const QPointF &position, const QTextLayout::FormatRange *selection) const
       
  2114 
       
  2115     Draws a line on the given \a painter at the specified \a position.
       
  2116     The \a selection is reserved for internal use.
       
  2117 */
       
  2118 void QTextLine::draw(QPainter *p, const QPointF &pos, const QTextLayout::FormatRange *selection) const
       
  2119 {
       
  2120     const QScriptLine &line = eng->lines[i];
       
  2121     QPen pen = p->pen();
       
  2122 
       
  2123     bool noText = (selection && selection->format.property(SuppressText).toBool());
       
  2124 
       
  2125     if (!line.length) {
       
  2126         if (selection
       
  2127             && selection->start <= line.from
       
  2128             && selection->start + selection->length > line.from) {
       
  2129 
       
  2130             const qreal lineHeight = line.height().toReal();
       
  2131             QRectF r(pos.x() + line.x.toReal(), pos.y() + line.y.toReal(),
       
  2132                      lineHeight / 2, QFontMetrics(eng->font()).width(QLatin1Char(' ')));
       
  2133             setPenAndDrawBackground(p, QPen(), selection->format, r);
       
  2134             p->setPen(pen);
       
  2135         }
       
  2136         return;
       
  2137     }
       
  2138 
       
  2139 
       
  2140     QTextLineItemIterator iterator(eng, i, pos, selection);
       
  2141     QFixed lineBase = line.base();
       
  2142 
       
  2143     const QFixed y = QFixed::fromReal(pos.y()) + line.y + lineBase;
       
  2144 
       
  2145     bool suppressColors = (eng->option.flags() & QTextOption::SuppressColors);
       
  2146     while (!iterator.atEnd()) {
       
  2147         QScriptItem &si = iterator.next();
       
  2148 
       
  2149         if (selection && selection->start >= 0 && iterator.isOutsideSelection())
       
  2150             continue;
       
  2151 
       
  2152         if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator
       
  2153             && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators))
       
  2154             continue;
       
  2155 
       
  2156         QFixed itemBaseLine = y;
       
  2157         QFont f = eng->font(si);
       
  2158         QTextCharFormat format;
       
  2159 
       
  2160         if (eng->hasFormats() || selection) {
       
  2161             if (!suppressColors)
       
  2162                 format = eng->format(&si);
       
  2163             if (selection)
       
  2164                 format.merge(selection->format);
       
  2165 
       
  2166             setPenAndDrawBackground(p, pen, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(),
       
  2167                                                            iterator.itemWidth.toReal(), line.height().toReal()));
       
  2168 
       
  2169             QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
       
  2170             if (valign == QTextCharFormat::AlignSuperScript || valign == QTextCharFormat::AlignSubScript) {
       
  2171                 QFontEngine *fe = f.d->engineForScript(si.analysis.script);
       
  2172                 QFixed height = fe->ascent() + fe->descent();
       
  2173                 if (valign == QTextCharFormat::AlignSubScript)
       
  2174                     itemBaseLine += height / 6;
       
  2175                 else if (valign == QTextCharFormat::AlignSuperScript)
       
  2176                     itemBaseLine -= height / 2;
       
  2177             }
       
  2178         }
       
  2179 
       
  2180         if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
       
  2181 
       
  2182             if (eng->hasFormats()) {
       
  2183                 p->save();
       
  2184                 if (si.analysis.flags == QScriptAnalysis::Object && eng->block.docHandle()) {
       
  2185                     QFixed itemY = y - si.ascent;
       
  2186                     if (format.verticalAlignment() == QTextCharFormat::AlignTop) {
       
  2187                         itemY = y - lineBase;
       
  2188                     }
       
  2189 
       
  2190                     QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal());
       
  2191 
       
  2192                     eng->docLayout()->drawInlineObject(p, itemRect,
       
  2193                                                        QTextInlineObject(iterator.item, eng),
       
  2194                                                        si.position + eng->block.position(),
       
  2195                                                        format);
       
  2196                     if (selection) {
       
  2197                         QBrush bg = format.brushProperty(ObjectSelectionBrush);
       
  2198                         if (bg.style() != Qt::NoBrush) {
       
  2199                             QColor c = bg.color();
       
  2200                             c.setAlpha(128);
       
  2201                             p->fillRect(itemRect, c);
       
  2202                         }
       
  2203                     }
       
  2204                 } else { // si.isTab
       
  2205                     QFont f = eng->font(si);
       
  2206                     QTextItemInt gf(si, &f, format);
       
  2207                     gf.chars = 0;
       
  2208                     gf.num_chars = 0;
       
  2209                     gf.width = iterator.itemWidth;
       
  2210                     p->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf);
       
  2211                     if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) {
       
  2212                         QChar visualTab(0x2192);
       
  2213                         int w = QFontMetrics(f).width(visualTab);
       
  2214                         qreal x = iterator.itemWidth.toReal() - w; // Right-aligned
       
  2215                         if (x < 0)
       
  2216                              p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(),
       
  2217                                                    iterator.itemWidth.toReal(), line.height().toReal()),
       
  2218                                             Qt::IntersectClip);
       
  2219                         else
       
  2220                              x /= 2; // Centered
       
  2221                         p->drawText(QPointF(iterator.x.toReal() + x,
       
  2222                                             y.toReal()), visualTab);
       
  2223                     }
       
  2224 
       
  2225                 }
       
  2226                 p->restore();
       
  2227             }
       
  2228 
       
  2229             continue;
       
  2230         }
       
  2231 
       
  2232         unsigned short *logClusters = eng->logClusters(&si);
       
  2233         QGlyphLayout glyphs = eng->shapedGlyphs(&si);
       
  2234 
       
  2235         QTextItemInt gf(si, &f, format);
       
  2236         gf.glyphs = glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart);
       
  2237         gf.chars = eng->layoutData->string.unicode() + iterator.itemStart;
       
  2238         gf.logClusters = logClusters + iterator.itemStart - si.position;
       
  2239         gf.num_chars = iterator.itemEnd - iterator.itemStart;
       
  2240         gf.width = iterator.itemWidth;
       
  2241         gf.justified = line.justified;
       
  2242 
       
  2243         Q_ASSERT(gf.fontEngine);
       
  2244 
       
  2245         if (eng->underlinePositions) {
       
  2246             // can't have selections in this case
       
  2247             drawMenuText(p, iterator.x, itemBaseLine, si, gf, eng, iterator.itemStart, iterator.glyphsStart);
       
  2248         } else {
       
  2249             QPointF pos(iterator.x.toReal(), itemBaseLine.toReal());
       
  2250             if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) {
       
  2251                 QPainterPath path;
       
  2252                 path.setFillRule(Qt::WindingFill);
       
  2253 
       
  2254                 if (gf.glyphs.numGlyphs)
       
  2255                     gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags);
       
  2256                 if (gf.flags) {
       
  2257                     const QFontEngine *fe = gf.fontEngine;
       
  2258                     const qreal lw = fe->lineThickness().toReal();
       
  2259                     if (gf.flags & QTextItem::Underline) {
       
  2260                         qreal offs = fe->underlinePosition().toReal();
       
  2261                         path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw);
       
  2262                     }
       
  2263                     if (gf.flags & QTextItem::Overline) {
       
  2264                         qreal offs = fe->ascent().toReal() + 1;
       
  2265                         path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
       
  2266                     }
       
  2267                     if (gf.flags & QTextItem::StrikeOut) {
       
  2268                         qreal offs = fe->ascent().toReal() / 3;
       
  2269                         path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
       
  2270                     }
       
  2271                 }
       
  2272 
       
  2273                 p->save();
       
  2274                 p->setRenderHint(QPainter::Antialiasing);
       
  2275                 //Currently QPen with a Qt::NoPen style still returns a default
       
  2276                 //QBrush which != Qt::NoBrush so we need this specialcase to reset it
       
  2277                 if (p->pen().style() == Qt::NoPen)
       
  2278                     p->setBrush(Qt::NoBrush);
       
  2279                 else
       
  2280                     p->setBrush(p->pen().brush());
       
  2281 
       
  2282                 p->setPen(format.textOutline());
       
  2283                 p->drawPath(path);
       
  2284                 p->restore();
       
  2285             } else {
       
  2286                 if (noText)
       
  2287                     gf.glyphs.numGlyphs = 0; // slightly less elegant than it should be
       
  2288                 p->drawTextItem(pos, gf);
       
  2289             }
       
  2290         }
       
  2291         if (si.analysis.flags == QScriptAnalysis::Space
       
  2292             && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) {
       
  2293             QBrush c = format.foreground();
       
  2294             if (c.style() != Qt::NoBrush)
       
  2295                 p->setPen(c.color());
       
  2296             QChar visualSpace((ushort)0xb7);
       
  2297             p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace);
       
  2298             p->setPen(pen);
       
  2299         }
       
  2300     }
       
  2301 
       
  2302 
       
  2303     if (eng->hasFormats())
       
  2304         p->setPen(pen);
       
  2305 }
       
  2306 
       
  2307 /*!
       
  2308   \fn int QTextLine::cursorToX(int cursorPos, Edge edge) const
       
  2309 
       
  2310   \overload
       
  2311 */
       
  2312 
       
  2313 
       
  2314 /*!
       
  2315   Converts the cursor position \a cursorPos to the corresponding x position
       
  2316   inside the line, taking account of the \a edge.
       
  2317 
       
  2318   If \a cursorPos is not a valid cursor position, the nearest valid
       
  2319   cursor position will be used instead, and cpos will be modified to
       
  2320   point to this valid cursor position.
       
  2321 
       
  2322   \sa xToCursor()
       
  2323 */
       
  2324 qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
       
  2325 {
       
  2326     if (!eng->layoutData)
       
  2327         eng->itemize();
       
  2328 
       
  2329     const QScriptLine &line = eng->lines[i];
       
  2330 
       
  2331     QFixed x = line.x;
       
  2332     x += alignLine(eng, line);
       
  2333 
       
  2334     if (!i && !eng->layoutData->items.size()) {
       
  2335         *cursorPos = 0;
       
  2336         return x.toReal();
       
  2337     }
       
  2338 
       
  2339     int pos = *cursorPos;
       
  2340     int itm;
       
  2341     if (pos == line.from + (int)line.length) {
       
  2342         // end of line ensure we have the last item on the line
       
  2343         itm = eng->findItem(pos-1);
       
  2344     }
       
  2345     else
       
  2346         itm = eng->findItem(pos);
       
  2347     eng->shapeLine(line);
       
  2348 
       
  2349     const QScriptItem *si = &eng->layoutData->items[itm];
       
  2350     if (!si->num_glyphs)
       
  2351         eng->shape(itm);
       
  2352     pos -= si->position;
       
  2353 
       
  2354     QGlyphLayout glyphs = eng->shapedGlyphs(si);
       
  2355     unsigned short *logClusters = eng->logClusters(si);
       
  2356     Q_ASSERT(logClusters);
       
  2357 
       
  2358     int l = eng->length(itm);
       
  2359     if (pos > l)
       
  2360         pos = l;
       
  2361     if (pos < 0)
       
  2362         pos = 0;
       
  2363 
       
  2364     int glyph_pos = pos == l ? si->num_glyphs : logClusters[pos];
       
  2365     if (edge == Trailing) {
       
  2366         // trailing edge is leading edge of next cluster
       
  2367         while (glyph_pos < si->num_glyphs && !glyphs.attributes[glyph_pos].clusterStart)
       
  2368             glyph_pos++;
       
  2369     }
       
  2370 
       
  2371     bool reverse = eng->layoutData->items[itm].analysis.bidiLevel % 2;
       
  2372 
       
  2373     int lineEnd = line.from + line.length;
       
  2374 
       
  2375     // add the items left of the cursor
       
  2376 
       
  2377     int firstItem = eng->findItem(line.from);
       
  2378     int lastItem = eng->findItem(lineEnd - 1);
       
  2379     int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
       
  2380 
       
  2381     QVarLengthArray<int> visualOrder(nItems);
       
  2382     QVarLengthArray<uchar> levels(nItems);
       
  2383     for (int i = 0; i < nItems; ++i)
       
  2384         levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
       
  2385     QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
       
  2386 
       
  2387     for (int i = 0; i < nItems; ++i) {
       
  2388         int item = visualOrder[i]+firstItem;
       
  2389         if (item == itm)
       
  2390             break;
       
  2391         QScriptItem &si = eng->layoutData->items[item];
       
  2392         if (!si.num_glyphs)
       
  2393             eng->shape(item);
       
  2394 
       
  2395         if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
       
  2396             x += si.width;
       
  2397             continue;
       
  2398         }
       
  2399         int start = qMax(line.from, si.position);
       
  2400         int end = qMin(lineEnd, si.position + eng->length(item));
       
  2401 
       
  2402         logClusters = eng->logClusters(&si);
       
  2403 
       
  2404         int gs = logClusters[start-si.position];
       
  2405         int ge = (end == si.position + eng->length(item)) ? si.num_glyphs-1 : logClusters[end-si.position-1];
       
  2406 
       
  2407         QGlyphLayout glyphs = eng->shapedGlyphs(&si);
       
  2408 
       
  2409         while (gs <= ge) {
       
  2410             x += glyphs.effectiveAdvance(gs);
       
  2411             ++gs;
       
  2412         }
       
  2413     }
       
  2414 
       
  2415     logClusters = eng->logClusters(si);
       
  2416     glyphs = eng->shapedGlyphs(si);
       
  2417     if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
       
  2418         if(pos == l)
       
  2419             x += si->width;
       
  2420     } else {
       
  2421         int offsetInCluster = 0;
       
  2422         for (int i=pos-1; i >= 0; i--) {
       
  2423             if (logClusters[i] == glyph_pos)
       
  2424                 offsetInCluster++;
       
  2425             else
       
  2426                 break;
       
  2427         }
       
  2428 
       
  2429         if (reverse) {
       
  2430             int end = qMin(lineEnd, si->position + l) - si->position;
       
  2431             int glyph_end = end == l ? si->num_glyphs : logClusters[end];
       
  2432             for (int i = glyph_end - 1; i >= glyph_pos; i--)
       
  2433                 x += glyphs.effectiveAdvance(i);
       
  2434         } else {
       
  2435             int start = qMax(line.from - si->position, 0);
       
  2436             int glyph_start = logClusters[start];
       
  2437             for (int i = glyph_start; i < glyph_pos; i++)
       
  2438                 x += glyphs.effectiveAdvance(i);
       
  2439         }
       
  2440         if (offsetInCluster > 0) { // in the case that the offset is inside a (multi-character) glyph, interpolate the position.
       
  2441             int clusterLength = 0;
       
  2442             for (int i=pos - offsetInCluster; i < line.length; i++) {
       
  2443                 if (logClusters[i] == glyph_pos)
       
  2444                     clusterLength++;
       
  2445                 else
       
  2446                     break;
       
  2447             }
       
  2448             if (clusterLength)
       
  2449                 x+= glyphs.advances_x[glyph_pos] * offsetInCluster / clusterLength;
       
  2450         }
       
  2451     }
       
  2452 
       
  2453     *cursorPos = pos + si->position;
       
  2454     return x.toReal();
       
  2455 }
       
  2456 
       
  2457 /*!
       
  2458   \fn int QTextLine::xToCursor(qreal x, CursorPosition cpos) const
       
  2459 
       
  2460   Converts the x-coordinate \a x, to the nearest matching cursor
       
  2461   position, depending on the cursor position type, \a cpos.
       
  2462 
       
  2463   \sa cursorToX()
       
  2464 */
       
  2465 int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
       
  2466 {
       
  2467     QFixed x = QFixed::fromReal(_x);
       
  2468     const QScriptLine &line = eng->lines[i];
       
  2469 
       
  2470     if (!eng->layoutData)
       
  2471         eng->itemize();
       
  2472 
       
  2473     int line_length = textLength();
       
  2474 
       
  2475     if (!line_length)
       
  2476         return line.from;
       
  2477 
       
  2478     int firstItem = eng->findItem(line.from);
       
  2479     int lastItem = eng->findItem(line.from + line_length - 1);
       
  2480     int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
       
  2481 
       
  2482     if (!nItems)
       
  2483         return 0;
       
  2484 
       
  2485     x -= line.x;
       
  2486     x -= alignLine(eng, line);
       
  2487 //     qDebug("xToCursor: x=%f, cpos=%d", x.toReal(), cpos);
       
  2488 
       
  2489     QVarLengthArray<int> visualOrder(nItems);
       
  2490     QVarLengthArray<unsigned char> levels(nItems);
       
  2491     for (int i = 0; i < nItems; ++i)
       
  2492         levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
       
  2493     QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
       
  2494 
       
  2495     if (x <= 0) {
       
  2496         // left of first item
       
  2497         int item = visualOrder[0]+firstItem;
       
  2498         QScriptItem &si = eng->layoutData->items[item];
       
  2499         if (!si.num_glyphs)
       
  2500             eng->shape(item);
       
  2501         int pos = si.position;
       
  2502         if (si.analysis.bidiLevel % 2)
       
  2503             pos += eng->length(item);
       
  2504         pos = qMax(line.from, pos);
       
  2505         pos = qMin(line.from + line_length, pos);
       
  2506         return pos;
       
  2507     } else if (x < line.textWidth
       
  2508                || (line.justified && x < line.width)) {
       
  2509         // has to be in one of the runs
       
  2510         QFixed pos;
       
  2511 
       
  2512         eng->shapeLine(line);
       
  2513         for (int i = 0; i < nItems; ++i) {
       
  2514             int item = visualOrder[i]+firstItem;
       
  2515             QScriptItem &si = eng->layoutData->items[item];
       
  2516             int item_length = eng->length(item);
       
  2517 //             qDebug("    item %d, visual %d x_remain=%f", i, item, x.toReal());
       
  2518 
       
  2519             int start = qMax(line.from - si.position, 0);
       
  2520             int end = qMin(line.from + line_length - si.position, item_length);
       
  2521 
       
  2522             unsigned short *logClusters = eng->logClusters(&si);
       
  2523 
       
  2524             int gs = logClusters[start];
       
  2525             int ge = (end == item_length ? si.num_glyphs : logClusters[end]) - 1;
       
  2526             QGlyphLayout glyphs = eng->shapedGlyphs(&si);
       
  2527 
       
  2528             QFixed item_width = 0;
       
  2529             if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
       
  2530                 item_width = si.width;
       
  2531             } else {
       
  2532                 int g = gs;
       
  2533                 while (g <= ge) {
       
  2534                     item_width += glyphs.effectiveAdvance(g);
       
  2535                     ++g;
       
  2536                 }
       
  2537             }
       
  2538 //             qDebug("      start=%d, end=%d, gs=%d, ge=%d item_width=%f", start, end, gs, ge, item_width.toReal());
       
  2539 
       
  2540             if (pos + item_width < x) {
       
  2541                 pos += item_width;
       
  2542                 continue;
       
  2543             }
       
  2544 //             qDebug("      inside run");
       
  2545             if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
       
  2546                 if (cpos == QTextLine::CursorOnCharacter)
       
  2547                     return si.position;
       
  2548                 bool left_half = (x - pos) < item_width/2;
       
  2549 
       
  2550                 if (bool(si.analysis.bidiLevel % 2) != left_half)
       
  2551                     return si.position;
       
  2552                 return si.position + 1;
       
  2553             }
       
  2554 
       
  2555             int glyph_pos = -1;
       
  2556             // has to be inside run
       
  2557             if (cpos == QTextLine::CursorOnCharacter) {
       
  2558                 if (si.analysis.bidiLevel % 2) {
       
  2559                     pos += item_width;
       
  2560                     glyph_pos = gs;
       
  2561                     while (gs <= ge) {
       
  2562                         if (glyphs.attributes[gs].clusterStart) {
       
  2563                             if (pos < x)
       
  2564                                 break;
       
  2565                             glyph_pos = gs;
       
  2566                             break;
       
  2567                         }
       
  2568                         pos -= glyphs.effectiveAdvance(gs);
       
  2569                         ++gs;
       
  2570                     }
       
  2571                 } else {
       
  2572                     glyph_pos = gs;
       
  2573                     while (gs <= ge) {
       
  2574                         if (glyphs.attributes[gs].clusterStart) {
       
  2575                             if (pos > x)
       
  2576                                 break;
       
  2577                             glyph_pos = gs;
       
  2578                         }
       
  2579                         pos += glyphs.effectiveAdvance(gs);
       
  2580                         ++gs;
       
  2581                     }
       
  2582                 }
       
  2583             } else {
       
  2584                 QFixed dist = INT_MAX/256;
       
  2585                 if (si.analysis.bidiLevel % 2) {
       
  2586                     pos += item_width;
       
  2587                     while (gs <= ge) {
       
  2588                         if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
       
  2589                             glyph_pos = gs;
       
  2590                             dist = qAbs(x-pos);
       
  2591                         }
       
  2592                         pos -= glyphs.effectiveAdvance(gs);
       
  2593                         ++gs;
       
  2594                     }
       
  2595                 } else {
       
  2596                     while (gs <= ge) {
       
  2597                         if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
       
  2598                             glyph_pos = gs;
       
  2599                             dist = qAbs(x-pos);
       
  2600                         }
       
  2601                         pos += glyphs.effectiveAdvance(gs);
       
  2602                         ++gs;
       
  2603                     }
       
  2604                 }
       
  2605                 if (qAbs(x-pos) < dist)
       
  2606                     return si.position + end;
       
  2607             }
       
  2608             Q_ASSERT(glyph_pos != -1);
       
  2609             int j;
       
  2610             for (j = 0; j < eng->length(item); ++j)
       
  2611                 if (logClusters[j] == glyph_pos)
       
  2612                     break;
       
  2613 //             qDebug("at pos %d (in run: %d)", si.position + j, j);
       
  2614             return si.position + j;
       
  2615         }
       
  2616     }
       
  2617     // right of last item
       
  2618 //     qDebug() << "right of last";
       
  2619     int item = visualOrder[nItems-1]+firstItem;
       
  2620     QScriptItem &si = eng->layoutData->items[item];
       
  2621     if (!si.num_glyphs)
       
  2622         eng->shape(item);
       
  2623     int pos = si.position;
       
  2624     if (!(si.analysis.bidiLevel % 2))
       
  2625         pos += eng->length(item);
       
  2626     pos = qMax(line.from, pos);
       
  2627 
       
  2628     int maxPos = line.from + line_length;
       
  2629 
       
  2630     // except for the last line we assume that the
       
  2631     // character between lines is a space and we want
       
  2632     // to position the cursor to the left of that
       
  2633     // character.
       
  2634     // ###### breaks with japanese for example
       
  2635     if (this->i < eng->lines.count() - 1)
       
  2636         --maxPos;
       
  2637 
       
  2638     pos = qMin(pos, maxPos);
       
  2639     return pos;
       
  2640 }
       
  2641 
       
  2642 QT_END_NAMESPACE