• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * This file is part of the WebKit project.
3  *
4  * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz>
5  *           (C) 2006 Apple Computer Inc.
6  *           (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
7  *           (C) 2008 Rob Buis <buis@kde.org>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  *
24  */
25 
26 #include "config.h"
27 
28 #if ENABLE(SVG)
29 #include "RenderSVGInlineText.h"
30 
31 #include "FloatConversion.h"
32 #include "FloatQuad.h"
33 #include "RenderBlock.h"
34 #include "RenderSVGRoot.h"
35 #include "SVGInlineTextBox.h"
36 #include "SVGRootInlineBox.h"
37 #include "VisiblePosition.h"
38 
39 namespace WebCore {
40 
isChildOfHiddenContainer(RenderObject * start)41 static inline bool isChildOfHiddenContainer(RenderObject* start)
42 {
43     while (start) {
44         if (start->isSVGHiddenContainer())
45             return true;
46 
47         start = start->parent();
48     }
49 
50     return false;
51 }
52 
RenderSVGInlineText(Node * n,PassRefPtr<StringImpl> str)53 RenderSVGInlineText::RenderSVGInlineText(Node* n, PassRefPtr<StringImpl> str)
54     : RenderText(n, str)
55 {
56 }
57 
58 
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)59 void RenderSVGInlineText::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
60 {
61     // Skip RenderText's possible layout scheduling on style change
62     RenderObject::styleDidChange(diff, oldStyle);
63 
64     // FIXME: SVG text is apparently always transformed?
65     if (RefPtr<StringImpl> textToTransform = originalText())
66         setText(textToTransform.release(), true);
67 }
68 
absoluteRects(Vector<IntRect> & rects,int,int)69 void RenderSVGInlineText::absoluteRects(Vector<IntRect>& rects, int, int)
70 {
71     rects.append(computeRepaintRectForRange(0, 0, textLength()));
72 }
73 
absoluteQuads(Vector<FloatQuad> & quads)74 void RenderSVGInlineText::absoluteQuads(Vector<FloatQuad>& quads)
75 {
76     quads.append(computeRepaintQuadForRange(0, 0, textLength()));
77 }
78 
selectionRectForRepaint(RenderBoxModelObject * repaintContainer,bool)79 IntRect RenderSVGInlineText::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool /*clipToVisibleContent*/)
80 {
81     ASSERT(!needsLayout());
82 
83     if (selectionState() == SelectionNone)
84         return IntRect();
85 
86     // Early exit if we're ie. a <text> within a <defs> section.
87     if (isChildOfHiddenContainer(this))
88         return IntRect();
89 
90     // Now calculate startPos and endPos for painting selection.
91     // We include a selection while endPos > 0
92     int startPos, endPos;
93     if (selectionState() == SelectionInside) {
94         // We are fully selected.
95         startPos = 0;
96         endPos = textLength();
97     } else {
98         selectionStartEnd(startPos, endPos);
99         if (selectionState() == SelectionStart)
100             endPos = textLength();
101         else if (selectionState() == SelectionEnd)
102             startPos = 0;
103     }
104 
105     if (startPos == endPos)
106         return IntRect();
107 
108     return computeRepaintRectForRange(repaintContainer, startPos, endPos);
109 }
110 
computeRepaintRectForRange(RenderBoxModelObject * repaintContainer,int startPos,int endPos)111 IntRect RenderSVGInlineText::computeRepaintRectForRange(RenderBoxModelObject* repaintContainer, int startPos, int endPos)
112 {
113     FloatQuad repaintQuad = computeRepaintQuadForRange(repaintContainer, startPos, endPos);
114     return enclosingIntRect(repaintQuad.boundingBox());
115 }
116 
computeRepaintQuadForRange(RenderBoxModelObject * repaintContainer,int startPos,int endPos)117 FloatQuad RenderSVGInlineText::computeRepaintQuadForRange(RenderBoxModelObject* repaintContainer, int startPos, int endPos)
118 {
119     RenderBlock* cb = containingBlock();
120     if (!cb || !cb->container())
121         return FloatQuad();
122 
123     RenderSVGRoot* root = findSVGRootObject(parent());
124     if (!root)
125         return FloatQuad();
126 
127     IntRect rect;
128     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
129         rect.unite(box->selectionRect(0, 0, startPos, endPos));
130 
131     return localToContainerQuad(FloatQuad(rect), repaintContainer);
132 }
133 
createTextBox()134 InlineTextBox* RenderSVGInlineText::createTextBox()
135 {
136     InlineTextBox* box = new (renderArena()) SVGInlineTextBox(this);
137     box->setHasVirtualHeight();
138     return box;
139 }
140 
localCaretRect(InlineBox *,int,int *)141 IntRect RenderSVGInlineText::localCaretRect(InlineBox*, int, int*)
142 {
143     // SVG doesn't have any editable content where a caret rect would be needed.
144     // FIXME: That's not sufficient. The localCaretRect function is also used for selection.
145     return IntRect();
146 }
147 
positionForPoint(const IntPoint & point)148 VisiblePosition RenderSVGInlineText::positionForPoint(const IntPoint& point)
149 {
150     SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(firstTextBox());
151 
152     if (!textBox || textLength() == 0)
153         return createVisiblePosition(0, DOWNSTREAM);
154 
155     SVGRootInlineBox* rootBox = textBox->svgRootInlineBox();
156     RenderBlock* object = rootBox ? rootBox->block() : 0;
157 
158     if (!object)
159         return createVisiblePosition(0, DOWNSTREAM);
160 
161     int closestOffsetInBox = 0;
162 
163     // FIXME: This approach is wrong.  The correct code would first find the
164     // closest SVGInlineTextBox to the point, and *then* ask only that inline box
165     // what the closest text offset to that point is.  This code instead walks
166     // through all boxes in order, so when you click "near" a box, you'll actually
167     // end up returning the nearest offset in the last box, even if the
168     // nearest offset to your click is contained in another box.
169     for (SVGInlineTextBox* box = textBox; box; box = static_cast<SVGInlineTextBox*>(box->nextTextBox())) {
170         if (box->svgCharacterHitsPosition(point.x() + object->x(), point.y() + object->y(), closestOffsetInBox)) {
171             // If we're not at the end/start of the box, stop looking for other selected boxes.
172             if (box->direction() == LTR) {
173                 if (closestOffsetInBox <= (int) box->end() + 1)
174                     break;
175             } else {
176                 if (closestOffsetInBox > (int) box->start())
177                     break;
178             }
179         }
180     }
181 
182     return createVisiblePosition(closestOffsetInBox, DOWNSTREAM);
183 }
184 
destroy()185 void RenderSVGInlineText::destroy()
186 {
187     if (!documentBeingDestroyed()) {
188         setNeedsLayoutAndPrefWidthsRecalc();
189         repaint();
190     }
191     RenderText::destroy();
192 }
193 
194 }
195 
196 #endif // ENABLE(SVG)
197