| /* |
| * This file is part of the WebKit project. |
| * |
| * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> |
| * (C) 2006 Apple Computer Inc. |
| * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> |
| * (C) 2008 Rob Buis <buis@kde.org> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| * |
| */ |
| |
| #include "config.h" |
| |
| #if ENABLE(SVG) |
| #include "RenderSVGInlineText.h" |
| |
| #include "FloatConversion.h" |
| #include "FloatQuad.h" |
| #include "RenderBlock.h" |
| #include "RenderSVGRoot.h" |
| #include "SVGInlineTextBox.h" |
| #include "SVGRootInlineBox.h" |
| #include "VisiblePosition.h" |
| |
| namespace WebCore { |
| |
| static inline bool isChildOfHiddenContainer(RenderObject* start) |
| { |
| while (start) { |
| if (start->isSVGHiddenContainer()) |
| return true; |
| |
| start = start->parent(); |
| } |
| |
| return false; |
| } |
| |
| RenderSVGInlineText::RenderSVGInlineText(Node* n, PassRefPtr<StringImpl> str) |
| : RenderText(n, str) |
| { |
| } |
| |
| |
| void RenderSVGInlineText::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) |
| { |
| // Skip RenderText's possible layout scheduling on style change |
| RenderObject::styleDidChange(diff, oldStyle); |
| |
| // FIXME: SVG text is apparently always transformed? |
| if (RefPtr<StringImpl> textToTransform = originalText()) |
| setText(textToTransform.release(), true); |
| } |
| |
| void RenderSVGInlineText::absoluteRects(Vector<IntRect>& rects, int, int) |
| { |
| rects.append(computeRepaintRectForRange(0, 0, textLength())); |
| } |
| |
| void RenderSVGInlineText::absoluteQuads(Vector<FloatQuad>& quads) |
| { |
| quads.append(computeRepaintQuadForRange(0, 0, textLength())); |
| } |
| |
| IntRect RenderSVGInlineText::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool /*clipToVisibleContent*/) |
| { |
| ASSERT(!needsLayout()); |
| |
| if (selectionState() == SelectionNone) |
| return IntRect(); |
| |
| // Early exit if we're ie. a <text> within a <defs> section. |
| if (isChildOfHiddenContainer(this)) |
| return IntRect(); |
| |
| // Now calculate startPos and endPos for painting selection. |
| // We include a selection while endPos > 0 |
| int startPos, endPos; |
| if (selectionState() == SelectionInside) { |
| // We are fully selected. |
| startPos = 0; |
| endPos = textLength(); |
| } else { |
| selectionStartEnd(startPos, endPos); |
| if (selectionState() == SelectionStart) |
| endPos = textLength(); |
| else if (selectionState() == SelectionEnd) |
| startPos = 0; |
| } |
| |
| if (startPos == endPos) |
| return IntRect(); |
| |
| return computeRepaintRectForRange(repaintContainer, startPos, endPos); |
| } |
| |
| IntRect RenderSVGInlineText::computeRepaintRectForRange(RenderBoxModelObject* repaintContainer, int startPos, int endPos) |
| { |
| FloatQuad repaintQuad = computeRepaintQuadForRange(repaintContainer, startPos, endPos); |
| return enclosingIntRect(repaintQuad.boundingBox()); |
| } |
| |
| FloatQuad RenderSVGInlineText::computeRepaintQuadForRange(RenderBoxModelObject* repaintContainer, int startPos, int endPos) |
| { |
| RenderBlock* cb = containingBlock(); |
| if (!cb || !cb->container()) |
| return FloatQuad(); |
| |
| RenderSVGRoot* root = findSVGRootObject(parent()); |
| if (!root) |
| return FloatQuad(); |
| |
| IntRect rect; |
| for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) |
| rect.unite(box->selectionRect(0, 0, startPos, endPos)); |
| |
| return localToContainerQuad(FloatQuad(rect), repaintContainer); |
| } |
| |
| InlineTextBox* RenderSVGInlineText::createTextBox() |
| { |
| InlineTextBox* box = new (renderArena()) SVGInlineTextBox(this); |
| box->setHasVirtualHeight(); |
| return box; |
| } |
| |
| IntRect RenderSVGInlineText::localCaretRect(InlineBox*, int, int*) |
| { |
| // SVG doesn't have any editable content where a caret rect would be needed. |
| // FIXME: That's not sufficient. The localCaretRect function is also used for selection. |
| return IntRect(); |
| } |
| |
| VisiblePosition RenderSVGInlineText::positionForPoint(const IntPoint& point) |
| { |
| SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(firstTextBox()); |
| |
| if (!textBox || textLength() == 0) |
| return createVisiblePosition(0, DOWNSTREAM); |
| |
| SVGRootInlineBox* rootBox = textBox->svgRootInlineBox(); |
| RenderBlock* object = rootBox ? rootBox->block() : 0; |
| |
| if (!object) |
| return createVisiblePosition(0, DOWNSTREAM); |
| |
| int closestOffsetInBox = 0; |
| |
| // FIXME: This approach is wrong. The correct code would first find the |
| // closest SVGInlineTextBox to the point, and *then* ask only that inline box |
| // what the closest text offset to that point is. This code instead walks |
| // through all boxes in order, so when you click "near" a box, you'll actually |
| // end up returning the nearest offset in the last box, even if the |
| // nearest offset to your click is contained in another box. |
| for (SVGInlineTextBox* box = textBox; box; box = static_cast<SVGInlineTextBox*>(box->nextTextBox())) { |
| if (box->svgCharacterHitsPosition(point.x() + object->x(), point.y() + object->y(), closestOffsetInBox)) { |
| // If we're not at the end/start of the box, stop looking for other selected boxes. |
| if (box->direction() == LTR) { |
| if (closestOffsetInBox <= (int) box->end() + 1) |
| break; |
| } else { |
| if (closestOffsetInBox > (int) box->start()) |
| break; |
| } |
| } |
| } |
| |
| return createVisiblePosition(closestOffsetInBox, DOWNSTREAM); |
| } |
| |
| void RenderSVGInlineText::destroy() |
| { |
| if (!documentBeingDestroyed()) { |
| setNeedsLayoutAndPrefWidthsRecalc(); |
| repaint(); |
| } |
| RenderText::destroy(); |
| } |
| |
| } |
| |
| #endif // ENABLE(SVG) |