Home | History | Annotate | Download | only in rendering
      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