blob: 470237cda3fbbc75f83d560abc9d24b4ffdd5b48 [file] [log] [blame]
/*
Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org>
2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
2007 Apple Inc. All rights reserved.
This file is part of the KDE project
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 "SVGSVGElement.h"
#include "AffineTransform.h"
#include "CSSPropertyNames.h"
#include "Document.h"
#include "EventListener.h"
#include "EventNames.h"
#include "FloatConversion.h"
#include "FloatRect.h"
#include "Frame.h"
#include "HTMLNames.h"
#include "RenderSVGContainer.h"
#include "SVGAngle.h"
#include "SVGLength.h"
#include "SVGNames.h"
#include "SVGPreserveAspectRatio.h"
#include "SVGTransform.h"
#include "SVGZoomEvent.h"
#include "SelectionController.h"
#include "TextStream.h"
#include "TimeScheduler.h"
namespace WebCore {
using namespace HTMLNames;
using namespace EventNames;
using namespace SVGNames;
SVGSVGElement::SVGSVGElement(const QualifiedName& tagName, Document* doc)
: SVGStyledLocatableElement(tagName, doc)
, SVGTests()
, SVGLangSpace()
, SVGExternalResourcesRequired()
, SVGFitToViewBox()
, SVGZoomAndPan()
, m_x(this, LengthModeWidth)
, m_y(this, LengthModeHeight)
, m_width(this, LengthModeWidth)
, m_height(this, LengthModeHeight)
, m_useCurrentView(false)
, m_timeScheduler(new TimeScheduler(doc))
{
setWidthBaseValue(SVGLength(this, LengthModeWidth, "100%"));
setHeightBaseValue(SVGLength(this, LengthModeHeight, "100%"));
}
SVGSVGElement::~SVGSVGElement()
{
delete m_timeScheduler;
m_timeScheduler = 0;
// There are cases where removedFromDocument() is not called.
// see ContainerNode::removeAllChildren, called by it's destructor.
document()->accessSVGExtensions()->removeTimeContainer(this);
}
ANIMATED_PROPERTY_DEFINITIONS(SVGSVGElement, SVGLength, Length, length, X, x, SVGNames::xAttr.localName(), m_x)
ANIMATED_PROPERTY_DEFINITIONS(SVGSVGElement, SVGLength, Length, length, Y, y, SVGNames::yAttr.localName(), m_y)
ANIMATED_PROPERTY_DEFINITIONS(SVGSVGElement, SVGLength, Length, length, Width, width, SVGNames::widthAttr.localName(), m_width)
ANIMATED_PROPERTY_DEFINITIONS(SVGSVGElement, SVGLength, Length, length, Height, height, SVGNames::heightAttr.localName(), m_height)
const AtomicString& SVGSVGElement::contentScriptType() const
{
static const AtomicString defaultValue("text/ecmascript");
const AtomicString& n = getAttribute(contentScriptTypeAttr);
return n.isNull() ? defaultValue : n;
}
void SVGSVGElement::setContentScriptType(const AtomicString& type)
{
setAttribute(SVGNames::contentScriptTypeAttr, type);
}
const AtomicString& SVGSVGElement::contentStyleType() const
{
static const AtomicString defaultValue("text/css");
const AtomicString& n = getAttribute(contentStyleTypeAttr);
return n.isNull() ? defaultValue : n;
}
void SVGSVGElement::setContentStyleType(const AtomicString& type)
{
setAttribute(SVGNames::contentStyleTypeAttr, type);
}
FloatRect SVGSVGElement::viewport() const
{
double _x = 0.0;
double _y = 0.0;
if (renderer() && renderer()->parent() &&
!renderer()->parent()->isSVGContainer()) {
_x = x().value();
_y = y().value();
}
float w = width().value();
float h = height().value();
AffineTransform viewBox = viewBoxToViewTransform(w, h);
double wDouble = w;
double hDouble = h;
viewBox.map(_x, _y, &_x, &_y);
viewBox.map(w, h, &wDouble, &hDouble);
return FloatRect::narrowPrecision(_x, _y, wDouble, hDouble);
}
float SVGSVGElement::pixelUnitToMillimeterX() const
{
// FIXME: Implement me (see bug 11273)
return .28f;
}
float SVGSVGElement::pixelUnitToMillimeterY() const
{
// FIXME: Implement me (see bug 11273)
return .28f;
}
float SVGSVGElement::screenPixelToMillimeterX() const
{
return pixelUnitToMillimeterX();
}
float SVGSVGElement::screenPixelToMillimeterY() const
{
return pixelUnitToMillimeterY();
}
bool SVGSVGElement::useCurrentView() const
{
return m_useCurrentView;
}
void SVGSVGElement::setUseCurrentView(bool currentView)
{
m_useCurrentView = currentView;
}
float SVGSVGElement::currentScale() const
{
if (document() && document()->frame())
return document()->frame()->zoomFactor() / 100.0f;
return 1.0f;
}
void SVGSVGElement::setCurrentScale(float scale)
{
if (document() && document()->frame())
document()->frame()->setZoomFactor(static_cast<int>(scale / 100.0f));
}
FloatPoint SVGSVGElement::currentTranslate() const
{
return m_translation;
}
void SVGSVGElement::setCurrentTranslate(const FloatPoint &translation)
{
m_translation = translation;
}
void SVGSVGElement::addSVGWindowEventListener(const AtomicString& eventType, const Attribute* attr)
{
// FIXME: None of these should be window events long term.
// Once we propertly support SVGLoad, etc.
RefPtr<EventListener> listener = document()->accessSVGExtensions()->
createSVGEventListener(attr->localName().domString(), attr->value(), this);
document()->setHTMLWindowEventListener(eventType, listener.release());
}
void SVGSVGElement::parseMappedAttribute(MappedAttribute* attr)
{
if (!nearestViewportElement()) {
// Only handle events if we're the outermost <svg> element
if (attr->name() == onunloadAttr)
addSVGWindowEventListener(unloadEvent, attr);
else if (attr->name() == onabortAttr)
addSVGWindowEventListener(abortEvent, attr);
else if (attr->name() == onerrorAttr)
addSVGWindowEventListener(errorEvent, attr);
else if (attr->name() == onresizeAttr)
addSVGWindowEventListener(resizeEvent, attr);
else if (attr->name() == onscrollAttr)
addSVGWindowEventListener(scrollEvent, attr);
else if (attr->name() == SVGNames::onzoomAttr)
addSVGWindowEventListener(zoomEvent, attr);
}
if (attr->name() == SVGNames::xAttr)
setXBaseValue(SVGLength(this, LengthModeWidth, attr->value()));
else if (attr->name() == SVGNames::yAttr)
setYBaseValue(SVGLength(this, LengthModeHeight, attr->value()));
else if (attr->name() == SVGNames::widthAttr) {
setWidthBaseValue(SVGLength(this, LengthModeWidth, attr->value()));
addCSSProperty(attr, CSS_PROP_WIDTH, attr->value());
if (width().value() < 0.0)
document()->accessSVGExtensions()->reportError("A negative value for svg attribute <width> is not allowed");
} else if (attr->name() == SVGNames::heightAttr) {
setHeightBaseValue(SVGLength(this, LengthModeHeight, attr->value()));
addCSSProperty(attr, CSS_PROP_HEIGHT, attr->value());
if (height().value() < 0.0)
document()->accessSVGExtensions()->reportError("A negative value for svg attribute <height> is not allowed");
} else {
if (SVGTests::parseMappedAttribute(attr))
return;
if (SVGLangSpace::parseMappedAttribute(attr))
return;
if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
return;
if (SVGFitToViewBox::parseMappedAttribute(attr) && renderer()) {
static_cast<RenderSVGContainer*>(renderer())->setViewBox(viewBox());
return;
}
if (SVGZoomAndPan::parseMappedAttribute(attr))
return;
SVGStyledLocatableElement::parseMappedAttribute(attr);
}
}
unsigned long SVGSVGElement::suspendRedraw(unsigned long /* max_wait_milliseconds */)
{
// FIXME: Implement me (see bug 11275)
return 0;
}
void SVGSVGElement::unsuspendRedraw(unsigned long /* suspend_handle_id */, ExceptionCode& ec)
{
// if suspend_handle_id is not found, throw exception
// FIXME: Implement me (see bug 11275)
}
void SVGSVGElement::unsuspendRedrawAll()
{
// FIXME: Implement me (see bug 11275)
}
void SVGSVGElement::forceRedraw()
{
// FIXME: Implement me (see bug 11275)
}
NodeList* SVGSVGElement::getIntersectionList(const FloatRect& rect, SVGElement*)
{
// FIXME: Implement me (see bug 11274)
return 0;
}
NodeList* SVGSVGElement::getEnclosureList(const FloatRect& rect, SVGElement*)
{
// FIXME: Implement me (see bug 11274)
return 0;
}
bool SVGSVGElement::checkIntersection(SVGElement* element, const FloatRect& rect)
{
// TODO : take into account pointer-events?
// FIXME: Why is element ignored??
// FIXME: Implement me (see bug 11274)
return rect.intersects(getBBox());
}
bool SVGSVGElement::checkEnclosure(SVGElement* element, const FloatRect& rect)
{
// TODO : take into account pointer-events?
// FIXME: Why is element ignored??
// FIXME: Implement me (see bug 11274)
return rect.contains(getBBox());
}
void SVGSVGElement::deselectAll()
{
document()->frame()->selectionController()->clear();
}
double SVGSVGElement::createSVGNumber()
{
return 0.0;
}
SVGLength SVGSVGElement::createSVGLength()
{
return SVGLength();
}
SVGAngle* SVGSVGElement::createSVGAngle()
{
return new SVGAngle(0);
}
FloatPoint SVGSVGElement::createSVGPoint()
{
return FloatPoint();
}
AffineTransform SVGSVGElement::createSVGMatrix()
{
return AffineTransform();
}
FloatRect SVGSVGElement::createSVGRect()
{
return FloatRect();
}
SVGTransform SVGSVGElement::createSVGTransform()
{
return SVGTransform();
}
SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const AffineTransform& matrix)
{
return SVGTransform(matrix);
}
AffineTransform SVGSVGElement::getCTM() const
{
AffineTransform mat;
if (renderer() && renderer()->parent() &&
!renderer()->parent()->isSVGContainer())
mat.translate(x().value(), y().value());
if (attributes()->getNamedItem(SVGNames::viewBoxAttr)) {
AffineTransform viewBox = viewBoxToViewTransform(width().value(), height().value());
mat = viewBox * mat;
}
return mat;
}
AffineTransform SVGSVGElement::getScreenCTM() const
{
// FIXME: This assumes that any <svg> element not immediately descending from another SVGElement
// has *no* svg ancestors
document()->updateLayoutIgnorePendingStylesheets();
float rootX = 0.0f;
float rootY = 0.0f;
if (RenderObject* renderer = this->renderer()) {
renderer = renderer->parent();
if (renderer && !(renderer->element() && renderer->element()->isSVGElement())) {
int tx = 0;
int ty = 0;
renderer->absolutePosition(tx, ty, true);
rootX += tx;
rootY += ty;
} else {
rootX += x().value();
rootY += y().value();
}
}
AffineTransform mat = SVGStyledLocatableElement::getScreenCTM();
mat.translate(rootX, rootY);
if (attributes()->getNamedItem(SVGNames::viewBoxAttr)) {
AffineTransform viewBox = viewBoxToViewTransform(width().value(), height().value());
mat = viewBox * mat;
}
return mat;
}
RenderObject* SVGSVGElement::createRenderer(RenderArena* arena, RenderStyle*)
{
RenderSVGContainer* rootContainer = new (arena) RenderSVGContainer(this);
// FIXME: All this setup should be done after attributesChanged, not here.
rootContainer->setViewBox(viewBox());
rootContainer->setAlign(KCAlign(preserveAspectRatio()->align() - 1));
rootContainer->setSlice(preserveAspectRatio()->meetOrSlice() == SVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE);
return rootContainer;
}
void SVGSVGElement::insertedIntoDocument()
{
document()->accessSVGExtensions()->addTimeContainer(this);
SVGStyledLocatableElement::insertedIntoDocument();
}
void SVGSVGElement::removedFromDocument()
{
document()->accessSVGExtensions()->removeTimeContainer(this);
SVGStyledLocatableElement::removedFromDocument();
}
void SVGSVGElement::pauseAnimations()
{
if (!m_timeScheduler->animationsPaused())
m_timeScheduler->toggleAnimations();
}
void SVGSVGElement::unpauseAnimations()
{
if (m_timeScheduler->animationsPaused())
m_timeScheduler->toggleAnimations();
}
bool SVGSVGElement::animationsPaused() const
{
return m_timeScheduler->animationsPaused();
}
float SVGSVGElement::getCurrentTime() const
{
return narrowPrecisionToFloat(m_timeScheduler->elapsed());
}
void SVGSVGElement::setCurrentTime(float /* seconds */)
{
// FIXME: Implement me, bug 12073
}
bool SVGSVGElement::hasRelativeValues() const
{
return (x().isRelative() || width().isRelative() ||
y().isRelative() || height().isRelative());
}
void SVGSVGElement::attributeChanged(Attribute* attr, bool preserveDecls)
{
if (attr->name() == SVGNames::xAttr ||
attr->name() == SVGNames::yAttr ||
attr->name() == SVGNames::widthAttr ||
attr->name() == SVGNames::heightAttr)
if (renderer())
renderer()->setNeedsLayout(true);
SVGStyledElement::attributeChanged(attr, preserveDecls);
}
}
#endif // ENABLE(SVG)
// vim:ts=4:noet