Change copyrights from Nokia to Digia
[profile/ivi/qtdeclarative.git] / src / quick / items / qquicktextnode.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquicktextnode_p.h"
43
44 #include <QtQuick/qsgsimplerectnode.h>
45 #include <private/qsgadaptationlayer_p.h>
46 #include <private/qsgdistancefieldglyphnode_p.h>
47 #include <private/qquickclipnode_p.h>
48 #include <QtQuick/private/qsgcontext_p.h>
49
50 #include <QtCore/qpoint.h>
51 #include <qmath.h>
52 #include <qtextdocument.h>
53 #include <qtextlayout.h>
54 #include <qabstracttextdocumentlayout.h>
55 #include <qxmlstream.h>
56 #include <qrawfont.h>
57 #include <qtexttable.h>
58 #include <qtextlist.h>
59 #include <private/qquickstyledtext_p.h>
60 #include <private/qquicktext_p_p.h>
61 #include <private/qfont_p.h>
62 #include <private/qfontengine_p.h>
63 #include <private/qrawfont_p.h>
64 #include <private/qtextimagehandler_p.h>
65 #include <private/qtextdocumentlayout_p.h>
66 #include <qhash.h>
67
68 QT_BEGIN_NAMESPACE
69
70 /*!
71   Creates an empty QQuickTextNode
72 */
73 QQuickTextNode::QQuickTextNode(QSGContext *context, QQuickItem *ownerElement)
74     : m_context(context), m_cursorNode(0), m_ownerElement(ownerElement), m_useNativeRenderer(false)
75 {
76 #if defined(QML_RUNTIME_TESTING)
77     description = QLatin1String("text");
78 #endif
79 }
80
81 QQuickTextNode::~QQuickTextNode()
82 {
83     qDeleteAll(m_textures);
84 }
85
86 #if 0
87 void QQuickTextNode::setColor(const QColor &color)
88 {
89     if (m_usePixmapCache) {
90         setUpdateFlag(UpdateNodes);
91     } else {
92         for (QSGNode *childNode = firstChild(); childNode; childNode = childNode->nextSibling()) {
93             if (childNode->subType() == GlyphNodeSubType) {
94                 QSGGlyphNode *glyphNode = static_cast<QSGGlyphNode *>(childNode);
95                 if (glyphNode->color() == m_color)
96                     glyphNode->setColor(color);
97             } else if (childNode->subType() == SolidRectNodeSubType) {
98                 QSGSimpleRectNode *solidRectNode = static_cast<QSGSimpleRectNode *>(childNode);
99                 if (solidRectNode->color() == m_color)
100                     solidRectNode->setColor(color);
101             }
102         }
103     }
104     m_color = color;
105 }
106
107 void QQuickTextNode::setStyleColor(const QColor &styleColor)
108 {
109     if (m_textStyle != QQuickTextNode::NormalTextStyle) {
110         if (m_usePixmapCache) {
111             setUpdateFlag(UpdateNodes);
112         } else {
113             for (QSGNode *childNode = firstChild(); childNode; childNode = childNode->nextSibling()) {
114                 if (childNode->subType() == GlyphNodeSubType) {
115                     QSGGlyphNode *glyphNode = static_cast<QSGGlyphNode *>(childNode);
116                     if (glyphNode->color() == m_styleColor)
117                         glyphNode->setColor(styleColor);
118                 } else if (childNode->subType() == SolidRectNodeSubType) {
119                     QSGSimpleRectNode *solidRectNode = static_cast<QSGSimpleRectNode *>(childNode);
120                     if (solidRectNode->color() == m_styleColor)
121                         solidRectNode->setColor(styleColor);
122                 }
123             }
124         }
125     }
126     m_styleColor = styleColor;
127 }
128 #endif
129
130 QSGGlyphNode *QQuickTextNode::addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color,
131                                      QQuickText::TextStyle style, const QColor &styleColor,
132                                      QSGNode *parentNode)
133 {
134     QSGGlyphNode *node = m_useNativeRenderer
135             ? m_context->createNativeGlyphNode()
136             : m_context->createGlyphNode();
137     node->setOwnerElement(m_ownerElement);
138     node->setGlyphs(position + QPointF(0, glyphs.rawFont().ascent()), glyphs);
139     node->setStyle(style);
140     node->setStyleColor(styleColor);
141     node->setColor(color);
142     node->update();
143
144     /* We flag the geometry as static, but we never call markVertexDataDirty
145        or markIndexDataDirty on them. This is because all text nodes are
146        discarded when a change occurs. If we start appending/removing from
147        existing geometry, then we also need to start marking the geometry as
148        dirty.
149      */
150     node->geometry()->setIndexDataPattern(QSGGeometry::StaticPattern);
151     node->geometry()->setVertexDataPattern(QSGGeometry::StaticPattern);
152
153     if (parentNode == 0)
154         parentNode = this;
155     parentNode->appendChildNode(node);
156
157     return node;
158 }
159
160 void QQuickTextNode::setCursor(const QRectF &rect, const QColor &color)
161 {
162     if (m_cursorNode != 0)
163         delete m_cursorNode;
164
165     m_cursorNode = new QSGSimpleRectNode(rect, color);
166     appendChildNode(m_cursorNode);
167 }
168
169 namespace {
170
171     struct BinaryTreeNode {
172         enum SelectionState {
173             Unselected,
174             Selected
175         };
176
177         BinaryTreeNode()
178             : selectionState(Unselected)
179             , clipNode(0)
180             , decorations(QQuickTextNode::NoDecoration)
181             , ascent(0.0)
182             , leftChildIndex(-1)
183             , rightChildIndex(-1)
184         {
185
186         }
187
188         BinaryTreeNode(const QRectF &brect, const QImage &i, SelectionState selState, qreal a)
189             : boundingRect(brect)
190             , selectionState(selState)
191             , clipNode(0)
192             , decorations(QQuickTextNode::NoDecoration)
193             , image(i)
194             , ascent(a)
195             , leftChildIndex(-1)
196             , rightChildIndex(-1)
197         {
198         }
199
200         BinaryTreeNode(const QGlyphRun &g, SelectionState selState, const QRectF &brect,
201                        const QQuickTextNode::Decorations &decs, const QColor &c, const QColor &bc,
202                        const QPointF &pos, qreal a)
203             : glyphRun(g)
204             , boundingRect(brect)
205             , selectionState(selState)
206             , clipNode(0)
207             , decorations(decs)
208             , color(c)
209             , backgroundColor(bc)
210             , position(pos)
211             , ascent(a)
212             , leftChildIndex(-1)
213             , rightChildIndex(-1)
214         {
215         }
216
217         QGlyphRun glyphRun;
218         QRectF boundingRect;
219         SelectionState selectionState;
220         QQuickDefaultClipNode *clipNode;
221         QQuickTextNode::Decorations decorations;
222         QColor color;
223         QColor backgroundColor;
224         QPointF position;
225         QImage image;
226         qreal ascent;
227
228         int leftChildIndex;
229         int rightChildIndex;
230
231         static void insert(QVarLengthArray<BinaryTreeNode> *binaryTree,
232                            const QRectF &rect,
233                            const QImage &image,
234                            qreal ascent,
235                            SelectionState selectionState)
236         {
237             insert(binaryTree, BinaryTreeNode(rect, image, selectionState, ascent));
238         }
239
240         static void insert(QVarLengthArray<BinaryTreeNode> *binaryTree,
241                            const QGlyphRun &glyphRun,
242                            SelectionState selectionState,
243                            QQuickTextNode::Decorations decorations,
244                            const QColor &textColor,
245                            const QColor &backgroundColor,
246                            const QPointF &position)
247         {
248             QRectF searchRect = glyphRun.boundingRect();
249             searchRect.translate(position);
250
251             if (qFuzzyIsNull(searchRect.width()) || qFuzzyIsNull(searchRect.height()))
252                 return;
253
254             decorations |= (glyphRun.underline() ? QQuickTextNode::Underline : QQuickTextNode::NoDecoration);
255             decorations |= (glyphRun.overline()  ? QQuickTextNode::Overline  : QQuickTextNode::NoDecoration);
256             decorations |= (glyphRun.strikeOut() ? QQuickTextNode::StrikeOut : QQuickTextNode::NoDecoration);
257             decorations |= (backgroundColor.isValid() ? QQuickTextNode::Background : QQuickTextNode::NoDecoration);
258
259             qreal ascent = glyphRun.rawFont().ascent();
260             insert(binaryTree, BinaryTreeNode(glyphRun, selectionState, searchRect, decorations,
261                                               textColor, backgroundColor, position, ascent));
262         }
263
264         static void insert(QVarLengthArray<BinaryTreeNode> *binaryTree,
265                            const BinaryTreeNode &binaryTreeNode)
266         {
267             int newIndex = binaryTree->size();
268             binaryTree->append(binaryTreeNode);
269             if (newIndex == 0)
270                 return;
271
272             int searchIndex = 0;
273             forever {
274                 BinaryTreeNode *node = binaryTree->data() + searchIndex;
275                 if (binaryTreeNode.boundingRect.left() < node->boundingRect.left()) {
276                     if (node->leftChildIndex < 0) {
277                         node->leftChildIndex = newIndex;
278                         break;
279                     } else {
280                         searchIndex = node->leftChildIndex;
281                     }
282                 } else {
283                     if (node->rightChildIndex < 0) {
284                         node->rightChildIndex = newIndex;
285                         break;
286                     } else {
287                         searchIndex = node->rightChildIndex;
288                     }
289                 }
290             }
291         }
292
293         static void inOrder(const QVarLengthArray<BinaryTreeNode> &binaryTree,
294                             QVarLengthArray<int> *sortedIndexes,
295                             int currentIndex = 0)
296         {
297             Q_ASSERT(currentIndex < binaryTree.size());
298
299             const BinaryTreeNode *node = binaryTree.data() + currentIndex;
300             if (node->leftChildIndex >= 0)
301                 inOrder(binaryTree, sortedIndexes, node->leftChildIndex);
302
303             sortedIndexes->append(currentIndex);
304
305             if (node->rightChildIndex >= 0)
306                 inOrder(binaryTree, sortedIndexes, node->rightChildIndex);
307         }
308     };
309
310     // Engine that takes glyph runs as input, and produces a set of glyph nodes, clip nodes,
311     // and rectangle nodes to represent the text, decorations and selection. Will try to minimize
312     // number of nodes, and join decorations in neighbouring items
313     class SelectionEngine
314     {
315     public:
316         SelectionEngine() : m_hasSelection(false) {}
317
318         QTextLine currentLine() const { return m_currentLine; }
319
320         void setCurrentLine(const QTextLine &currentLine)
321         {
322             if (m_currentLine.isValid())
323                 processCurrentLine();
324
325             m_currentLine = currentLine;
326         }
327
328         void addBorder(const QRectF &rect, qreal border, QTextFrameFormat::BorderStyle borderStyle,
329                        const QBrush &borderBrush);
330         void addFrameDecorations(QTextDocument *document, QTextFrame *frame);
331         void addImage(const QRectF &rect, const QImage &image, qreal ascent,
332                       BinaryTreeNode::SelectionState selectionState,
333                       QTextFrameFormat::Position layoutPosition);
334         int addText(const QTextBlock &block,
335                     const QTextCharFormat &charFormat,
336                     const QColor &textColor,
337                     const QVarLengthArray<QTextLayout::FormatRange> &colorChanges,
338                     int textPos, int fragmentEnd,
339                     int selectionStart, int selectionEnd);
340         void addTextObject(const QPointF &position, const QTextCharFormat &format,
341                            BinaryTreeNode::SelectionState selectionState,
342                            QTextDocument *textDocument, int pos,
343                            QTextFrameFormat::Position layoutPosition = QTextFrameFormat::InFlow);
344         void addSelectedGlyphs(const QGlyphRun &glyphRun);
345         void addUnselectedGlyphs(const QGlyphRun &glyphRun);
346         void addGlyphsInRange(int rangeStart, int rangeEnd,
347                               const QColor &color, const QColor &backgroundColor,
348                               int selectionStart, int selectionEnd);
349         void addGlyphsForRanges(const QVarLengthArray<QTextLayout::FormatRange> &ranges,
350                                 int start, int end,
351                                 int selectionStart, int selectionEnd);
352
353         void addToSceneGraph(QQuickTextNode *parent,
354                              QQuickText::TextStyle style = QQuickText::Normal,
355                              const QColor &styleColor = QColor());
356
357         void setSelectionColor(const QColor &selectionColor)
358         {
359             m_selectionColor = selectionColor;
360         }
361
362         void setSelectedTextColor(const QColor &selectedTextColor)
363         {
364             m_selectedTextColor = selectedTextColor;
365         }
366
367         void setTextColor(const QColor &textColor)
368         {
369             m_textColor = textColor;
370         }
371
372         void setAnchorColor(const QColor &anchorColor)
373         {
374             m_anchorColor = anchorColor;
375         }
376
377         void setPosition(const QPointF &position)
378         {
379             m_position = position;
380         }
381
382     private:
383         struct TextDecoration
384         {
385             TextDecoration() : selectionState(BinaryTreeNode::Unselected) {}
386             TextDecoration(const BinaryTreeNode::SelectionState &s,
387                            const QRectF &r,
388                            const QColor &c)
389                 : selectionState(s)
390                 , rect(r)
391                 , color(c)
392             {
393             }
394
395             BinaryTreeNode::SelectionState selectionState;
396             QRectF rect;
397             QColor color;
398         };
399
400         void processCurrentLine();
401         void addTextDecorations(const QVarLengthArray<TextDecoration> &textDecorations,
402                                 qreal offset, qreal thickness);
403
404         QColor m_selectionColor;
405         QColor m_textColor;
406         QColor m_backgroundColor;
407         QColor m_selectedTextColor;
408         QColor m_anchorColor;
409         QPointF m_position;
410
411         QTextLine m_currentLine;
412         bool m_hasSelection;
413
414         QList<QPair<QRectF, QColor> > m_backgrounds;
415         QList<QRectF> m_selectionRects;
416         QVarLengthArray<BinaryTreeNode> m_currentLineTree;
417
418         QList<TextDecoration> m_lines;
419         QVector<BinaryTreeNode> m_processedNodes;
420
421         QList<QPair<QRectF, QImage> > m_images;
422     };
423
424     int SelectionEngine::addText(const QTextBlock &block,
425                                  const QTextCharFormat &charFormat,
426                                  const QColor &textColor,
427                                  const QVarLengthArray<QTextLayout::FormatRange> &colorChanges,
428                                  int textPos, int fragmentEnd,
429                                  int selectionStart, int selectionEnd)
430     {
431         if (charFormat.foreground().style() != Qt::NoBrush)
432             setTextColor(charFormat.foreground().color());
433         else
434             setTextColor(textColor);
435
436         while (textPos < fragmentEnd) {
437             int blockRelativePosition = textPos - block.position();
438             QTextLine line = block.layout()->lineForTextPosition(blockRelativePosition);
439             if (!currentLine().isValid()
440                 || line.lineNumber() != currentLine().lineNumber()) {
441                 setCurrentLine(line);
442             }
443
444             Q_ASSERT(line.textLength() > 0);
445             int lineEnd = line.textStart() + block.position() + line.textLength();
446
447             int len = qMin(lineEnd - textPos, fragmentEnd - textPos);
448             Q_ASSERT(len > 0);
449
450             int currentStepEnd = textPos + len;
451
452             addGlyphsForRanges(colorChanges,
453                                textPos - block.position(),
454                                currentStepEnd - block.position(),
455                                selectionStart - block.position(),
456                                selectionEnd - block.position());
457
458             textPos = currentStepEnd;
459         }
460         return textPos;
461     }
462
463     void SelectionEngine::addTextDecorations(const QVarLengthArray<TextDecoration> &textDecorations,
464                                              qreal offset, qreal thickness)
465     {
466         for (int i=0; i<textDecorations.size(); ++i) {
467             TextDecoration textDecoration = textDecorations.at(i);
468
469             {
470                 QRectF &rect = textDecoration.rect;
471                 rect.setY(qRound(rect.y() + m_currentLine.ascent() + offset));
472                 rect.setHeight(thickness);
473             }
474
475             m_lines.append(textDecoration);
476         }
477     }
478
479     void SelectionEngine::processCurrentLine()
480     {
481         // No glyphs, do nothing
482         if (m_currentLineTree.isEmpty())
483             return;
484
485         // 1. Go through current line and get correct decoration position for each node based on
486         // neighbouring decorations. Add decoration to global list
487         // 2. Create clip nodes for all selected text. Try to merge as many as possible within
488         // the line.
489         // 3. Add QRects to a list of selection rects.
490         // 4. Add all nodes to a global processed list
491         QVarLengthArray<int> sortedIndexes; // Indexes in tree sorted by x position
492         BinaryTreeNode::inOrder(m_currentLineTree, &sortedIndexes);
493
494         Q_ASSERT(sortedIndexes.size() == m_currentLineTree.size());
495
496         BinaryTreeNode::SelectionState currentSelectionState = BinaryTreeNode::Unselected;
497         QRectF currentRect;
498
499         QQuickTextNode::Decorations currentDecorations = QQuickTextNode::NoDecoration;
500         qreal underlineOffset = 0.0;
501         qreal underlineThickness = 0.0;
502
503         qreal overlineOffset = 0.0;
504         qreal overlineThickness = 0.0;
505
506         qreal strikeOutOffset = 0.0;
507         qreal strikeOutThickness = 0.0;
508
509         QRectF decorationRect = currentRect;
510
511         QColor lastColor;
512         QColor lastBackgroundColor;
513
514         QVarLengthArray<TextDecoration> pendingUnderlines;
515         QVarLengthArray<TextDecoration> pendingOverlines;
516         QVarLengthArray<TextDecoration> pendingStrikeOuts;
517         if (!sortedIndexes.isEmpty()) {
518             QQuickDefaultClipNode *currentClipNode = m_hasSelection ? new QQuickDefaultClipNode(QRectF()) : 0;
519             bool currentClipNodeUsed = false;
520             for (int i=0; i<=sortedIndexes.size(); ++i) {
521                 BinaryTreeNode *node = 0;
522                 if (i < sortedIndexes.size()) {
523                     int sortedIndex = sortedIndexes.at(i);
524                     Q_ASSERT(sortedIndex < m_currentLineTree.size());
525
526                     node = m_currentLineTree.data() + sortedIndex;
527                 }
528
529                 if (i == 0)
530                     currentSelectionState = node->selectionState;
531
532                 // Update decorations
533                 if (currentDecorations != QQuickTextNode::NoDecoration) {
534                     decorationRect.setY(m_position.y() + m_currentLine.y());
535                     decorationRect.setHeight(m_currentLine.height());
536
537                     if (node != 0)
538                         decorationRect.setRight(node->boundingRect.left());
539
540                     TextDecoration textDecoration(currentSelectionState, decorationRect, lastColor);
541                     if (currentDecorations & QQuickTextNode::Underline)
542                         pendingUnderlines.append(textDecoration);
543
544                     if (currentDecorations & QQuickTextNode::Overline)
545                         pendingOverlines.append(textDecoration);
546
547                     if (currentDecorations & QQuickTextNode::StrikeOut)
548                         pendingStrikeOuts.append(textDecoration);
549
550                     if (currentDecorations & QQuickTextNode::Background)
551                         m_backgrounds.append(qMakePair(decorationRect, lastBackgroundColor));
552                 }
553
554                 // If we've reached an unselected node from a selected node, we add the
555                 // selection rect to the graph, and we add decoration every time the
556                 // selection state changes, because that means the text color changes
557                 if (node == 0 || node->selectionState != currentSelectionState) {
558                     if (node != 0)
559                         currentRect.setRight(node->boundingRect.left());
560                     currentRect.setY(m_position.y() + m_currentLine.y());
561                     currentRect.setHeight(m_currentLine.height());
562
563                     // Draw selection all the way up to the left edge of the unselected item
564                     if (currentSelectionState == BinaryTreeNode::Selected)
565                         m_selectionRects.append(currentRect);
566
567                     if (currentClipNode != 0) {
568                         if (!currentClipNodeUsed) {
569                             delete currentClipNode;
570                         } else {
571                             currentClipNode->setIsRectangular(true);
572                             currentClipNode->setRect(currentRect);
573                             currentClipNode->update();
574                         }
575                     }
576
577                     if (node != 0 && m_hasSelection)
578                         currentClipNode = new QQuickDefaultClipNode(QRectF());
579                     else
580                         currentClipNode = 0;
581                     currentClipNodeUsed = false;
582
583                     if (node != 0) {
584                         currentSelectionState = node->selectionState;
585                         currentRect = node->boundingRect;
586
587                         // Make sure currentRect is valid, otherwise the unite won't work
588                         if (currentRect.isNull())
589                             currentRect.setSize(QSizeF(1, 1));
590                     }
591                 } else {
592                     if (currentRect.isNull())
593                         currentRect = node->boundingRect;
594                     else
595                         currentRect = currentRect.united(node->boundingRect);
596                 }
597
598                 if (node != 0) {
599                     node->clipNode = currentClipNode;
600                     currentClipNodeUsed = true;
601
602                     decorationRect = node->boundingRect;
603
604                     // If previous item(s) had underline and current does not, then we add the
605                     // pending lines to the lists and likewise for overlines and strikeouts
606                     if (!pendingUnderlines.isEmpty()
607                       && !(node->decorations & QQuickTextNode::Underline)) {
608                         addTextDecorations(pendingUnderlines, underlineOffset, underlineThickness);
609
610                         pendingUnderlines.clear();
611
612                         underlineOffset = 0.0;
613                         underlineThickness = 0.0;
614                     }
615
616                     // ### Add pending when overlineOffset/thickness changes to minimize number of
617                     // nodes
618                     if (!pendingOverlines.isEmpty()) {
619                         addTextDecorations(pendingOverlines, overlineOffset, overlineThickness);
620
621                         pendingOverlines.clear();
622
623                         overlineOffset = 0.0;
624                         overlineThickness = 0.0;
625                     }
626
627                     // ### Add pending when overlineOffset/thickness changes to minimize number of
628                     // nodes
629                     if (!pendingStrikeOuts.isEmpty()) {
630                         addTextDecorations(pendingStrikeOuts, strikeOutOffset, strikeOutThickness);
631
632                         pendingStrikeOuts.clear();
633
634                         strikeOutOffset = 0.0;
635                         strikeOutThickness = 0.0;
636                     }
637
638                     // Merge current values with previous. Prefer greatest thickness
639                     QRawFont rawFont = node->glyphRun.rawFont();
640                     if (node->decorations & QQuickTextNode::Underline) {
641                         if (rawFont.lineThickness() > underlineThickness) {
642                             underlineThickness = rawFont.lineThickness();
643                             underlineOffset = rawFont.underlinePosition();
644                         }
645                     }
646
647                     if (node->decorations & QQuickTextNode::Overline) {
648                         overlineOffset = -rawFont.ascent();
649                         overlineThickness = rawFont.lineThickness();
650                     }
651
652                     if (node->decorations & QQuickTextNode::StrikeOut) {
653                         strikeOutThickness = rawFont.lineThickness();
654                         strikeOutOffset = rawFont.ascent() / -3.0;
655                     }
656
657                     currentDecorations = node->decorations;
658                     lastColor = node->color;
659                     lastBackgroundColor = node->backgroundColor;
660                     m_processedNodes.append(*node);
661                 }
662             }
663
664             if (!pendingUnderlines.isEmpty())
665                 addTextDecorations(pendingUnderlines, underlineOffset, underlineThickness);
666
667             if (!pendingOverlines.isEmpty())
668                 addTextDecorations(pendingOverlines, overlineOffset, overlineThickness);
669
670             if (!pendingStrikeOuts.isEmpty())
671                 addTextDecorations(pendingStrikeOuts, strikeOutOffset, strikeOutThickness);
672         }
673
674         m_currentLineTree.clear();
675         m_currentLine = QTextLine();
676         m_hasSelection = false;
677     }
678
679     void SelectionEngine::addImage(const QRectF &rect, const QImage &image, qreal ascent,
680                                    BinaryTreeNode::SelectionState selectionState,
681                                    QTextFrameFormat::Position layoutPosition)
682     {
683         QRectF searchRect = rect;
684         if (layoutPosition == QTextFrameFormat::InFlow) {
685             if (m_currentLineTree.isEmpty()) {
686                 searchRect.moveTopLeft(m_position + m_currentLine.position());
687             } else {
688                 const BinaryTreeNode *lastNode = m_currentLineTree.data() + m_currentLineTree.size() - 1;
689                 if (lastNode->glyphRun.isRightToLeft()) {
690                     QPointF lastPos = lastNode->boundingRect.topLeft();
691                     searchRect.moveTopRight(lastPos - QPointF(0, ascent - lastNode->ascent));
692                 } else {
693                     QPointF lastPos = lastNode->boundingRect.topRight();
694                     searchRect.moveTopLeft(lastPos - QPointF(0, ascent - lastNode->ascent));
695                 }
696             }
697         }
698
699         BinaryTreeNode::insert(&m_currentLineTree, searchRect, image, ascent, selectionState);
700     }
701
702     void SelectionEngine::addTextObject(const QPointF &position, const QTextCharFormat &format,
703                                         BinaryTreeNode::SelectionState selectionState,
704                                         QTextDocument *textDocument, int pos,
705                                         QTextFrameFormat::Position layoutPosition)
706     {
707         QTextObjectInterface *handler = textDocument->documentLayout()->handlerForObject(format.objectType());
708         if (handler != 0) {
709             QImage image;
710             QSizeF size = handler->intrinsicSize(textDocument, pos, format);
711
712             if (format.objectType() == QTextFormat::ImageObject) {
713                 QTextImageFormat imageFormat = format.toImageFormat();
714                 if (QQuickTextDocumentWithImageResources *imageDoc = qobject_cast<QQuickTextDocumentWithImageResources *>(textDocument)) {
715                     image = imageDoc->image(imageFormat);
716
717                     if (image.isNull())
718                         return;
719                 } else {
720                     QTextImageHandler *imageHandler = static_cast<QTextImageHandler *>(handler);
721                     image = imageHandler->image(textDocument, imageFormat);
722                 }
723             }
724
725             if (image.isNull()) {
726                 image = QImage(size.toSize(), QImage::Format_ARGB32_Premultiplied);
727                 image.fill(Qt::transparent);
728                 {
729                     QPainter painter(&image);
730                     handler->drawObject(&painter, image.rect(), textDocument, pos, format);
731                 }
732             }
733
734             qreal ascent;
735             QFontMetrics m(format.font());
736             switch (format.verticalAlignment())
737             {
738             case QTextCharFormat::AlignMiddle:
739                 ascent = size.height() / 2 - 1;
740                 break;
741             case QTextCharFormat::AlignBaseline:
742                 ascent = size.height() - m.descent() - 1;
743                 break;
744             default:
745                 ascent = size.height() - 1;
746             }
747
748             addImage(QRectF(position, size), image, ascent, selectionState, layoutPosition);
749         }
750     }
751
752     void SelectionEngine::addUnselectedGlyphs(const QGlyphRun &glyphRun)
753     {
754         BinaryTreeNode::insert(&m_currentLineTree, glyphRun, BinaryTreeNode::Unselected,
755                                QQuickTextNode::NoDecoration, m_textColor, m_backgroundColor, m_position);
756     }
757
758     void SelectionEngine::addSelectedGlyphs(const QGlyphRun &glyphRun)
759     {
760         int currentSize = m_currentLineTree.size();
761         BinaryTreeNode::insert(&m_currentLineTree, glyphRun, BinaryTreeNode::Selected,
762                                QQuickTextNode::NoDecoration, m_textColor, m_backgroundColor, m_position);
763         m_hasSelection = m_hasSelection || m_currentLineTree.size() > currentSize;
764     }
765
766     void SelectionEngine::addGlyphsForRanges(const QVarLengthArray<QTextLayout::FormatRange> &ranges,
767                                              int start, int end,
768                                              int selectionStart, int selectionEnd)
769     {
770         int currentPosition = start;
771         int remainingLength = end - start;
772         for (int j=0; j<ranges.size(); ++j) {
773             const QTextLayout::FormatRange &range = ranges.at(j);
774             if (range.start + range.length >= currentPosition
775                 && range.start < currentPosition + remainingLength) {
776
777                 if (range.start > currentPosition) {
778                     addGlyphsInRange(currentPosition, range.start - currentPosition,
779                                      QColor(), QColor(), selectionStart, selectionEnd);
780                 }
781                 int rangeEnd = qMin(range.start + range.length, currentPosition + remainingLength);
782                 QColor rangeColor;
783                 if (range.format.hasProperty(QTextFormat::ForegroundBrush))
784                     rangeColor = range.format.foreground().color();
785                 else if (range.format.isAnchor())
786                     rangeColor = m_anchorColor;
787                 QColor rangeBackgroundColor = range.format.hasProperty(QTextFormat::BackgroundBrush)
788                         ? range.format.background().color()
789                         : QColor();
790
791                 addGlyphsInRange(range.start, rangeEnd - range.start,
792                                  rangeColor, rangeBackgroundColor,
793                                  selectionStart, selectionEnd);
794
795                 currentPosition = range.start + range.length;
796                 remainingLength = end - currentPosition;
797
798             } else if (range.start > currentPosition + remainingLength || remainingLength <= 0) {
799                 break;
800             }
801         }
802
803         if (remainingLength > 0) {
804             addGlyphsInRange(currentPosition, remainingLength, QColor(), QColor(),
805                              selectionStart, selectionEnd);
806         }
807
808     }
809
810     void SelectionEngine::addGlyphsInRange(int rangeStart, int rangeLength,
811                                            const QColor &color, const QColor &backgroundColor,
812                                            int selectionStart, int selectionEnd)
813     {
814         QColor oldColor;
815         if (color.isValid()) {
816             oldColor = m_textColor;
817             m_textColor = color;
818         }
819
820         QColor oldBackgroundColor = m_backgroundColor;
821         if (backgroundColor.isValid()) {
822             oldBackgroundColor = m_backgroundColor;
823             m_backgroundColor = backgroundColor;
824         }
825
826         bool hasSelection = selectionEnd >= 0
827                          && selectionStart <= selectionEnd;
828
829         QTextLine &line = m_currentLine;
830         int rangeEnd = rangeStart + rangeLength;
831         if (!hasSelection || (selectionStart > rangeEnd || selectionEnd < rangeStart)) {
832             QList<QGlyphRun> glyphRuns = line.glyphRuns(rangeStart, rangeLength);
833             for (int j=0; j<glyphRuns.size(); ++j) {
834                 const QGlyphRun &glyphRun = glyphRuns.at(j);
835                 addUnselectedGlyphs(glyphRun);
836             }
837         } else {
838             if (rangeStart < selectionStart) {
839                 QList<QGlyphRun> glyphRuns = line.glyphRuns(rangeStart,
840                                                             qMin(selectionStart - rangeStart,
841                                                                  rangeLength));
842
843                 for (int j=0; j<glyphRuns.size(); ++j) {
844                     const QGlyphRun &glyphRun = glyphRuns.at(j);
845                     addUnselectedGlyphs(glyphRun);
846                 }
847             }
848
849             if (rangeEnd > selectionStart) {
850                 int start = qMax(selectionStart, rangeStart);
851                 int length = qMin(selectionEnd - start + 1, rangeEnd - start);
852                 QList<QGlyphRun> glyphRuns = line.glyphRuns(start, length);
853
854                 for (int j=0; j<glyphRuns.size(); ++j) {
855                     const QGlyphRun &glyphRun = glyphRuns.at(j);
856                     addSelectedGlyphs(glyphRun);
857                 }
858             }
859
860             if (selectionEnd >= rangeStart && selectionEnd < rangeEnd) {
861                 QList<QGlyphRun> glyphRuns = line.glyphRuns(selectionEnd + 1, rangeEnd - selectionEnd - 1);
862                 for (int j=0; j<glyphRuns.size(); ++j) {
863                     const QGlyphRun &glyphRun = glyphRuns.at(j);
864                     addUnselectedGlyphs(glyphRun);
865                 }
866             }
867         }
868
869         if (backgroundColor.isValid())
870             m_backgroundColor = oldBackgroundColor;
871
872         if (oldColor.isValid())
873             m_textColor = oldColor;
874     }
875
876     void SelectionEngine::addBorder(const QRectF &rect, qreal border,
877                                     QTextFrameFormat::BorderStyle borderStyle,
878                                     const QBrush &borderBrush)
879     {
880         QColor color = borderBrush.color();
881
882         // Currently we don't support other styles than solid
883         Q_UNUSED(borderStyle);
884
885         m_backgrounds.append(qMakePair(QRectF(rect.left(), rect.top(), border, rect.height() + border), color));
886         m_backgrounds.append(qMakePair(QRectF(rect.left() + border, rect.top(), rect.width(), border), color));
887         m_backgrounds.append(qMakePair(QRectF(rect.right(), rect.top() + border, border, rect.height() - border), color));
888         m_backgrounds.append(qMakePair(QRectF(rect.left() + border, rect.bottom(), rect.width(), border), color));
889     }
890
891     void SelectionEngine::addFrameDecorations(QTextDocument *document, QTextFrame *frame)
892     {
893         QTextDocumentLayout *documentLayout = qobject_cast<QTextDocumentLayout *>(document->documentLayout());
894         QTextFrameFormat frameFormat = frame->format().toFrameFormat();
895
896         QTextTable *table = qobject_cast<QTextTable *>(frame);
897         QRectF boundingRect = table == 0
898                 ? documentLayout->frameBoundingRect(frame)
899                 : documentLayout->tableBoundingRect(table);
900
901         QBrush bg = frame->frameFormat().background();
902         if (bg.style() != Qt::NoBrush)
903             m_backgrounds.append(qMakePair(boundingRect, bg.color()));
904
905         if (!frameFormat.hasProperty(QTextFormat::FrameBorder))
906             return;
907
908         qreal borderWidth = frameFormat.border();
909         if (qFuzzyIsNull(borderWidth))
910             return;
911
912         QBrush borderBrush = frameFormat.borderBrush();
913         QTextFrameFormat::BorderStyle borderStyle = frameFormat.borderStyle();
914         if (borderStyle == QTextFrameFormat::BorderStyle_None)
915             return;
916
917         addBorder(boundingRect.adjusted(frameFormat.leftMargin(), frameFormat.topMargin(),
918                                         -frameFormat.rightMargin(), -frameFormat.bottomMargin()),
919                   borderWidth, borderStyle, borderBrush);
920         if (table != 0) {
921             int rows = table->rows();
922             int columns = table->columns();
923
924             for (int row=0; row<rows; ++row) {
925                 for (int column=0; column<columns; ++column) {
926                     QTextTableCell cell = table->cellAt(row, column);
927
928                     QRectF cellRect = documentLayout->tableCellBoundingRect(table, cell);
929                     addBorder(cellRect.adjusted(-borderWidth, -borderWidth, 0, 0), borderWidth,
930                               borderStyle, borderBrush);
931                 }
932             }
933         }
934     }
935
936     void SelectionEngine::addToSceneGraph(QQuickTextNode *parentNode,
937                                           QQuickText::TextStyle style,
938                                           const QColor &styleColor)
939     {
940         if (m_currentLine.isValid())
941             processCurrentLine();
942
943
944         for (int i=0; i<m_backgrounds.size(); ++i) {
945             const QRectF &rect = m_backgrounds.at(i).first;
946             const QColor &color = m_backgrounds.at(i).second;
947
948             parentNode->appendChildNode(new QSGSimpleRectNode(rect, color));
949         }
950
951         // First, prepend all selection rectangles to the tree
952         for (int i=0; i<m_selectionRects.size(); ++i) {
953             const QRectF &rect = m_selectionRects.at(i);
954
955             parentNode->appendChildNode(new QSGSimpleRectNode(rect, m_selectionColor));
956         }
957
958         // Finally, add decorations for each node to the tree.
959         for (int i=0; i<m_lines.size(); ++i) {
960             const TextDecoration &textDecoration = m_lines.at(i);
961
962             QColor color = textDecoration.selectionState == BinaryTreeNode::Selected
963                     ? m_selectedTextColor
964                     : textDecoration.color;
965
966             parentNode->appendChildNode(new QSGSimpleRectNode(textDecoration.rect, color));
967         }
968
969         // Then, go through all the nodes for all lines and combine all QGlyphRuns with a common
970         // font, selection state and clip node.
971         typedef QPair<QFontEngine *, QPair<QQuickDefaultClipNode *, QPair<QRgb, int> > > KeyType;
972         QHash<KeyType, BinaryTreeNode *> map;
973         QList<BinaryTreeNode *> nodes;
974         for (int i=0; i<m_processedNodes.size(); ++i) {
975             BinaryTreeNode *node = m_processedNodes.data() + i;
976
977             if (node->image.isNull()) {
978                 QGlyphRun glyphRun = node->glyphRun;
979                 QRawFont rawFont = glyphRun.rawFont();
980                 QRawFontPrivate *rawFontD = QRawFontPrivate::get(rawFont);
981
982                 QFontEngine *fontEngine = rawFontD->fontEngine;
983
984                 KeyType key(qMakePair(fontEngine,
985                                       qMakePair(node->clipNode,
986                                                 qMakePair(node->color.rgba(), int(node->selectionState)))));
987
988                 BinaryTreeNode *otherNode = map.value(key, 0);
989                 if (otherNode != 0) {
990                     QGlyphRun &otherGlyphRun = otherNode->glyphRun;
991
992                     QVector<quint32> otherGlyphIndexes = otherGlyphRun.glyphIndexes();
993                     QVector<QPointF> otherGlyphPositions = otherGlyphRun.positions();
994
995                     otherGlyphIndexes += glyphRun.glyphIndexes();
996
997                     QVector<QPointF> glyphPositions = glyphRun.positions();
998                     for (int j=0; j<glyphPositions.size(); ++j) {
999                         otherGlyphPositions += glyphPositions.at(j) + (node->position - otherNode->position);
1000                     }
1001
1002                     otherGlyphRun.setGlyphIndexes(otherGlyphIndexes);
1003                     otherGlyphRun.setPositions(otherGlyphPositions);
1004
1005                 } else {
1006                     map.insert(key, node);
1007                     nodes.append(node);
1008                 }
1009             } else {
1010                 parentNode->addImage(node->boundingRect, node->image);
1011                 if (node->selectionState == BinaryTreeNode::Selected) {
1012                     QColor color = m_selectionColor;
1013                     color.setAlpha(128);
1014                     parentNode->appendChildNode(new QSGSimpleRectNode(node->boundingRect, color));
1015                 }
1016             }
1017         }
1018
1019         // ...and add clip nodes and glyphs to tree.
1020         foreach (const BinaryTreeNode *node, nodes) {
1021
1022             QQuickDefaultClipNode *clipNode = node->clipNode;
1023             if (clipNode != 0 && clipNode->parent() == 0 )
1024                 parentNode->appendChildNode(clipNode);
1025
1026             QColor color = node->selectionState == BinaryTreeNode::Selected
1027                     ? m_selectedTextColor
1028                     : node->color;
1029
1030             parentNode->addGlyphs(node->position, node->glyphRun, color, style, styleColor, clipNode);
1031         }
1032     }
1033 }
1034
1035 void QQuickTextNode::mergeFormats(QTextLayout *textLayout,
1036                                QVarLengthArray<QTextLayout::FormatRange> *mergedFormats)
1037 {
1038     Q_ASSERT(mergedFormats != 0);
1039     if (textLayout == 0)
1040         return;
1041
1042     QList<QTextLayout::FormatRange> additionalFormats = textLayout->additionalFormats();
1043     for (int i=0; i<additionalFormats.size(); ++i) {
1044         QTextLayout::FormatRange additionalFormat = additionalFormats.at(i);
1045         if (additionalFormat.format.hasProperty(QTextFormat::ForegroundBrush)
1046          || additionalFormat.format.hasProperty(QTextFormat::BackgroundBrush)
1047          || additionalFormat.format.isAnchor()) {
1048             // Merge overlapping formats
1049             if (!mergedFormats->isEmpty()) {
1050                 QTextLayout::FormatRange *lastFormat = mergedFormats->data() + mergedFormats->size() - 1;
1051
1052                 if (additionalFormat.start < lastFormat->start + lastFormat->length) {
1053                     QTextLayout::FormatRange *mergedRange = 0;
1054
1055                     int length = additionalFormat.length;
1056                     if (additionalFormat.start > lastFormat->start) {
1057                         lastFormat->length = additionalFormat.start - lastFormat->start;
1058                         length -= lastFormat->length;
1059
1060                         mergedFormats->append(QTextLayout::FormatRange());
1061                         mergedRange = mergedFormats->data() + mergedFormats->size() - 1;
1062                         lastFormat = mergedFormats->data() + mergedFormats->size() - 2;
1063                     } else {
1064                         mergedRange = lastFormat;
1065                     }
1066
1067                     mergedRange->format = lastFormat->format;
1068                     mergedRange->format.merge(additionalFormat.format);
1069                     mergedRange->start = additionalFormat.start;
1070
1071                     int end = qMin(additionalFormat.start + additionalFormat.length,
1072                                    lastFormat->start + lastFormat->length);
1073
1074                     mergedRange->length = end - mergedRange->start;
1075                     length -= mergedRange->length;
1076
1077                     additionalFormat.start = end;
1078                     additionalFormat.length = length;
1079                 }
1080             }
1081
1082             if (additionalFormat.length > 0)
1083                 mergedFormats->append(additionalFormat);
1084         }
1085     }
1086
1087 }
1088
1089 namespace {
1090
1091     class ProtectedLayoutAccessor: public QAbstractTextDocumentLayout
1092     {
1093     public:
1094         inline QTextCharFormat formatAccessor(int pos)
1095         {
1096             return format(pos);
1097         }
1098     };
1099
1100 }
1101
1102 void QQuickTextNode::addImage(const QRectF &rect, const QImage &image)
1103 {
1104     QSGImageNode *node = m_context->createImageNode();
1105     QSGTexture *texture = m_context->createTexture(image);
1106     m_textures.append(texture);
1107     node->setTargetRect(rect);
1108     node->setTexture(texture);
1109     appendChildNode(node);
1110     node->update();
1111 }
1112
1113 void QQuickTextNode::addTextDocument(const QPointF &position, QTextDocument *textDocument,
1114                                   const QColor &textColor,
1115                                   QQuickText::TextStyle style, const QColor &styleColor,
1116                                   const QColor &anchorColor,
1117                                   const QColor &selectionColor, const QColor &selectedTextColor,
1118                                   int selectionStart, int selectionEnd)
1119 {
1120     SelectionEngine engine;
1121     engine.setTextColor(textColor);
1122     engine.setSelectedTextColor(selectedTextColor);
1123     engine.setSelectionColor(selectionColor);
1124     engine.setAnchorColor(anchorColor);
1125
1126     QList<QTextFrame *> frames;
1127     frames.append(textDocument->rootFrame());
1128     while (!frames.isEmpty()) {
1129         QTextFrame *textFrame = frames.takeFirst();
1130         frames.append(textFrame->childFrames());
1131
1132         engine.addFrameDecorations(textDocument, textFrame);
1133
1134         if (textFrame->firstPosition() > textFrame->lastPosition()
1135          && textFrame->frameFormat().position() != QTextFrameFormat::InFlow) {
1136             const int pos = textFrame->firstPosition() - 1;
1137             ProtectedLayoutAccessor *a = static_cast<ProtectedLayoutAccessor *>(textDocument->documentLayout());
1138             QTextCharFormat format = a->formatAccessor(pos);
1139             QRectF rect = a->frameBoundingRect(textFrame);
1140
1141             QTextBlock block = textFrame->firstCursorPosition().block();
1142             engine.setCurrentLine(block.layout()->lineForTextPosition(pos - block.position()));
1143             engine.addTextObject(rect.topLeft(), format, BinaryTreeNode::Unselected, textDocument,
1144                                  pos, textFrame->frameFormat().position());
1145         } else {
1146             QTextFrame::iterator it = textFrame->begin();
1147
1148             while (!it.atEnd()) {
1149                 Q_ASSERT(!engine.currentLine().isValid());
1150
1151                 QTextBlock block = it.currentBlock();
1152                 int preeditLength = block.isValid() ? block.layout()->preeditAreaText().length() : 0;
1153                 int preeditPosition = block.isValid() ? block.layout()->preeditAreaPosition() : -1;
1154
1155                 QVarLengthArray<QTextLayout::FormatRange> colorChanges;
1156                 mergeFormats(block.layout(), &colorChanges);
1157
1158                 QPointF blockPosition = textDocument->documentLayout()->blockBoundingRect(block).topLeft() + position;
1159                 if (QTextList *textList = block.textList()) {
1160                     QPointF pos = blockPosition;
1161                     QTextLayout *layout = block.layout();
1162                     if (layout->lineCount() > 0) {
1163                         QTextLine firstLine = layout->lineAt(0);
1164                         Q_ASSERT(firstLine.isValid());
1165
1166                         engine.setCurrentLine(firstLine);
1167
1168                         QRectF textRect = firstLine.naturalTextRect();
1169                         pos += textRect.topLeft();
1170                         if (block.textDirection() == Qt::RightToLeft)
1171                             pos.rx() += textRect.width();
1172
1173                         const QTextCharFormat charFormat = block.charFormat();
1174                         QFont font(charFormat.font());
1175                         QFontMetricsF fontMetrics(font);
1176                         QTextListFormat listFormat = textList->format();
1177
1178                         QString listItemBullet;
1179                         switch (listFormat.style()) {
1180                         case QTextListFormat::ListCircle:
1181                             listItemBullet = QChar(0x25E6); // White bullet
1182                             break;
1183                         case QTextListFormat::ListSquare:
1184                             listItemBullet = QChar(0x25AA); // Black small square
1185                             break;
1186                         case QTextListFormat::ListDecimal:
1187                         case QTextListFormat::ListLowerAlpha:
1188                         case QTextListFormat::ListUpperAlpha:
1189                         case QTextListFormat::ListLowerRoman:
1190                         case QTextListFormat::ListUpperRoman:
1191                             listItemBullet = textList->itemText(block);
1192                             break;
1193                         default:
1194                             listItemBullet = QChar(0x2022); // Black bullet
1195                             break;
1196                         };
1197
1198                         QSizeF size(fontMetrics.width(listItemBullet), fontMetrics.height());
1199                         qreal xoff = fontMetrics.width(QLatin1Char(' '));
1200                         if (block.textDirection() == Qt::LeftToRight)
1201                             xoff = -xoff - size.width();
1202                         engine.setPosition(pos + QPointF(xoff, 0));
1203
1204                         QTextLayout layout;
1205                         layout.setFont(font);
1206                         layout.setText(listItemBullet); // Bullet
1207                         layout.beginLayout();
1208                         QTextLine line = layout.createLine();
1209                         line.setPosition(QPointF(0, 0));
1210                         layout.endLayout();
1211
1212                         QList<QGlyphRun> glyphRuns = layout.glyphRuns();
1213                         for (int i=0; i<glyphRuns.size(); ++i)
1214                             engine.addUnselectedGlyphs(glyphRuns.at(i));
1215                     }
1216                 }
1217
1218                 int textPos = block.position();
1219                 QTextBlock::iterator blockIterator = block.begin();
1220
1221                 while (!blockIterator.atEnd()) {
1222                     QTextFragment fragment = blockIterator.fragment();
1223                     QString text = fragment.text();
1224                     if (text.isEmpty())
1225                         continue;
1226
1227                     QTextCharFormat charFormat = fragment.charFormat();
1228                     engine.setPosition(blockPosition);
1229                     if (text.contains(QChar::ObjectReplacementCharacter)) {
1230                         QTextFrame *frame = qobject_cast<QTextFrame *>(textDocument->objectForFormat(charFormat));
1231                         if (frame && frame->frameFormat().position() == QTextFrameFormat::InFlow) {
1232                             int blockRelativePosition = textPos - block.position();
1233                             QTextLine line = block.layout()->lineForTextPosition(blockRelativePosition);
1234                             if (!engine.currentLine().isValid()
1235                                     || line.lineNumber() != engine.currentLine().lineNumber()) {
1236                                 engine.setCurrentLine(line);
1237                             }
1238
1239                             BinaryTreeNode::SelectionState selectionState =
1240                                     (selectionStart < textPos + text.length()
1241                                      && selectionEnd >= textPos)
1242                                     ? BinaryTreeNode::Selected
1243                                     : BinaryTreeNode::Unselected;
1244
1245                             engine.addTextObject(QPointF(), charFormat, selectionState, textDocument, textPos);
1246                         }
1247                         textPos += text.length();
1248                     } else {
1249                         if (charFormat.foreground().style() != Qt::NoBrush)
1250                             engine.setTextColor(charFormat.foreground().color());
1251                         else if (charFormat.isAnchor())
1252                             engine.setTextColor(anchorColor);
1253                         else
1254                             engine.setTextColor(textColor);
1255
1256                         int fragmentEnd = textPos + fragment.length();
1257                         if (preeditPosition >= 0
1258                          && preeditPosition >= textPos
1259                          && preeditPosition <= fragmentEnd) {
1260                             fragmentEnd += preeditLength;
1261                         }
1262
1263                         textPos = engine.addText(block, charFormat, textColor, colorChanges, textPos, fragmentEnd,
1264                                        selectionStart, selectionEnd);
1265                     }
1266
1267                     ++blockIterator;
1268                 }
1269
1270                 if (preeditLength >= 0 && textPos <= block.position() + preeditPosition) {
1271                     engine.setPosition(blockPosition);
1272                     textPos = block.position() + preeditPosition;
1273                     QTextLine line = block.layout()->lineForTextPosition(preeditPosition);
1274                     if (!engine.currentLine().isValid()
1275                             || line.lineNumber() != engine.currentLine().lineNumber()) {
1276                         engine.setCurrentLine(line);
1277                     }
1278                     textPos = engine.addText(block, block.charFormat(), textColor, colorChanges,
1279                                              textPos, textPos + preeditLength,
1280                                              selectionStart, selectionEnd);
1281                 }
1282
1283                 engine.setCurrentLine(QTextLine()); // Reset current line because the text layout changed
1284                 ++it;
1285             }
1286         }
1287     }
1288
1289     engine.addToSceneGraph(this, style, styleColor);
1290 }
1291
1292 void QQuickTextNode::addTextLayout(const QPointF &position, QTextLayout *textLayout, const QColor &color,
1293                                 QQuickText::TextStyle style, const QColor &styleColor,
1294                                 const QColor &anchorColor,
1295                                 const QColor &selectionColor, const QColor &selectedTextColor,
1296                                 int selectionStart, int selectionEnd,
1297                                 int lineStart, int lineCount)
1298 {
1299     SelectionEngine engine;
1300     engine.setTextColor(color);
1301     engine.setSelectedTextColor(selectedTextColor);
1302     engine.setSelectionColor(selectionColor);
1303     engine.setAnchorColor(anchorColor);
1304     engine.setPosition(position);
1305
1306     int preeditLength = textLayout->preeditAreaText().length();
1307     int preeditPosition = textLayout->preeditAreaPosition();
1308
1309     QVarLengthArray<QTextLayout::FormatRange> colorChanges;
1310     mergeFormats(textLayout, &colorChanges);
1311
1312     lineCount = lineCount >= 0
1313             ? qMin(lineStart + lineCount, textLayout->lineCount())
1314             : textLayout->lineCount();
1315
1316     for (int i=lineStart; i<lineCount; ++i) {
1317         QTextLine line = textLayout->lineAt(i);
1318
1319         int start = line.textStart();
1320         int length = line.textLength();
1321         int end = start + length;
1322
1323         if (preeditPosition >= 0
1324          && preeditPosition >= start
1325          && preeditPosition < end) {
1326             end += preeditLength;
1327         }
1328
1329         engine.setCurrentLine(line);
1330         engine.addGlyphsForRanges(colorChanges, start, end, selectionStart, selectionEnd);
1331     }
1332
1333     engine.addToSceneGraph(this, style, styleColor);
1334 }
1335
1336 void QQuickTextNode::deleteContent()
1337 {
1338     while (firstChild() != 0)
1339         delete firstChild();
1340     m_cursorNode = 0;
1341 }
1342
1343 #if 0
1344 void QQuickTextNode::updateNodes()
1345 {
1346     return;
1347     deleteContent();
1348     if (m_text.isEmpty())
1349         return;
1350
1351     if (m_usePixmapCache) {
1352         // ### gunnar: port properly
1353 //        QPixmap pixmap = generatedPixmap();
1354 //        if (pixmap.isNull())
1355 //            return;
1356
1357 //        QSGImageNode *pixmapNode = m_context->createImageNode();
1358 //        pixmapNode->setRect(pixmap.rect());
1359 //        pixmapNode->setSourceRect(pixmap.rect());
1360 //        pixmapNode->setOpacity(m_opacity);
1361 //        pixmapNode->setClampToEdge(true);
1362 //        pixmapNode->setLinearFiltering(m_linearFiltering);
1363
1364 //        appendChildNode(pixmapNode);
1365     } else {
1366         if (m_text.isEmpty())
1367             return;
1368
1369         // Implement styling by drawing text several times at slight shifts. shiftForStyle
1370         // contains the sequence of shifted positions at which to draw the text. All except
1371         // the last will be drawn with styleColor.
1372         QList<QPointF> shiftForStyle;
1373         switch (m_textStyle) {
1374         case OutlineTextStyle:
1375             // ### Should be made faster by implementing outline material
1376             shiftForStyle << QPointF(-1, 0);
1377             shiftForStyle << QPointF(0, -1);
1378             shiftForStyle << QPointF(1, 0);
1379             shiftForStyle << QPointF(0, 1);
1380             break;
1381         case SunkenTextStyle:
1382             shiftForStyle << QPointF(0, -1);
1383             break;
1384         case RaisedTextStyle:
1385             shiftForStyle << QPointF(0, 1);
1386             break;
1387         default:
1388             break;
1389         }
1390
1391         shiftForStyle << QPointF(0, 0); // Regular position
1392         while (!shiftForStyle.isEmpty()) {
1393             QPointF shift = shiftForStyle.takeFirst();
1394
1395             // Use styleColor for all but last shift
1396             if (m_richText) {
1397                 QColor overrideColor = shiftForStyle.isEmpty() ? QColor() : m_styleColor;
1398
1399                 QTextFrame *textFrame = m_textDocument->rootFrame();
1400                 QPointF p = m_textDocument->documentLayout()->frameBoundingRect(textFrame).topLeft();
1401
1402                 QTextFrame::iterator it = textFrame->begin();
1403                 while (!it.atEnd()) {
1404                     addTextBlock(shift + p, it.currentBlock(), overrideColor);
1405                     ++it;
1406                 }
1407             } else {
1408                 addTextLayout(shift, m_textLayout, shiftForStyle.isEmpty()
1409                                                    ? m_color
1410                                                    : m_styleColor);
1411             }
1412         }
1413     }
1414 }
1415 #endif
1416
1417 QT_END_NAMESPACE