1 /* 2 * This file is part of the WebKit project. 3 * 4 * Copyright (C) 2006 Oliver Hunt <ojh16 (at) student.canterbury.ac.nz> 5 * (C) 2006 Apple Computer Inc. 6 * (C) 2007 Nikolas Zimmermann <zimmermann (at) kde.org> 7 * (C) 2008 Rob Buis <buis (at) 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 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 53 RenderSVGInlineText::RenderSVGInlineText(Node* n, PassRefPtr<StringImpl> str) 54 : RenderText(n, str) 55 { 56 } 57 58 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 69 void RenderSVGInlineText::absoluteRects(Vector<IntRect>& rects, int, int) 70 { 71 rects.append(computeRepaintRectForRange(0, 0, textLength())); 72 } 73 74 void RenderSVGInlineText::absoluteQuads(Vector<FloatQuad>& quads) 75 { 76 quads.append(computeRepaintQuadForRange(0, 0, textLength())); 77 } 78 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 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 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 134 InlineTextBox* RenderSVGInlineText::createTextBox() 135 { 136 InlineTextBox* box = new (renderArena()) SVGInlineTextBox(this); 137 box->setHasVirtualHeight(); 138 return box; 139 } 140 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 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 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