diff options
Diffstat (limited to 'ksvg/impl/SVGElementImpl.cc')
-rw-r--r-- | ksvg/impl/SVGElementImpl.cc | 713 |
1 files changed, 713 insertions, 0 deletions
diff --git a/ksvg/impl/SVGElementImpl.cc b/ksvg/impl/SVGElementImpl.cc new file mode 100644 index 00000000..2f3d70c6 --- /dev/null +++ b/ksvg/impl/SVGElementImpl.cc @@ -0,0 +1,713 @@ +/* + Copyright (C) 2001-2003 KSVG Team + 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 <qxml.h> + +#include <kdebug.h> + +#include "SVGEvent.h" +#include "SVGEventImpl.h" +#include "SVGHelperImpl.h" +#include "SVGElementImpl.h" +#include "SVGDocumentImpl.h" +#include "SVGSVGElementImpl.h" + +using namespace KSVG; + +#include "SVGElementImpl.lut.h" +#include "ksvg_scriptinterpreter.h" +#include "ksvg_ecma.h" + +SVGElementImpl::Factory *SVGElementImpl::Factory::m_instance = 0; + +SVGElementImpl::SVGElementImpl(DOM::ElementImpl *impl) : DOM::DomShared(), DOM::Element(impl), SVGDOMElementBridge(static_cast<DOM::Element>(*this)) +{ + KSVG_EMPTY_FLAGS + + m_ownerSVGElement = 0; + m_viewportElement = 0; + m_ownerDoc = 0; + + m_mouseOver = false; + m_focus = false; + + m_eventListeners.setAutoDelete(true); + m_attributes.setAutoDelete(true); +} + +SVGElementImpl::~SVGElementImpl() +{ + if(m_ownerSVGElement) + m_ownerSVGElement->deref(); +} + +void SVGElementImpl::setEventListener(int id, SVGEventListener *listener) +{ + if(listener) + listener->ref(); + + removeEventListener(id); + + if(listener) + { + SVGRegisteredEventListener *rl = new SVGRegisteredEventListener(static_cast<SVGEvent::EventId>(id), listener, false); + m_eventListeners.append(rl); + + listener->deref(); + } +} + +int SVGElementImpl::getEventListeners(bool local) +{ + int events = 0; + + QPtrListIterator<SVGRegisteredEventListener> it(m_eventListeners); + for(; it.current(); ++it) + events |= (1 << it.current()->id); + + if(local) + return events; + + for(DOM::Node node = parentNode(); !node.isNull(); node = node.parentNode()) + { + SVGElementImpl *element = ownerDoc()->getElementFromHandle(node.handle()); + + if(element) + { + QPtrListIterator<SVGRegisteredEventListener> it(element->m_eventListeners); + for(; it.current(); ++it) + events |= (1 << it.current()->id); + } + } + + return events; +} + +void SVGElementImpl::setupEventListeners(SVGDocumentImpl *doc, SVGDocumentImpl *newDoc) +{ + if(!doc || !newDoc) + return; + + // Changes the document where the eventlisteners are registered + // Needed for parseXML'ed elements with events, their listeners + // are created in the temporary document fragment and need to be + // registered in the main document (Niko) + QPtrListIterator<SVGRegisteredEventListener> it(m_eventListeners); + for(; it.current(); ++it) + { + SVGRegisteredEventListener *current = it.current(); + + QString valueOfCurrent = newDoc->ecmaEngine()->valueOfEventListener(current->listener); + setEventListener(current->id, doc->createEventListener(valueOfCurrent)); + } +} + +bool SVGElementImpl::hasEventListener(int id, bool local) +{ + // First check if we have the listener, locally + QPtrListIterator<SVGRegisteredEventListener> it(m_eventListeners); + for(; it.current(); ++it) + { + if(it.current()->id == id) + return true; + } + + // We have no local listeners, if we are just interessted + // in those listeners, then return now... + if(local) + return false; + + // Check every parent element + for(DOM::Node node = parentNode(); !node.isNull(); node = node.parentNode()) + { + SVGElementImpl *element = ownerDoc()->getElementFromHandle(node.handle()); + + if(element) + { + QPtrListIterator<SVGRegisteredEventListener> it(element->m_eventListeners); + for(; it.current(); ++it) + { + if(it.current()->id == id) + return true; + } + } + } + + return false; +} + +void SVGElementImpl::removeEventListener(int id) +{ + QPtrListIterator<SVGRegisteredEventListener> it(m_eventListeners); + for(; it.current(); ++it) + { + if(it.current()->id == id) + { + m_eventListeners.removeRef(it.current()); + break; + } + } +} + +void SVGElementImpl::handleLocalEvents(SVGEventImpl *evt, bool useCapture) +{ + QPtrListIterator<SVGRegisteredEventListener> it(m_eventListeners); + for(; it.current(); ++it) + { + if(it.current()->id == evt->id() && it.current()->useCapture == useCapture) + { + it.current()->listener->handleEvent(evt); + break; + } + } +} + +void SVGElementImpl::defaultEventHandler(SVGEventImpl *) +{ +} + +/* +@namespace KSVG +@begin SVGElementImpl::s_hashTable 23 + id SVGElementImpl::ElementId DontDelete + ownerSVGElement SVGElementImpl::OwnerSvgElement DontDelete|ReadOnly + viewportElement SVGElementImpl::ViewportElement DontDelete|ReadOnly + xmlbase SVGElementImpl::XmlBase DontDelete + base SVGElementImpl::XmlBase DontDelete + onmouseup SVGElementImpl::OnMouseUp DontDelete + onmousedown SVGElementImpl::OnMouseDown DontDelete + onmousemove SVGElementImpl::OnMouseMove DontDelete + onmouseover SVGElementImpl::OnMouseOver DontDelete + onmouseout SVGElementImpl::OnMouseOut DontDelete + onclick SVGElementImpl::OnClick DontDelete + onmouseclick SVGElementImpl::OnClick DontDelete + onactivate SVGElementImpl::OnActivate DontDelete + onkeydown SVGElementImpl::OnKeyDown DontDelete + onkeyup SVGElementImpl::OnKeyUp DontDelete + onkeypress SVGElementImpl::OnKeyPress DontDelete + onload SVGElementImpl::OnLoad DontDelete + onfocusin SVGElementImpl::OnFocusIn DontDelete + onfocusout SVGElementImpl::OnFocusOut DontDelete + onerror SVGElementImpl::OnError DontDelete + onabort SVGElementImpl::OnAbort DontDelete +@end +@namespace KSVG +@begin SVGElementImplProto::s_hashTable 5 + getStyle SVGElementImpl::GetStyle DontDelete|Function 0 + setProperty SVGElementImpl::SetProperty DontDelete|Function 2 + getPropertyValue SVGElementImpl::GetPropertyValue DontDelete|Function 1 +@end +*/ + +KSVG_IMPLEMENT_PROTOTYPE("SVGElement", SVGElementImplProto, SVGElementImplProtoFunc) + +Value SVGElementImpl::getValueProperty(ExecState *exec, int token) const +{ + switch(token) + { + case ElementId: + return String(id().string()); + case XmlBase: + return String(xmlbase().string()); + case OwnerSvgElement: + return getDOMNode(exec, *ownerSVGElement()); + case ViewportElement: + return getDOMNode(exec, *viewportElement()); + default: + kdWarning() << "Unhandled token in " << k_funcinfo << " : " << token << endl; + return Undefined(); + } +} + +void SVGElementImpl::putValueProperty(ExecState *exec, int token, const Value &value, int) +{ + switch(token) + { + case ElementId: + setId(value.toString(exec).string()); + break; + case XmlBase: + setXmlbase(value.toString(exec).string()); + break; + case OnMouseUp: + setEventListener(SVGEvent::MOUSEUP_EVENT, m_ownerDoc->createEventListener(value.toString(exec).string())); + break; + case OnMouseDown: + setEventListener(SVGEvent::MOUSEDOWN_EVENT, m_ownerDoc->createEventListener(value.toString(exec).string())); + break; + case OnMouseMove: + setEventListener(SVGEvent::MOUSEMOVE_EVENT, m_ownerDoc->createEventListener(value.toString(exec).string())); + break; + case OnMouseOver: + setEventListener(SVGEvent::MOUSEOVER_EVENT, m_ownerDoc->createEventListener(value.toString(exec).string())); + break; + case OnMouseOut: + setEventListener(SVGEvent::MOUSEOUT_EVENT, m_ownerDoc->createEventListener(value.toString(exec).string())); + break; + case OnClick: + setEventListener(SVGEvent::CLICK_EVENT, m_ownerDoc->createEventListener(value.toString(exec).string())); + break; + case OnActivate: + setEventListener(SVGEvent::DOMACTIVATE_EVENT, m_ownerDoc->createEventListener(value.toString(exec).string())); + break; + case OnKeyDown: + setEventListener(SVGEvent::KEYDOWN_EVENT, m_ownerDoc->createEventListener(value.toString(exec).string())); + break; + case OnKeyUp: + setEventListener(SVGEvent::KEYUP_EVENT, m_ownerDoc->createEventListener(value.toString(exec).string())); + break; + case OnKeyPress: + setEventListener(SVGEvent::KEYPRESS_EVENT, m_ownerDoc->createEventListener(value.toString(exec).string())); + break; + case OnLoad: + setEventListener(SVGEvent::LOAD_EVENT, m_ownerDoc->createEventListener(value.toString(exec).string())); + break; + case OnFocusIn: + setEventListener(SVGEvent::DOMFOCUSIN_EVENT, m_ownerDoc->createEventListener(value.toString(exec).string())); + break; + case OnFocusOut: + setEventListener(SVGEvent::DOMFOCUSOUT_EVENT, m_ownerDoc->createEventListener(value.toString(exec).string())); + break; + case OnError: + setEventListener(SVGEvent::ERROR_EVENT, m_ownerDoc->createEventListener(value.toString(exec).string())); + break; + case OnAbort: + setEventListener(SVGEvent::ABORT_EVENT, m_ownerDoc->createEventListener(value.toString(exec).string())); + break; + default: + kdWarning() << k_funcinfo << "unhandled token " << token << endl; + } +} + +Value SVGElementImplProtoFunc::call(ExecState *exec, Object &thisObj, const List &args) +{ + KSVG_CHECK_THIS(SVGElementImpl) + + switch(id) + { + case SVGElementImpl::GetStyle: + return obj->cache(exec); + case SVGElementImpl::SetProperty: + { + DOM::DOMString attribute = args[0].toString(exec).qstring().lower(); + DOM::DOMString value = args[1].toString(exec).qstring(); + obj->setAttribute(attribute, value); + break; + } + case SVGElementImpl::GetPropertyValue: + return String(obj->getAttribute(args[0].toString(exec).qstring())); + default: + break; + } + + return Undefined(); +} + +QDict<DOM::DOMString> &SVGElementImpl::attributes() +{ + return m_attributes; +} + +// khtml overrides +void SVGElementImpl::setAttribute(const DOM::DOMString &name, const DOM::DOMString &value) +{ + m_attributes.replace(name.string(), new DOM::DOMString(value)); +} + +// Changes internal value. This will have no effect on getAttribute(). +void SVGElementImpl::setAttributeInternal(const DOM::DOMString &name, const DOM::DOMString &value) +{ + ExecState *exec = ownerDoc()->ecmaEngine()->globalExec(); + + static_cast<KSVGScriptInterpreter *>(exec->interpreter())->setAttributeSetMode(true); + bridge(exec)->put(exec, Identifier(UString(name)), String(value), KJS::Internal); + static_cast<KSVGScriptInterpreter *>(exec->interpreter())->setAttributeSetMode(false); +} + +// This gets the actual attribute as set in the svg source +DOM::DOMString SVGElementImpl::getAttribute(const DOM::DOMString &name) const +{ + DOM::DOMString *result = m_attributes[name.string()]; + if(result) + return *result; + else + return DOM::DOMString(); +} + +// This gets the internal, real-time value. This means it can return a default value +// for an attribute even if its not explicitly set in the svg source. +DOM::DOMString SVGElementImpl::getAttributeInternal(const DOM::DOMString &name) +{ + ExecState *exec = ownerDoc()->ecmaEngine()->globalExec(); + + DOM::DOMString retVal; + + static_cast<KSVGScriptInterpreter *>(exec->interpreter())->setAttributeGetMode(true); + retVal = bridge(exec)->get(exec, Identifier(UString(name))).toString(exec).string(); + static_cast<KSVGScriptInterpreter *>(exec->interpreter())->setAttributeGetMode(false); + + return retVal; +} + +bool SVGElementImpl::hasAttribute(const DOM::DOMString &name) +{ + return m_attributes.find(name.string()) != 0; +} + +bool SVGElementImpl::hasAttributes() +{ + return m_attributes.count() > 0; +} + +void SVGElementImpl::setApplyAttribute(const QString &name, const QString &value) +{ + if(hasAttribute(name)) + { + QString cur = getAttribute(name).string(); + cur = cur.simplifyWhiteSpace(); + + if(!cur.endsWith(";")) + cur += "; "; + + cur += value; + + setAttribute(name, cur); + } + else + setAttribute(name, value); +} + +void SVGElementImpl::setId(DOM::DOMString id) +{ + setAttribute("id", id); + + if(ownerDoc() && ownerDoc()->rootElement() && !id.isEmpty()) + ownerDoc()->rootElement()->addToIdMap(id.string(), this); + else if(m_ownerSVGElement && !id.isEmpty()) + m_ownerSVGElement->addToIdMap(id.string(), this); +} + +DOM::DOMString SVGElementImpl::id() const +{ + return getAttribute("id"); +} + +void SVGElementImpl::setXmlbase(DOM::DOMString xmlbase) +{ + setAttribute("xml:base", xmlbase); +} + +DOM::DOMString SVGElementImpl::xmlbase() const +{ + return getAttribute("xml:base"); +} + +void SVGElementImpl::setOwnerSVGElement(SVGSVGElementImpl *owner) +{ + if(m_ownerSVGElement) + m_ownerSVGElement->deref(); + + m_ownerSVGElement = owner; + + if(m_ownerSVGElement) + m_ownerSVGElement->ref(); +} + +void SVGElementImpl::setViewportElement(SVGElementImpl *viewport) +{ + if(m_viewportElement) + m_viewportElement->deref(); + + m_viewportElement = viewport; + + if(m_viewportElement) + m_viewportElement->ref(); +} + +SVGSVGElementImpl *SVGElementImpl::ownerSVGElement() const +{ + return m_ownerSVGElement; +} + +SVGElementImpl *SVGElementImpl::viewportElement() const +{ + return m_viewportElement; +} + +void SVGElementImpl::setAttributes(const QXmlAttributes &attrs) +{ + for(int i = 0; i < attrs.length(); i++) + { + setAttribute(attrs.localName(i), attrs.value(i)); + setAttributeInternal(attrs.localName(i), attrs.value(i)); + } + + setAttributes(); +} + +void SVGElementImpl::setAttributes() +{ + // Finalize style + SVGStylableImpl *style = dynamic_cast<SVGStylableImpl *>(this); + + if(style) + style->processStyle(); +} + +void SVGElementImpl::setAttributes(bool deep) +{ + // Finalize style + SVGStylableImpl *style = dynamic_cast<SVGStylableImpl *>(this); + + if(style) + style->processStyle(); + + if(deep) + { + if(hasChildNodes()) + { + DOM::Node n; + for(n = firstChild(); !n.isNull(); n = n.nextSibling()) + { + SVGElementImpl *elem = ownerDoc()->getElementFromHandle(n.handle()); + if(elem) + elem->setAttributes(true); + } + } + } +} + +bool SVGElementImpl::prepareMouseEvent(const QPoint &p, const QPoint &a, SVGMouseEventImpl *mev) +{ + SVGShapeImpl *shape = dynamic_cast<SVGShapeImpl *>(this); + if(shape) + return shape->prepareMouseEvent(p, a, mev); + + return false; +} + +bool SVGElementImpl::dispatchEvent(int id, bool canBubbleArg, bool cancelableArg) +{ + SVGEventImpl *evt = new SVGEventImpl(static_cast<SVGEvent::EventId>(id), canBubbleArg, cancelableArg); + + evt->ref(); + bool ret = dispatchEvent(evt, true); + evt->deref(); + + return ret; +} + +bool SVGElementImpl::dispatchEvent(SVGEventImpl *evt, bool tempEvent) +{ + evt->setTarget(this); + + // Find out, where to send to -> collect parent nodes + QPtrList<SVGElementImpl> nodeChain; + + for(DOM::Element e = *this; !e.isNull(); e = e.parentNode()) + nodeChain.prepend(ownerDoc()->getElementFromHandle(e.handle())); + + // Trigger any capturing event handlers on our way down + evt->setEventPhase(DOM::Event::CAPTURING_PHASE); + + QPtrListIterator<SVGElementImpl> it(nodeChain); + for(; it.current() && it.current() != this && !evt->propagationStopped(); ++it) + { + evt->setCurrentTarget(it.current()); + + if(it.current()) + it.current()->handleLocalEvents(evt, true); + } + + // Dispatch to the actual target node + it.toLast(); + if(!evt->propagationStopped()) + { + evt->setEventPhase(DOM::Event::AT_TARGET); + evt->setCurrentTarget(it.current()); + + if(it.current()) + it.current()->handleLocalEvents(evt, false); + } + + --it; + + // Bubble up again + if(evt->bubbles()) + { + evt->setEventPhase(DOM::Event::BUBBLING_PHASE); + for(; it.current() && !evt->propagationStopped(); --it) + { + evt->setCurrentTarget(it.current()); + + if(it.current()) + it.current()->handleLocalEvents(evt, false); + } + } + + evt->setCurrentTarget(0); + evt->setEventPhase(0); // I guess this is correct, the spec does not seem to say + // anything about the default event handler phase. + + if(evt->bubbles()) + { + // now we call all default event handlers (this is not part of DOM - it is internal to ksvg) + it.toLast(); + for(; it.current() && !evt->propagationStopped() && !evt->defaultPrevented() && !evt->defaultHandled(); --it) + it.current()->defaultEventHandler(evt); + } + + // If tempEvent is true, this means that the DOM implementation will not be storing a reference to the event, i.e. + // there is no way to retrieve it from javascript if a script does not already have a reference to it in a variable. + // So there is no need for the interpreter to keep the event in its cache + if(tempEvent) + ownerDoc()->ecmaEngine()->finishedWithEvent(evt); + + return !evt->defaultPrevented(); // ### what if defaultPrevented was called before dispatchEvent? +} + +bool SVGElementImpl::dispatchKeyEvent(QKeyEvent *ke) +{ + DOM::AbstractView temp; + + SVGEvent::EventId evtId = SVGEvent::UNKNOWN_EVENT; + + if(ke->type() == QEvent::KeyRelease && !ke->isAutoRepeat()) + evtId = SVGEvent::KEYUP_EVENT; + else if(ke->isAutoRepeat()) + evtId = SVGEvent::KEYPRESS_EVENT; + else if(ke->type() == QEvent::KeyPress) + evtId = SVGEvent::KEYDOWN_EVENT; + + if(evtId == SVGEvent::KEYUP_EVENT && hasEventListener(SVGEvent::DOMACTIVATE_EVENT, false)) + dispatchEvent(SVGEvent::DOMACTIVATE_EVENT, true, true); + + if(!hasEventListener(evtId, false)) + return false; + + SVGEventImpl *evt = new SVGKeyEventImpl(ke, temp, evtId); + + evt->ref(); + bool ret = dispatchEvent(evt, true); + evt->deref(); + + // Rerender now! Once! (Niko) + ownerDoc()->rerender(); + + return ret; +} + +bool SVGElementImpl::dispatchMouseEvent(int id, bool canBubbleArg, bool cancelableArg, long detailArg, long screenXArg, long screenYArg, long clientXArg, long clientYArg, bool ctrlKeyArg, bool altKeyArg, bool shiftKeyArg, bool metaKeyArg, unsigned short buttonArg, SVGElementImpl *relatedTargetArg) +{ + DOM::AbstractView temp; + + SVGEventImpl *evt = new SVGMouseEventImpl(static_cast<SVGEvent::EventId>(id), + canBubbleArg, cancelableArg, temp, detailArg, + screenXArg, screenYArg, + clientXArg, clientYArg, + ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg, + buttonArg, relatedTargetArg); + + evt->ref(); + bool ret = dispatchEvent(evt, true); + evt->deref(); + + return ret; +} + +void SVGElementImpl::setOwnerDoc(SVGDocumentImpl *doc) +{ + if(m_ownerDoc) + m_ownerDoc->removeFromElemDict(handle()); + + m_ownerDoc = doc; + + if(m_ownerDoc) + m_ownerDoc->addToElemDict(handle(), this); +} + +SVGDocumentImpl *SVGElementImpl::ownerDoc() const +{ + return m_ownerDoc; +} + +SVGElementImpl *SVGElementImpl::cloneNode(bool deep) +{ + DOM::Element impl = static_cast<DOM::Document *>(ownerDoc())->createElementNS("", tagName()); + SVGElementImpl *newElement = SVGDocumentImpl::createElement(tagName(), impl.cloneNode(false), ownerDoc()); + newElement->setOwnerSVGElement(ownerSVGElement()); + newElement->setViewportElement(viewportElement()); + + SVGHelperImpl::copyAttributes(this, newElement); + + // Recalc style + //newElement->setAttributes(); + + if(deep) + cloneChildNodes(newElement); + + return newElement; +} + +void SVGElementImpl::cloneChildNodes(SVGElementImpl *clone) +{ + DOM::Node n; + for(n = firstChild(); !n.isNull(); n = n.nextSibling()) + { + SVGElementImpl *elem = ownerDoc()->getElementFromHandle(n.handle()); + if(elem) + clone->appendChild(*elem->cloneNode(true)); + else if(n.nodeType() == DOM::Node::TEXT_NODE) + clone->appendChild(n.cloneNode(true)); + } +} + +void SVGElementImpl::gotError(const QString &errorDesc) +{ + if(ownerDoc()) + { + ownerDoc()->finishParsing(true, errorDesc); + if(hasEventListener(SVGEvent::ERROR_EVENT, true)) + dispatchEvent(SVGEvent::ERROR_EVENT, false, false); + } +} + +QString SVGElementImpl::collectText() +{ + QString text; + + if(hasChildNodes()) + { + DOM::Node node = firstChild(); + + for(; !node.isNull(); node = node.nextSibling()) + { + if(node.nodeType() == DOM::Node::TEXT_NODE) + { + DOM::Text textNode = node; + text += textNode.data().string(); + } + } + } + + return text; +} + +// vim:ts=4:noet |