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