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