src/gui/text/qtextlayout.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 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.ascent+si.descent+1);
       
   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     const QFixed y = QFixed::fromReal(pos.y()) + line.y + line.ascent;
       
  1075 
       
  1076     const qreal lineHeight = line.height().toReal();
       
  1077     const qreal selectionY = (y - line.ascent).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     const qreal x = position.x() + l.cursorToX(cursorPosition);
       
  1335 
       
  1336     int itm = d->findItem(cursorPosition - 1);
       
  1337     QFixed ascent = sl.ascent;
       
  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             ascent = 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.ascent - ascent).toReal();
       
  1349     bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing)
       
  1350                               && (p->transform().type() > QTransform::TxTranslate);
       
  1351     if (toggleAntialiasing)
       
  1352         p->setRenderHint(QPainter::Antialiasing);
       
  1353     p->fillRect(QRectF(x, y, qreal(width), (ascent + descent).toReal()), p->pen().brush());
       
  1354     if (toggleAntialiasing)
       
  1355         p->setRenderHint(QPainter::Antialiasing, false);
       
  1356     if (d->layoutData->hasBidi) {
       
  1357         const int arrow_extent = 4;
       
  1358         int sign = rightToLeft ? -1 : 1;
       
  1359         p->drawLine(QLineF(x, y, x + (sign * arrow_extent/2), y + arrow_extent/2));
       
  1360         p->drawLine(QLineF(x, y+arrow_extent, x + (sign * arrow_extent/2), y + arrow_extent/2));
       
  1361     }
       
  1362     return;
       
  1363 }
       
  1364 
       
  1365 /*!
       
  1366     \class QTextLine
       
  1367     \reentrant
       
  1368 
       
  1369     \brief The QTextLine class represents a line of text inside a QTextLayout.
       
  1370 
       
  1371     \ingroup richtext-processing
       
  1372 
       
  1373     A text line is usually created by QTextLayout::createLine().
       
  1374 
       
  1375     After being created, the line can be filled using the setLineWidth()
       
  1376     or setNumColumns() functions. A line has a number of attributes including the
       
  1377     rectangle it occupies, rect(), its coordinates, x() and y(), its
       
  1378     textLength(), width() and naturalTextWidth(), and its ascent() and decent()
       
  1379     relative to the text. The position of the cursor in terms of the
       
  1380     line is available from cursorToX() and its inverse from
       
  1381     xToCursor(). A line can be moved with setPosition().
       
  1382 */
       
  1383 
       
  1384 /*!
       
  1385     \enum QTextLine::Edge
       
  1386 
       
  1387     \value Leading
       
  1388     \value Trailing
       
  1389 */
       
  1390 
       
  1391 /*!
       
  1392     \enum QTextLine::CursorPosition
       
  1393 
       
  1394     \value CursorBetweenCharacters
       
  1395     \value CursorOnCharacter
       
  1396 */
       
  1397 
       
  1398 /*!
       
  1399     \fn QTextLine::QTextLine(int line, QTextEngine *e)
       
  1400     \internal
       
  1401 
       
  1402     Constructs a new text line using the line at position \a line in
       
  1403     the text engine \a e.
       
  1404 */
       
  1405 
       
  1406 /*!
       
  1407     \fn QTextLine::QTextLine()
       
  1408 
       
  1409     Creates an invalid line.
       
  1410 */
       
  1411 
       
  1412 /*!
       
  1413     \fn bool QTextLine::isValid() const
       
  1414 
       
  1415     Returns true if this text line is valid; otherwise returns false.
       
  1416 */
       
  1417 
       
  1418 /*!
       
  1419     \fn int QTextLine::lineNumber() const
       
  1420 
       
  1421     Returns the position of the line in the text engine.
       
  1422 */
       
  1423 
       
  1424 
       
  1425 /*!
       
  1426     Returns the line's bounding rectangle.
       
  1427 
       
  1428     \sa x() y() textLength() width()
       
  1429 */
       
  1430 QRectF QTextLine::rect() const
       
  1431 {
       
  1432     const QScriptLine& sl = eng->lines[i];
       
  1433     return QRectF(sl.x.toReal(), sl.y.toReal(), sl.width.toReal(), sl.height().toReal());
       
  1434 }
       
  1435 
       
  1436 /*!
       
  1437     Returns the rectangle covered by the line.
       
  1438 */
       
  1439 QRectF QTextLine::naturalTextRect() const
       
  1440 {
       
  1441     const QScriptLine& sl = eng->lines[i];
       
  1442     QFixed x = sl.x + alignLine(eng, sl);
       
  1443 
       
  1444     QFixed width = sl.textWidth;
       
  1445     if (sl.justified)
       
  1446         width = sl.width;
       
  1447 
       
  1448     return QRectF(x.toReal(), sl.y.toReal(), width.toReal(), sl.height().toReal());
       
  1449 }
       
  1450 
       
  1451 /*!
       
  1452     Returns the line's x position.
       
  1453 
       
  1454     \sa rect() y() textLength() width()
       
  1455 */
       
  1456 qreal QTextLine::x() const
       
  1457 {
       
  1458     return eng->lines[i].x.toReal();
       
  1459 }
       
  1460 
       
  1461 /*!
       
  1462     Returns the line's y position.
       
  1463 
       
  1464     \sa x() rect() textLength() width()
       
  1465 */
       
  1466 qreal QTextLine::y() const
       
  1467 {
       
  1468     return eng->lines[i].y.toReal();
       
  1469 }
       
  1470 
       
  1471 /*!
       
  1472     Returns the line's width as specified by the layout() function.
       
  1473 
       
  1474     \sa naturalTextWidth() x() y() textLength() rect()
       
  1475 */
       
  1476 qreal QTextLine::width() const
       
  1477 {
       
  1478     return eng->lines[i].width.toReal();
       
  1479 }
       
  1480 
       
  1481 
       
  1482 /*!
       
  1483     Returns the line's ascent.
       
  1484 
       
  1485     \sa descent() height()
       
  1486 */
       
  1487 qreal QTextLine::ascent() const
       
  1488 {
       
  1489     return eng->lines[i].ascent.toReal();
       
  1490 }
       
  1491 
       
  1492 /*!
       
  1493     Returns the line's descent.
       
  1494 
       
  1495     \sa ascent() height()
       
  1496 */
       
  1497 qreal QTextLine::descent() const
       
  1498 {
       
  1499     return eng->lines[i].descent.toReal();
       
  1500 }
       
  1501 
       
  1502 /*!
       
  1503     Returns the line's height. This is equal to ascent() + descent() + 1.
       
  1504 
       
  1505     \sa ascent() descent()
       
  1506 */
       
  1507 qreal QTextLine::height() const
       
  1508 {
       
  1509     return eng->lines[i].height().toReal();
       
  1510 }
       
  1511 
       
  1512 /*!
       
  1513     Returns the width of the line that is occupied by text. This is
       
  1514     always \<= to width(), and is the minimum width that could be used
       
  1515     by layout() without changing the line break position.
       
  1516 */
       
  1517 qreal QTextLine::naturalTextWidth() const
       
  1518 {
       
  1519     return eng->lines[i].textWidth.toReal();
       
  1520 }
       
  1521 
       
  1522 /*!
       
  1523     Lays out the line with the given \a width. The line is filled from
       
  1524     its starting position with as many characters as will fit into
       
  1525     the line. In case the text cannot be split at the end of the line,
       
  1526     it will be filled with additional characters to the next whitespace
       
  1527     or end of the text.
       
  1528 */
       
  1529 void QTextLine::setLineWidth(qreal width)
       
  1530 {
       
  1531     QScriptLine &line = eng->lines[i];
       
  1532     if (!eng->layoutData) {
       
  1533         qWarning("QTextLine: Can't set a line width while not layouting.");
       
  1534         return;
       
  1535     }
       
  1536 
       
  1537     if (width > QFIXED_MAX)
       
  1538         width = QFIXED_MAX;
       
  1539 
       
  1540     line.width = QFixed::fromReal(width);
       
  1541     if (line.length
       
  1542         && line.textWidth <= line.width
       
  1543         && line.from + line.length == eng->layoutData->string.length())
       
  1544         // no need to do anything if the line is already layouted and the last one. This optimisation helps
       
  1545         // when using things in a single line layout.
       
  1546         return;
       
  1547     line.length = 0;
       
  1548     line.textWidth = 0;
       
  1549 
       
  1550     layout_helper(INT_MAX);
       
  1551 }
       
  1552 
       
  1553 /*!
       
  1554     Lays out the line. The line is filled from its starting position
       
  1555     with as many characters as are specified by \a numColumns. In case
       
  1556     the text cannot be split until \a numColumns characters, the line
       
  1557     will be filled with as many characters to the next whitespace or
       
  1558     end of the text.
       
  1559 */
       
  1560 void QTextLine::setNumColumns(int numColumns)
       
  1561 {
       
  1562     QScriptLine &line = eng->lines[i];
       
  1563     line.width = QFIXED_MAX;
       
  1564     line.length = 0;
       
  1565     line.textWidth = 0;
       
  1566     layout_helper(numColumns);
       
  1567 }
       
  1568 
       
  1569 /*!
       
  1570     Lays out the line. The line is filled from its starting position
       
  1571     with as many characters as are specified by \a numColumns. In case
       
  1572     the text cannot be split until \a numColumns characters, the line
       
  1573     will be filled with as many characters to the next whitespace or
       
  1574     end of the text. The provided \a alignmentWidth is used as reference
       
  1575     width for alignment.
       
  1576 */
       
  1577 void QTextLine::setNumColumns(int numColumns, qreal alignmentWidth)
       
  1578 {
       
  1579     QScriptLine &line = eng->lines[i];
       
  1580     line.width = QFixed::fromReal(alignmentWidth);
       
  1581     line.length = 0;
       
  1582     line.textWidth = 0;
       
  1583     layout_helper(numColumns);
       
  1584 }
       
  1585 
       
  1586 #if 0
       
  1587 #define LB_DEBUG qDebug
       
  1588 #else
       
  1589 #define LB_DEBUG if (0) qDebug
       
  1590 #endif
       
  1591 
       
  1592 namespace {
       
  1593 
       
  1594     struct LineBreakHelper
       
  1595     {
       
  1596         LineBreakHelper() : glyphCount(0), maxGlyphs(0), manualWrap(false) {}
       
  1597 
       
  1598         QScriptLine tmpData;
       
  1599         QScriptLine spaceData;
       
  1600 
       
  1601         int glyphCount;
       
  1602         int maxGlyphs;
       
  1603 
       
  1604         QFixed minw;
       
  1605         QFixed softHyphenWidth;
       
  1606         QFixed rightBearing;
       
  1607 
       
  1608         bool manualWrap;
       
  1609 
       
  1610         bool checkFullOtherwiseExtend(QScriptLine &line);
       
  1611     };
       
  1612 
       
  1613 inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line)
       
  1614 {        
       
  1615     LB_DEBUG("possible break width %f, spacew=%f", tmpData.textWidth.toReal(), spaceData.textWidth.toReal());
       
  1616 
       
  1617     QFixed newWidth = line.textWidth + tmpData.textWidth + spaceData.textWidth + softHyphenWidth + rightBearing;
       
  1618     if (line.length && !manualWrap && (newWidth > line.width || glyphCount > maxGlyphs))
       
  1619         return true;
       
  1620 
       
  1621     minw = qMax(minw, tmpData.textWidth);
       
  1622     line += tmpData;
       
  1623     line.textWidth += spaceData.textWidth;
       
  1624 
       
  1625     line.length += spaceData.length;
       
  1626     tmpData.textWidth = 0;
       
  1627     tmpData.length = 0;
       
  1628     spaceData.textWidth = 0;
       
  1629     spaceData.length = 0;
       
  1630 
       
  1631     return false;
       
  1632 }
       
  1633 
       
  1634 } // anonymous namespace
       
  1635 
       
  1636 
       
  1637 static inline void addNextCluster(int &pos, int end, QScriptLine &line, int &glyphCount,
       
  1638                                   const QScriptItem &current, const unsigned short *logClusters,
       
  1639                                   const QGlyphLayout &glyphs)
       
  1640 {
       
  1641     int glyphPosition = logClusters[pos];
       
  1642     do { // got to the first next cluster
       
  1643         ++pos;
       
  1644         ++line.length;
       
  1645     } while (pos < end && logClusters[pos] == glyphPosition);
       
  1646     do { // calculate the textWidth for the rest of the current cluster.
       
  1647         line.textWidth += glyphs.advances_x[glyphPosition] * !glyphs.attributes[glyphPosition].dontPrint;
       
  1648         ++glyphPosition;
       
  1649     } while (glyphPosition < current.num_glyphs && !glyphs.attributes[glyphPosition].clusterStart);
       
  1650 
       
  1651     Q_ASSERT((pos == end && glyphPosition == current.num_glyphs) || logClusters[pos] == glyphPosition);
       
  1652 
       
  1653     ++glyphCount;
       
  1654 }
       
  1655 
       
  1656 
       
  1657 // fill QScriptLine
       
  1658 void QTextLine::layout_helper(int maxGlyphs)
       
  1659 {
       
  1660     QScriptLine &line = eng->lines[i];
       
  1661     line.length = 0;
       
  1662     line.textWidth = 0;
       
  1663     line.hasTrailingSpaces = false;
       
  1664 
       
  1665     if (!eng->layoutData->items.size() || line.from >= eng->layoutData->string.length()) {
       
  1666         line.setDefaultHeight(eng);
       
  1667         return;
       
  1668     }
       
  1669 
       
  1670     Q_ASSERT(line.from < eng->layoutData->string.length());
       
  1671 
       
  1672     LineBreakHelper lbh;
       
  1673 
       
  1674     lbh.maxGlyphs = maxGlyphs;
       
  1675 
       
  1676     QTextOption::WrapMode wrapMode = eng->option.wrapMode();
       
  1677     bool breakany = (wrapMode == QTextOption::WrapAnywhere);
       
  1678     lbh.manualWrap = (wrapMode == QTextOption::ManualWrap || wrapMode == QTextOption::NoWrap);
       
  1679 
       
  1680     // #### binary search!
       
  1681     int item = -1;
       
  1682     int newItem;
       
  1683     for (newItem = eng->layoutData->items.size()-1; newItem > 0; --newItem) {
       
  1684         if (eng->layoutData->items[newItem].position <= line.from)
       
  1685             break;
       
  1686     }
       
  1687 
       
  1688     LB_DEBUG("from: %d: item=%d, total %d, width available %f", line.from, newItem, eng->layoutData->items.size(), line.width.toReal());
       
  1689 
       
  1690     Qt::Alignment alignment = eng->option.alignment();
       
  1691 
       
  1692     const HB_CharAttributes *attributes = eng->attributes();
       
  1693     int pos = line.from;
       
  1694     int end = 0;
       
  1695     QGlyphLayout glyphs;
       
  1696     const unsigned short *logClusters = eng->layoutData->logClustersPtr;
       
  1697 
       
  1698     while (newItem < eng->layoutData->items.size()) {
       
  1699         lbh.rightBearing = 0;
       
  1700         lbh.softHyphenWidth = 0;
       
  1701         if (newItem != item) {
       
  1702             item = newItem;
       
  1703             const QScriptItem &current = eng->layoutData->items[item];
       
  1704             if (!current.num_glyphs) {
       
  1705                 eng->shape(item);
       
  1706                 attributes = eng->attributes();
       
  1707                 logClusters = eng->layoutData->logClustersPtr;
       
  1708             }
       
  1709             pos = qMax(line.from, current.position);
       
  1710             end = current.position + eng->length(item);
       
  1711             glyphs = eng->shapedGlyphs(&current);
       
  1712         }
       
  1713         const QScriptItem &current = eng->layoutData->items[item];
       
  1714 
       
  1715         lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
       
  1716         lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent);
       
  1717 
       
  1718         if (current.analysis.flags == QScriptAnalysis::Tab && (alignment & (Qt::AlignLeft | Qt::AlignRight | Qt::AlignCenter | Qt::AlignJustify))) {
       
  1719             if (lbh.checkFullOtherwiseExtend(line))
       
  1720                 goto found;
       
  1721 
       
  1722             QFixed x = line.x + line.textWidth + lbh.tmpData.textWidth + lbh.spaceData.textWidth;
       
  1723             QFixed tabWidth = eng->calculateTabWidth(item, x);
       
  1724 
       
  1725             lbh.spaceData.textWidth += tabWidth;
       
  1726             lbh.spaceData.length++;
       
  1727             newItem = item + 1;
       
  1728 
       
  1729             QFixed averageCharWidth = eng->fontEngine(current)->averageCharWidth();
       
  1730             lbh.glyphCount += qRound(tabWidth / averageCharWidth);
       
  1731 
       
  1732             if (lbh.checkFullOtherwiseExtend(line))
       
  1733                 goto found;
       
  1734         } else if (current.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator) {
       
  1735             // if the line consists only of the line separator make sure
       
  1736             // we have a sane height
       
  1737             if (!line.length && !lbh.tmpData.length)
       
  1738                 line.setDefaultHeight(eng);
       
  1739             if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
       
  1740                 addNextCluster(pos, end, lbh.tmpData, lbh.glyphCount,
       
  1741                                current, logClusters, glyphs);
       
  1742             } else {
       
  1743                 lbh.tmpData.length++;
       
  1744             }
       
  1745             line += lbh.tmpData;
       
  1746             goto found;
       
  1747         } else if (current.analysis.flags == QScriptAnalysis::Object) {
       
  1748             lbh.tmpData.length++;
       
  1749 
       
  1750             QTextFormat format = eng->formats()->format(eng->formatIndex(&eng->layoutData->items[item]));
       
  1751             if (eng->block.docHandle())
       
  1752                 eng->docLayout()->positionInlineObject(QTextInlineObject(item, eng), eng->block.position() + current.position, format);
       
  1753 
       
  1754             lbh.tmpData.textWidth += current.width;
       
  1755 
       
  1756             newItem = item + 1;
       
  1757             ++lbh.glyphCount;
       
  1758             if (lbh.checkFullOtherwiseExtend(line))
       
  1759                 goto found;
       
  1760         } else if (attributes[pos].whiteSpace) {
       
  1761             while (pos < end && attributes[pos].whiteSpace)
       
  1762                 addNextCluster(pos, end, lbh.spaceData, lbh.glyphCount,
       
  1763                                current, logClusters, glyphs);
       
  1764 
       
  1765             if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width) {
       
  1766                 lbh.spaceData.textWidth = line.width; // ignore spaces that fall out of the line.
       
  1767                 goto found;
       
  1768             }
       
  1769         } else {
       
  1770             bool sb_or_ws = false;
       
  1771             do {
       
  1772                 addNextCluster(pos, end, lbh.tmpData, lbh.glyphCount,
       
  1773                                current, logClusters, glyphs);
       
  1774 
       
  1775                 if (attributes[pos].whiteSpace || attributes[pos-1].lineBreakType != HB_NoBreak) {
       
  1776                     sb_or_ws = true;
       
  1777                     break;
       
  1778                 } else if (breakany && attributes[pos].charStop) {
       
  1779                     break;
       
  1780                 }
       
  1781             } while (pos < end);
       
  1782             lbh.minw = qMax(lbh.tmpData.textWidth, lbh.minw);
       
  1783 
       
  1784             if (pos && attributes[pos - 1].lineBreakType == HB_SoftHyphen) {
       
  1785                 // if we are splitting up a word because of
       
  1786                 // a soft hyphen then we ...
       
  1787                 //
       
  1788                 //  a) have to take the width of the soft hyphen into
       
  1789                 //     account to see if the first syllable(s) /and/
       
  1790                 //     the soft hyphen fit into the line
       
  1791                 //
       
  1792                 //  b) if we are so short of available width that the
       
  1793                 //     soft hyphen is the first breakable position, then
       
  1794                 //     we don't want to show it. However we initially
       
  1795                 //     have to take the width for it into accoun so that
       
  1796                 //     the text document layout sees the overflow and
       
  1797                 //     switch to break-anywhere mode, in which we
       
  1798                 //     want the soft-hyphen to slip into the next line
       
  1799                 //     and thus become invisible again.
       
  1800                 //
       
  1801                 if (line.length)
       
  1802                     lbh.softHyphenWidth = glyphs.advances_x[logClusters[pos - 1]];
       
  1803                 else if (breakany)
       
  1804                     lbh.tmpData.textWidth += glyphs.advances_x[logClusters[pos - 1]];
       
  1805             }
       
  1806 
       
  1807             // The actual width of the text needs to take the right bearing into account. The
       
  1808             // right bearing is left-ward, which means that if the rightmost pixel is to the right
       
  1809             // of the advance of the glyph, the bearing will be negative. We flip the sign
       
  1810             // for the code to be more readable. Logic borrowed from qfontmetrics.cpp.
       
  1811             if (pos) {
       
  1812                 QFontEngine *fontEngine = eng->fontEngine(current);
       
  1813                 glyph_t glyph = glyphs.glyphs[logClusters[pos - 1]];
       
  1814                 glyph_metrics_t gi = fontEngine->boundingBox(glyph);
       
  1815                 if (gi.isValid())
       
  1816                     lbh.rightBearing = qMax(QFixed(), -(gi.xoff - gi.x - gi.width));
       
  1817             }
       
  1818 
       
  1819             if ((sb_or_ws|breakany) && lbh.checkFullOtherwiseExtend(line)) {
       
  1820                 if (!breakany) {
       
  1821                     line.textWidth += lbh.softHyphenWidth;
       
  1822                 }
       
  1823 
       
  1824                 line.textWidth += lbh.rightBearing;
       
  1825 
       
  1826                 goto found;
       
  1827             }
       
  1828         }
       
  1829         if (pos == end)
       
  1830             newItem = item + 1;
       
  1831     }
       
  1832     LB_DEBUG("reached end of line");
       
  1833     lbh.checkFullOtherwiseExtend(line);
       
  1834     line.textWidth += lbh.rightBearing;
       
  1835 
       
  1836 found:       
       
  1837     if (line.length == 0) {
       
  1838         LB_DEBUG("no break available in line, adding temp: length %d, width %f, space: length %d, width %f",
       
  1839                lbh.tmpData.length, lbh.tmpData.textWidth.toReal(),
       
  1840                lbh.spaceData.length, lbh.spaceData.textWidth.toReal());
       
  1841         line += lbh.tmpData;
       
  1842     }
       
  1843 
       
  1844     LB_DEBUG("line length = %d, ascent=%f, descent=%f, textWidth=%f (spacew=%f)", line.length, line.ascent.toReal(),
       
  1845            line.descent.toReal(), line.textWidth.toReal(), lbh.spaceData.width.toReal());
       
  1846     LB_DEBUG("        : '%s'", eng->layoutData->string.mid(line.from, line.length).toUtf8().data());
       
  1847 
       
  1848     if (lbh.manualWrap) {
       
  1849         eng->minWidth = qMax(eng->minWidth, line.textWidth);
       
  1850         eng->maxWidth = qMax(eng->maxWidth, line.textWidth);
       
  1851     } else {
       
  1852         eng->minWidth = qMax(eng->minWidth, lbh.minw);
       
  1853         eng->maxWidth += line.textWidth;
       
  1854     }
       
  1855 
       
  1856     if (line.textWidth > 0 && item < eng->layoutData->items.size())
       
  1857         eng->maxWidth += lbh.spaceData.textWidth;
       
  1858     if (eng->option.flags() & QTextOption::IncludeTrailingSpaces)
       
  1859         line.textWidth += lbh.spaceData.textWidth;
       
  1860     line.length += lbh.spaceData.length;
       
  1861     if (lbh.spaceData.length)
       
  1862         line.hasTrailingSpaces = true;
       
  1863 
       
  1864     line.justified = false;
       
  1865     line.gridfitted = false;
       
  1866 
       
  1867     if (eng->option.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere) {
       
  1868         if ((lbh.maxGlyphs != INT_MAX && lbh.glyphCount > lbh.maxGlyphs)
       
  1869             || (lbh.maxGlyphs == INT_MAX && line.textWidth > line.width)) {
       
  1870 
       
  1871             eng->option.setWrapMode(QTextOption::WrapAnywhere);
       
  1872             line.length = 0;
       
  1873             line.textWidth = 0;
       
  1874             layout_helper(lbh.maxGlyphs);
       
  1875             eng->option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
       
  1876         }
       
  1877     }
       
  1878 }
       
  1879 
       
  1880 /*!
       
  1881     Moves the line to position \a pos.
       
  1882 */
       
  1883 void QTextLine::setPosition(const QPointF &pos)
       
  1884 {
       
  1885     eng->lines[i].x = QFixed::fromReal(pos.x());
       
  1886     eng->lines[i].y = QFixed::fromReal(pos.y());
       
  1887 }
       
  1888 
       
  1889 /*!
       
  1890     Returns the line's position relative to the text layout's position.
       
  1891 */
       
  1892 QPointF QTextLine::position() const
       
  1893 {
       
  1894     return QPointF(eng->lines[i].x.toReal(), eng->lines[i].y.toReal());
       
  1895 }
       
  1896 
       
  1897 // ### DOC: I have no idea what this means/does.
       
  1898 // You create a text layout with a string of text. Once you laid
       
  1899 // it out, it contains a number of QTextLines. from() returns the position
       
  1900 // inside the text string where this line starts. If you e.g. has a
       
  1901 // text of "This is a string", laid out into two lines (the second
       
  1902 // starting at the word 'a'), layout.lineAt(0).from() == 0 and
       
  1903 // layout.lineAt(1).from() == 8.
       
  1904 /*!
       
  1905     Returns the start of the line from the beginning of the string
       
  1906     passed to the QTextLayout.
       
  1907 */
       
  1908 int QTextLine::textStart() const
       
  1909 {
       
  1910     return eng->lines[i].from;
       
  1911 }
       
  1912 
       
  1913 /*!
       
  1914     Returns the length of the text in the line.
       
  1915 
       
  1916     \sa naturalTextWidth()
       
  1917 */
       
  1918 int QTextLine::textLength() const
       
  1919 {
       
  1920     if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators
       
  1921         && eng->block.isValid() && i == eng->lines.count()-1) {
       
  1922         return eng->lines[i].length - 1;
       
  1923     }
       
  1924     return eng->lines[i].length;
       
  1925 }
       
  1926 
       
  1927 static void drawMenuText(QPainter *p, QFixed x, QFixed y, const QScriptItem &si, QTextItemInt &gf, QTextEngine *eng,
       
  1928                          int start, int glyph_start)
       
  1929 {
       
  1930     int ge = glyph_start + gf.glyphs.numGlyphs;
       
  1931     int gs = glyph_start;
       
  1932     int end = start + gf.num_chars;
       
  1933     unsigned short *logClusters = eng->logClusters(&si);
       
  1934     QGlyphLayout glyphs = eng->shapedGlyphs(&si);
       
  1935     QFixed orig_width = gf.width;
       
  1936 
       
  1937     int *ul = eng->underlinePositions;
       
  1938     if (ul)
       
  1939         while (*ul != -1 && *ul < start)
       
  1940             ++ul;
       
  1941     bool rtl = si.analysis.bidiLevel % 2;
       
  1942     if (rtl)
       
  1943         x += si.width;
       
  1944 
       
  1945     do {
       
  1946         int gtmp = ge;
       
  1947         int stmp = end;
       
  1948         if (ul && *ul != -1 && *ul < end) {
       
  1949             stmp = *ul;
       
  1950             gtmp = logClusters[*ul-si.position];
       
  1951         }
       
  1952 
       
  1953         gf.glyphs = glyphs.mid(gs, gtmp - gs);
       
  1954         gf.num_chars = stmp - start;
       
  1955         gf.chars = eng->layoutData->string.unicode() + start;
       
  1956         QFixed w = 0;
       
  1957         while (gs < gtmp) {
       
  1958             w += glyphs.effectiveAdvance(gs);
       
  1959             ++gs;
       
  1960         }
       
  1961         start = stmp;
       
  1962         gf.width = w;
       
  1963         if (rtl)
       
  1964             x -= w;
       
  1965         if (gf.num_chars)
       
  1966             p->drawTextItem(QPointF(x.toReal(), y.toReal()), gf);
       
  1967         if (!rtl)
       
  1968             x += w;
       
  1969         if (ul && *ul != -1 && *ul < end) {
       
  1970             // draw underline
       
  1971             gtmp = (*ul == end-1) ? ge : logClusters[*ul+1-si.position];
       
  1972             ++stmp;
       
  1973             gf.glyphs = glyphs.mid(gs, gtmp - gs);
       
  1974             gf.num_chars = stmp - start;
       
  1975             gf.chars = eng->layoutData->string.unicode() + start;
       
  1976             gf.logClusters = logClusters + start - si.position;
       
  1977             w = 0;
       
  1978             while (gs < gtmp) {
       
  1979                 w += glyphs.effectiveAdvance(gs);
       
  1980                 ++gs;
       
  1981             }
       
  1982             ++start;
       
  1983             gf.width = w;
       
  1984             gf.underlineStyle = QTextCharFormat::SingleUnderline;
       
  1985             if (rtl)
       
  1986                 x -= w;
       
  1987             p->drawTextItem(QPointF(x.toReal(), y.toReal()), gf);
       
  1988             if (!rtl)
       
  1989                 x += w;
       
  1990             gf.underlineStyle = QTextCharFormat::NoUnderline;
       
  1991             ++gf.chars;
       
  1992             ++ul;
       
  1993         }
       
  1994     } while (gs < ge);
       
  1995 
       
  1996     gf.width = orig_width;
       
  1997 }
       
  1998 
       
  1999 
       
  2000 static void setPenAndDrawBackground(QPainter *p, const QPen &defaultPen, const QTextCharFormat &chf, const QRectF &r)
       
  2001 {
       
  2002     QBrush c = chf.foreground();
       
  2003     if (c.style() == Qt::NoBrush) {
       
  2004         p->setPen(defaultPen);
       
  2005     }
       
  2006 
       
  2007     QBrush bg = chf.background();
       
  2008     if (bg.style() != Qt::NoBrush && !chf.property(SuppressBackground).toBool())
       
  2009         p->fillRect(r, bg);
       
  2010     if (c.style() != Qt::NoBrush) {
       
  2011         p->setPen(QPen(c, 0));
       
  2012     }
       
  2013 
       
  2014 }
       
  2015 
       
  2016 /*!
       
  2017     \fn void QTextLine::draw(QPainter *painter, const QPointF &position, const QTextLayout::FormatRange *selection) const
       
  2018 
       
  2019     Draws a line on the given \a painter at the specified \a position.
       
  2020     The \a selection is reserved for internal use.
       
  2021 */
       
  2022 void QTextLine::draw(QPainter *p, const QPointF &pos, const QTextLayout::FormatRange *selection) const
       
  2023 {
       
  2024     const QScriptLine &line = eng->lines[i];
       
  2025     QPen pen = p->pen();
       
  2026 
       
  2027     bool noText = (selection && selection->format.property(SuppressText).toBool());
       
  2028 
       
  2029     if (!line.length) {
       
  2030         if (selection
       
  2031             && selection->start <= line.from
       
  2032             && selection->start + selection->length > line.from) {
       
  2033 
       
  2034             const qreal lineHeight = line.height().toReal();
       
  2035             QRectF r(pos.x() + line.x.toReal(), pos.y() + line.y.toReal(),
       
  2036                      lineHeight / 2, QFontMetrics(eng->font()).width(QLatin1Char(' ')));
       
  2037             setPenAndDrawBackground(p, QPen(), selection->format, r);
       
  2038             p->setPen(pen);
       
  2039         }
       
  2040         return;
       
  2041     }
       
  2042 
       
  2043 
       
  2044     QTextLineItemIterator iterator(eng, i, pos, selection);
       
  2045     const QFixed y = QFixed::fromReal(pos.y()) + line.y + line.ascent;
       
  2046 
       
  2047     bool suppressColors = (eng->option.flags() & QTextOption::SuppressColors);
       
  2048     while (!iterator.atEnd()) {
       
  2049         QScriptItem &si = iterator.next();
       
  2050 
       
  2051         if (selection && selection->start >= 0 && iterator.isOutsideSelection())
       
  2052             continue;
       
  2053 
       
  2054         if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator
       
  2055             && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators))
       
  2056             continue;
       
  2057 
       
  2058         QFixed itemBaseLine = y;
       
  2059         QFont f = eng->font(si);
       
  2060         QTextCharFormat format;
       
  2061 
       
  2062         if (eng->hasFormats() || selection) {
       
  2063             if (!suppressColors)
       
  2064                 format = eng->format(&si);
       
  2065             if (selection)
       
  2066                 format.merge(selection->format);
       
  2067 
       
  2068             setPenAndDrawBackground(p, pen, format, QRectF(iterator.x.toReal(), (y - line.ascent).toReal(),
       
  2069                                                            iterator.itemWidth.toReal(), line.height().toReal()));
       
  2070 
       
  2071             QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
       
  2072             if (valign == QTextCharFormat::AlignSuperScript || valign == QTextCharFormat::AlignSubScript) {
       
  2073                 QFontEngine *fe = f.d->engineForScript(si.analysis.script);
       
  2074                 QFixed height = fe->ascent() + fe->descent();
       
  2075                 if (valign == QTextCharFormat::AlignSubScript)
       
  2076                     itemBaseLine += height / 6;
       
  2077                 else if (valign == QTextCharFormat::AlignSuperScript)
       
  2078                     itemBaseLine -= height / 2;
       
  2079             }
       
  2080         }
       
  2081 
       
  2082         if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
       
  2083 
       
  2084             if (eng->hasFormats()) {
       
  2085                 p->save();
       
  2086                 if (si.analysis.flags == QScriptAnalysis::Object && eng->block.docHandle()) {
       
  2087                     QFixed itemY = y - si.ascent;
       
  2088                     if (format.verticalAlignment() == QTextCharFormat::AlignTop) {
       
  2089                         itemY = y - line.ascent;
       
  2090                     }
       
  2091 
       
  2092                     QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal());
       
  2093 
       
  2094                     eng->docLayout()->drawInlineObject(p, itemRect,
       
  2095                                                        QTextInlineObject(iterator.item, eng),
       
  2096                                                        si.position + eng->block.position(),
       
  2097                                                        format);
       
  2098                     if (selection) {
       
  2099                         QBrush bg = format.brushProperty(ObjectSelectionBrush);
       
  2100                         if (bg.style() != Qt::NoBrush) {
       
  2101                             QColor c = bg.color();
       
  2102                             c.setAlpha(128);
       
  2103                             p->fillRect(itemRect, c);
       
  2104                         }
       
  2105                     }
       
  2106                 } else { // si.isTab
       
  2107                     QFont f = eng->font(si);
       
  2108                     QTextItemInt gf(si, &f, format);
       
  2109                     gf.chars = 0;
       
  2110                     gf.num_chars = 0;
       
  2111                     gf.width = iterator.itemWidth;
       
  2112                     p->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf);
       
  2113                     if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) {
       
  2114                         QChar visualTab(0x2192);
       
  2115                         int w = QFontMetrics(f).width(visualTab);
       
  2116                         qreal x = iterator.itemWidth.toReal() - w; // Right-aligned
       
  2117                         if (x < 0)
       
  2118                              p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(),
       
  2119                                                    iterator.itemWidth.toReal(), line.height().toReal()),
       
  2120                                             Qt::IntersectClip);
       
  2121                         else
       
  2122                              x /= 2; // Centered
       
  2123                         p->drawText(QPointF(iterator.x.toReal() + x,
       
  2124                                             y.toReal()), visualTab);
       
  2125                     }
       
  2126 
       
  2127                 }
       
  2128                 p->restore();
       
  2129             }
       
  2130 
       
  2131             continue;
       
  2132         }
       
  2133 
       
  2134         unsigned short *logClusters = eng->logClusters(&si);
       
  2135         QGlyphLayout glyphs = eng->shapedGlyphs(&si);
       
  2136 
       
  2137         QTextItemInt gf(si, &f, format);
       
  2138         gf.glyphs = glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart);
       
  2139         gf.chars = eng->layoutData->string.unicode() + iterator.itemStart;
       
  2140         gf.logClusters = logClusters + iterator.itemStart - si.position;
       
  2141         gf.num_chars = iterator.itemEnd - iterator.itemStart;
       
  2142         gf.width = iterator.itemWidth;
       
  2143         gf.justified = line.justified;
       
  2144 
       
  2145         Q_ASSERT(gf.fontEngine);
       
  2146 
       
  2147         if (eng->underlinePositions) {
       
  2148             // can't have selections in this case
       
  2149             drawMenuText(p, iterator.x, itemBaseLine, si, gf, eng, iterator.itemStart, iterator.glyphsStart);
       
  2150         } else {
       
  2151             QPointF pos(iterator.x.toReal(), itemBaseLine.toReal());
       
  2152             if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) {
       
  2153                 QPainterPath path;
       
  2154                 path.setFillRule(Qt::WindingFill);
       
  2155 
       
  2156                 if (gf.glyphs.numGlyphs)
       
  2157                     gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags);
       
  2158                 if (gf.flags) {
       
  2159                     const QFontEngine *fe = gf.fontEngine;
       
  2160                     const qreal lw = fe->lineThickness().toReal();
       
  2161                     if (gf.flags & QTextItem::Underline) {
       
  2162                         qreal offs = fe->underlinePosition().toReal();
       
  2163                         path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw);
       
  2164                     }
       
  2165                     if (gf.flags & QTextItem::Overline) {
       
  2166                         qreal offs = fe->ascent().toReal() + 1;
       
  2167                         path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
       
  2168                     }
       
  2169                     if (gf.flags & QTextItem::StrikeOut) {
       
  2170                         qreal offs = fe->ascent().toReal() / 3;
       
  2171                         path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
       
  2172                     }
       
  2173                 }
       
  2174 
       
  2175                 p->save();
       
  2176                 p->setRenderHint(QPainter::Antialiasing);
       
  2177                 //Currently QPen with a Qt::NoPen style still returns a default
       
  2178                 //QBrush which != Qt::NoBrush so we need this specialcase to reset it
       
  2179                 if (p->pen().style() == Qt::NoPen)
       
  2180                     p->setBrush(Qt::NoBrush);
       
  2181                 else
       
  2182                     p->setBrush(p->pen().brush());
       
  2183 
       
  2184                 p->setPen(format.textOutline());
       
  2185                 p->drawPath(path);
       
  2186                 p->restore();
       
  2187             } else {
       
  2188                 if (noText)
       
  2189                     gf.glyphs.numGlyphs = 0; // slightly less elegant than it should be
       
  2190                 p->drawTextItem(pos, gf);
       
  2191             }
       
  2192         }
       
  2193         if (si.analysis.flags == QScriptAnalysis::Space
       
  2194             && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) {
       
  2195             QBrush c = format.foreground();
       
  2196             if (c.style() != Qt::NoBrush)
       
  2197                 p->setPen(c.color());
       
  2198             QChar visualSpace((ushort)0xb7);
       
  2199             p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace);
       
  2200             p->setPen(pen);
       
  2201         }
       
  2202     }
       
  2203 
       
  2204 
       
  2205     if (eng->hasFormats())
       
  2206         p->setPen(pen);
       
  2207 }
       
  2208 
       
  2209 /*!
       
  2210   \fn int QTextLine::cursorToX(int cursorPos, Edge edge) const
       
  2211 
       
  2212   \overload
       
  2213 */
       
  2214 
       
  2215 
       
  2216 /*!
       
  2217   Converts the cursor position \a cursorPos to the corresponding x position
       
  2218   inside the line, taking account of the \a edge.
       
  2219 
       
  2220   If \a cursorPos is not a valid cursor position, the nearest valid
       
  2221   cursor position will be used instead, and cpos will be modified to
       
  2222   point to this valid cursor position.
       
  2223 
       
  2224   \sa xToCursor()
       
  2225 */
       
  2226 qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
       
  2227 {
       
  2228     if (!eng->layoutData)
       
  2229         eng->itemize();
       
  2230 
       
  2231     const QScriptLine &line = eng->lines[i];
       
  2232 
       
  2233     QFixed x = line.x;
       
  2234     x += alignLine(eng, line);
       
  2235 
       
  2236     if (!i && !eng->layoutData->items.size()) {
       
  2237         *cursorPos = 0;
       
  2238         return x.toReal();
       
  2239     }
       
  2240 
       
  2241     int pos = *cursorPos;
       
  2242     int itm;
       
  2243     if (pos == line.from + (int)line.length) {
       
  2244         // end of line ensure we have the last item on the line
       
  2245         itm = eng->findItem(pos-1);
       
  2246     }
       
  2247     else
       
  2248         itm = eng->findItem(pos);
       
  2249     eng->shapeLine(line);
       
  2250 
       
  2251     const QScriptItem *si = &eng->layoutData->items[itm];
       
  2252     if (!si->num_glyphs)
       
  2253         eng->shape(itm);
       
  2254     pos -= si->position;
       
  2255 
       
  2256     QGlyphLayout glyphs = eng->shapedGlyphs(si);
       
  2257     unsigned short *logClusters = eng->logClusters(si);
       
  2258     Q_ASSERT(logClusters);
       
  2259 
       
  2260     int l = eng->length(itm);
       
  2261     if (pos > l)
       
  2262         pos = l;
       
  2263     if (pos < 0)
       
  2264         pos = 0;
       
  2265 
       
  2266     int glyph_pos = pos == l ? si->num_glyphs : logClusters[pos];
       
  2267     if (edge == Trailing) {
       
  2268         // trailing edge is leading edge of next cluster
       
  2269         while (glyph_pos < si->num_glyphs && !glyphs.attributes[glyph_pos].clusterStart)
       
  2270             glyph_pos++;
       
  2271     }
       
  2272 
       
  2273     bool reverse = eng->layoutData->items[itm].analysis.bidiLevel % 2;
       
  2274 
       
  2275     int lineEnd = line.from + line.length;
       
  2276 
       
  2277     // add the items left of the cursor
       
  2278 
       
  2279     int firstItem = eng->findItem(line.from);
       
  2280     int lastItem = eng->findItem(lineEnd - 1);
       
  2281     int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
       
  2282 
       
  2283     QVarLengthArray<int> visualOrder(nItems);
       
  2284     QVarLengthArray<uchar> levels(nItems);
       
  2285     for (int i = 0; i < nItems; ++i)
       
  2286         levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
       
  2287     QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
       
  2288 
       
  2289     for (int i = 0; i < nItems; ++i) {
       
  2290         int item = visualOrder[i]+firstItem;
       
  2291         if (item == itm)
       
  2292             break;
       
  2293         QScriptItem &si = eng->layoutData->items[item];
       
  2294         if (!si.num_glyphs)
       
  2295             eng->shape(item);
       
  2296 
       
  2297         if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
       
  2298             x += si.width;
       
  2299             continue;
       
  2300         }
       
  2301         int start = qMax(line.from, si.position);
       
  2302         int end = qMin(lineEnd, si.position + eng->length(item));
       
  2303 
       
  2304         logClusters = eng->logClusters(&si);
       
  2305 
       
  2306         int gs = logClusters[start-si.position];
       
  2307         int ge = (end == si.position + eng->length(item)) ? si.num_glyphs-1 : logClusters[end-si.position-1];
       
  2308 
       
  2309         QGlyphLayout glyphs = eng->shapedGlyphs(&si);
       
  2310 
       
  2311         while (gs <= ge) {
       
  2312             x += glyphs.effectiveAdvance(gs);
       
  2313             ++gs;
       
  2314         }
       
  2315     }
       
  2316 
       
  2317     logClusters = eng->logClusters(si);
       
  2318     glyphs = eng->shapedGlyphs(si);
       
  2319     if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
       
  2320         if(pos == l)
       
  2321             x += si->width;
       
  2322     } else {
       
  2323         int offsetInCluster = 0;
       
  2324         for (int i=pos-1; i >= 0; i--) {
       
  2325             if (logClusters[i] == glyph_pos)
       
  2326                 offsetInCluster++;
       
  2327             else
       
  2328                 break;
       
  2329         }
       
  2330 
       
  2331         if (reverse) {
       
  2332             int end = qMin(lineEnd, si->position + l) - si->position;
       
  2333             int glyph_end = end == l ? si->num_glyphs : logClusters[end];
       
  2334             for (int i = glyph_end - 1; i >= glyph_pos; i--)
       
  2335                 x += glyphs.effectiveAdvance(i);
       
  2336         } else {
       
  2337             int start = qMax(line.from - si->position, 0);
       
  2338             int glyph_start = logClusters[start];
       
  2339             for (int i = glyph_start; i < glyph_pos; i++)
       
  2340                 x += glyphs.effectiveAdvance(i);
       
  2341         }
       
  2342         if (offsetInCluster > 0) { // in the case that the offset is inside a (multi-character) glyph, interpolate the position.
       
  2343             int clusterLength = 0;
       
  2344             for (int i=pos - offsetInCluster; i < line.length; i++) {
       
  2345                 if (logClusters[i] == glyph_pos)
       
  2346                     clusterLength++;
       
  2347                 else
       
  2348                     break;
       
  2349             }
       
  2350             if (clusterLength)
       
  2351                 x+= glyphs.advances_x[glyph_pos] * offsetInCluster / clusterLength;
       
  2352         }
       
  2353     }
       
  2354 
       
  2355     *cursorPos = pos + si->position;
       
  2356     return x.toReal();
       
  2357 }
       
  2358 
       
  2359 /*!
       
  2360   \fn int QTextLine::xToCursor(qreal x, CursorPosition cpos) const
       
  2361 
       
  2362   Converts the x-coordinate \a x, to the nearest matching cursor
       
  2363   position, depending on the cursor position type, \a cpos.
       
  2364 
       
  2365   \sa cursorToX()
       
  2366 */
       
  2367 int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
       
  2368 {
       
  2369     QFixed x = QFixed::fromReal(_x);
       
  2370     const QScriptLine &line = eng->lines[i];
       
  2371 
       
  2372     if (!eng->layoutData)
       
  2373         eng->itemize();
       
  2374 
       
  2375     int line_length = textLength();
       
  2376 
       
  2377     if (!line_length)
       
  2378         return line.from;
       
  2379 
       
  2380     int firstItem = eng->findItem(line.from);
       
  2381     int lastItem = eng->findItem(line.from + line_length - 1);
       
  2382     int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
       
  2383 
       
  2384     if (!nItems)
       
  2385         return 0;
       
  2386 
       
  2387     x -= line.x;
       
  2388     x -= alignLine(eng, line);
       
  2389 //     qDebug("xToCursor: x=%f, cpos=%d", x.toReal(), cpos);
       
  2390 
       
  2391     QVarLengthArray<int> visualOrder(nItems);
       
  2392     QVarLengthArray<unsigned char> levels(nItems);
       
  2393     for (int i = 0; i < nItems; ++i)
       
  2394         levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
       
  2395     QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
       
  2396 
       
  2397     if (x <= 0) {
       
  2398         // left of first item
       
  2399         int item = visualOrder[0]+firstItem;
       
  2400         QScriptItem &si = eng->layoutData->items[item];
       
  2401         if (!si.num_glyphs)
       
  2402             eng->shape(item);
       
  2403         int pos = si.position;
       
  2404         if (si.analysis.bidiLevel % 2)
       
  2405             pos += eng->length(item);
       
  2406         pos = qMax(line.from, pos);
       
  2407         pos = qMin(line.from + line_length, pos);
       
  2408         return pos;
       
  2409     } else if (x < line.textWidth
       
  2410                || (line.justified && x < line.width)) {
       
  2411         // has to be in one of the runs
       
  2412         QFixed pos;
       
  2413 
       
  2414         eng->shapeLine(line);
       
  2415         for (int i = 0; i < nItems; ++i) {
       
  2416             int item = visualOrder[i]+firstItem;
       
  2417             QScriptItem &si = eng->layoutData->items[item];
       
  2418             int item_length = eng->length(item);
       
  2419 //             qDebug("    item %d, visual %d x_remain=%f", i, item, x.toReal());
       
  2420 
       
  2421             int start = qMax(line.from - si.position, 0);
       
  2422             int end = qMin(line.from + line_length - si.position, item_length);
       
  2423 
       
  2424             unsigned short *logClusters = eng->logClusters(&si);
       
  2425 
       
  2426             int gs = logClusters[start];
       
  2427             int ge = (end == item_length ? si.num_glyphs : logClusters[end]) - 1;
       
  2428             QGlyphLayout glyphs = eng->shapedGlyphs(&si);
       
  2429 
       
  2430             QFixed item_width = 0;
       
  2431             if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
       
  2432                 item_width = si.width;
       
  2433             } else {
       
  2434                 int g = gs;
       
  2435                 while (g <= ge) {
       
  2436                     item_width += glyphs.effectiveAdvance(g);
       
  2437                     ++g;
       
  2438                 }
       
  2439             }
       
  2440 //             qDebug("      start=%d, end=%d, gs=%d, ge=%d item_width=%f", start, end, gs, ge, item_width.toReal());
       
  2441 
       
  2442             if (pos + item_width < x) {
       
  2443                 pos += item_width;
       
  2444                 continue;
       
  2445             }
       
  2446 //             qDebug("      inside run");
       
  2447             if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
       
  2448                 if (cpos == QTextLine::CursorOnCharacter)
       
  2449                     return si.position;
       
  2450                 bool left_half = (x - pos) < item_width/2;
       
  2451 
       
  2452                 if (bool(si.analysis.bidiLevel % 2) != left_half)
       
  2453                     return si.position;
       
  2454                 return si.position + 1;
       
  2455             }
       
  2456 
       
  2457             int glyph_pos = -1;
       
  2458             // has to be inside run
       
  2459             if (cpos == QTextLine::CursorOnCharacter) {
       
  2460                 if (si.analysis.bidiLevel % 2) {
       
  2461                     pos += item_width;
       
  2462                     glyph_pos = gs;
       
  2463                     while (gs <= ge) {
       
  2464                         if (glyphs.attributes[gs].clusterStart) {
       
  2465                             if (pos < x)
       
  2466                                 break;
       
  2467                             glyph_pos = gs;
       
  2468                             break;
       
  2469                         }
       
  2470                         pos -= glyphs.effectiveAdvance(gs);
       
  2471                         ++gs;
       
  2472                     }
       
  2473                 } else {
       
  2474                     glyph_pos = gs;
       
  2475                     while (gs <= ge) {
       
  2476                         if (glyphs.attributes[gs].clusterStart) {
       
  2477                             if (pos > x)
       
  2478                                 break;
       
  2479                             glyph_pos = gs;
       
  2480                         }
       
  2481                         pos += glyphs.effectiveAdvance(gs);
       
  2482                         ++gs;
       
  2483                     }
       
  2484                 }
       
  2485             } else {
       
  2486                 QFixed dist = INT_MAX/256;
       
  2487                 if (si.analysis.bidiLevel % 2) {
       
  2488                     pos += item_width;
       
  2489                     while (gs <= ge) {
       
  2490                         if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
       
  2491                             glyph_pos = gs;
       
  2492                             dist = qAbs(x-pos);
       
  2493                         }
       
  2494                         pos -= glyphs.effectiveAdvance(gs);
       
  2495                         ++gs;
       
  2496                     }
       
  2497                 } else {
       
  2498                     while (gs <= ge) {
       
  2499                         if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
       
  2500                             glyph_pos = gs;
       
  2501                             dist = qAbs(x-pos);
       
  2502                         }
       
  2503                         pos += glyphs.effectiveAdvance(gs);
       
  2504                         ++gs;
       
  2505                     }
       
  2506                 }
       
  2507                 if (qAbs(x-pos) < dist)
       
  2508                     return si.position + end;
       
  2509             }
       
  2510             Q_ASSERT(glyph_pos != -1);
       
  2511             int j;
       
  2512             for (j = 0; j < eng->length(item); ++j)
       
  2513                 if (logClusters[j] == glyph_pos)
       
  2514                     break;
       
  2515 //             qDebug("at pos %d (in run: %d)", si.position + j, j);
       
  2516             return si.position + j;
       
  2517         }
       
  2518     }
       
  2519     // right of last item
       
  2520 //     qDebug() << "right of last";
       
  2521     int item = visualOrder[nItems-1]+firstItem;
       
  2522     QScriptItem &si = eng->layoutData->items[item];
       
  2523     if (!si.num_glyphs)
       
  2524         eng->shape(item);
       
  2525     int pos = si.position;
       
  2526     if (!(si.analysis.bidiLevel % 2))
       
  2527         pos += eng->length(item);
       
  2528     pos = qMax(line.from, pos);
       
  2529 
       
  2530     int maxPos = line.from + line_length;
       
  2531 
       
  2532     // except for the last line we assume that the
       
  2533     // character between lines is a space and we want
       
  2534     // to position the cursor to the left of that
       
  2535     // character.
       
  2536     // ###### breaks with japanese for example
       
  2537     if (this->i < eng->lines.count() - 1)
       
  2538         --maxPos;
       
  2539 
       
  2540     pos = qMin(pos, maxPos);
       
  2541     return pos;
       
  2542 }
       
  2543 
       
  2544 QT_END_NAMESPACE