/* 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 #include #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(*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(id), listener, false); m_eventListeners.append(rl); listener->deref(); } } int SVGElementImpl::getEventListeners(bool local) { int events = 0; TQPtrListIterator 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) { TQPtrListIterator 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) TQPtrListIterator it(m_eventListeners); for(; it.current(); ++it) { SVGRegisteredEventListener *current = it.current(); TQString 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 TQPtrListIterator 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) { TQPtrListIterator it(element->m_eventListeners); for(; it.current(); ++it) { if(it.current()->id == id) return true; } } } return false; } void SVGElementImpl::removeEventListener(int id) { TQPtrListIterator 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) { TQPtrListIterator 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(); } TQDict &SVGElementImpl::attributes() { return m_attributes; } // tdehtml 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(exec->interpreter())->setAttributeSetMode(true); bridge(exec)->put(exec, Identifier(UString(name)), String(value), KJS::Internal); static_cast(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(exec->interpreter())->setAttributeGetMode(true); retVal = bridge(exec)->get(exec, Identifier(UString(name))).toString(exec).string(); static_cast(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 TQString &name, const TQString &value) { if(hasAttribute(name)) { TQString 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 TQXmlAttributes &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(this); if(style) style->processStyle(); } void SVGElementImpl::setAttributes(bool deep) { // Finalize style SVGStylableImpl *style = dynamic_cast(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 TQPoint &p, const TQPoint &a, SVGMouseEventImpl *mev) { SVGShapeImpl *shape = dynamic_cast(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(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 TQPtrList 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); TQPtrListIterator 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(TQKeyEvent *ke) { DOM::AbstractView temp; SVGEvent::EventId evtId = SVGEvent::UNKNOWN_EVENT; if(ke->type() == TQEvent::KeyRelease && !ke->isAutoRepeat()) evtId = SVGEvent::KEYUP_EVENT; else if(ke->isAutoRepeat()) evtId = SVGEvent::KEYPRESS_EVENT; else if(ke->type() == TQEvent::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(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(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 TQString &errorDesc) { if(ownerDoc()) { ownerDoc()->finishParsing(true, errorDesc); if(hasEventListener(SVGEvent::ERROR_EVENT, true)) dispatchEvent(SVGEvent::ERROR_EVENT, false, false); } } TQString SVGElementImpl::collectText() { TQString 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; }