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