blob: a0d62b1dbf00b832dc506fa6ea6d5224a2c6a735 [file] [log] [blame]
/*
Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org>
2004, 2005 Rob Buis <buis@kde.org>
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., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
#include <assert.h>
#include <q3dict.h>
#include <kurl.h>
#include <klocale.h>
#include <kdom/Helper.h>
#include <kdom/Shared.h>
#include <kdom/Namespace.h>
#include <kdom/core/domattrs.h>
#include <kdom/cache/KDOMLoader.h>
#include <kdom/core/CDFInterface.h>
#include <kdom/cache/KDOMCachedObject.h>
#include <kdom/css/CSSStyleSheetImpl.h>
#include <kdom/events/MouseEventImpl.h>
#include <kdom/css/StyleSheetListImpl.h>
#include <kdom/events/KeyboardEventImpl.h>
#include <kdom/core/ProcessingInstructionImpl.h>
#include <kcanvas/KCanvas.h>
#include <kcanvas/KCanvasItem.h>
#include <kcanvas/KCanvasView.h>
#include "ksvg.h"
//#include "Ecma.h"
#include <ksvg2/KSVGView.h>
#include "SVGEventImpl.h"
#include "SVGElementImpl.h"
#include "SVGRenderStyle.h"
#include "SVGDocumentImpl.h"
#include "SVGZoomEventImpl.h"
#include "KSVGTimeScheduler.h"
#include "SVGSVGElementImpl.h"
#include "SVGCSSStyleSelector.h"
#include "SVGCSSStyleSheetImpl.h"
#include "SVGAnimatedStringImpl.h"
#include "SVGDOMImplementationImpl.h"
#include "SVGAElementImpl.h"
#include "SVGGElementImpl.h"
#include "SVGUseElementImpl.h"
#include "SVGSVGElementImpl.h"
#include "SVGSetElementImpl.h"
#include "SVGDescElementImpl.h"
#include "SVGRectElementImpl.h"
#include "SVGDefsElementImpl.h"
#include "SVGStopElementImpl.h"
#include "SVGPathElementImpl.h"
#include "SVGLineElementImpl.h"
#include "SVGViewElementImpl.h"
#include "SVGTextElementImpl.h"
#include "SVGTSpanElementImpl.h"
#include "SVGImageElementImpl.h"
#include "SVGTitleElementImpl.h"
#include "SVGCursorElementImpl.h"
#include "SVGFilterElementImpl.h"
#include "SVGFEImageElementImpl.h"
#include "SVGFEBlendElementImpl.h"
#include "SVGFEFloodElementImpl.h"
#include "SVGFEOffsetElementImpl.h"
#include "SVGFEMergeElementImpl.h"
#include "SVGFEMergeNodeElementImpl.h"
#include "SVGFETurbulenceElementImpl.h"
#include "SVGFEFuncRElementImpl.h"
#include "SVGFEFuncGElementImpl.h"
#include "SVGFEFuncBElementImpl.h"
#include "SVGFEFuncAElementImpl.h"
#include "SVGStyleElementImpl.h"
#include "SVGSwitchElementImpl.h"
#include "SVGScriptElementImpl.h"
#include "SVGCircleElementImpl.h"
#include "SVGSymbolElementImpl.h"
#include "SVGMarkerElementImpl.h"
#include "SVGEllipseElementImpl.h"
#include "SVGAnimateElementImpl.h"
#include "SVGPolygonElementImpl.h"
#include "SVGPatternElementImpl.h"
#include "SVGPolylineElementImpl.h"
#include "SVGClipPathElementImpl.h"
#include "SVGAnimateColorElementImpl.h"
#include "SVGFECompositeElementImpl.h"
#include "SVGFEColorMatrixElementImpl.h"
#include "SVGFEGaussianBlurElementImpl.h"
#include "SVGLinearGradientElementImpl.h"
#include "SVGRadialGradientElementImpl.h"
#include "SVGAnimateTransformElementImpl.h"
#include "SVGFEComponentTransferElementImpl.h"
#include "svgtags.c"
#include "svgtags.h"
#include "svgattrs.c"
#include "svgattrs.h"
using namespace KSVG;
SVGDocumentImpl::SVGDocumentImpl(SVGDOMImplementationImpl *i, KDOM::KDOMView *view) : KDOM::DocumentImpl(i, view, ID_LAST_SVGTAG + 1, ATTR_LAST_SVGATTR + 1), KDOM::CachedObjectClient()
{
setPaintDevice(svgView()); // Assign our KSVGView as document paint device
KDOM::DOMStringImpl *svgNamespace = KDOM::NS_SVG.handle();
svgNamespace->ref();
m_namespaceMap->names.insert(0, svgNamespace);
m_namespaceMap->count++;
m_canvasView = 0;
m_lastTarget = 0;
m_scriptsIt = 0;
m_cachedScript = 0;
m_timeScheduler = new TimeScheduler(this);
}
SVGDocumentImpl::~SVGDocumentImpl()
{
// Fire UNLOAD_EVENT upon destruction...
if(KDOM::DocumentImpl::hasListenerType(KDOM::UNLOAD_EVENT))
{
SVGEventImpl *event = static_cast<SVGEventImpl *>(createEvent(KDOM::DOMString("SVGEvents").handle()));
event->ref();
event->initEvent(KDOM::DOMString("unload").handle(), false, false);
dispatchRecursiveEvent(event, lastChild());
event->deref();
}
delete m_scriptsIt;
delete m_timeScheduler;
delete m_cachedScript;
}
KDOM::DOMStringImpl *SVGDocumentImpl::title() const
{
if(rootElement())
{
for(NodeImpl *child = rootElement()->firstChild(); child != 0; child = child->nextSibling())
if(child->id() == ID_TITLE)
return static_cast<SVGTitleElementImpl *>(child)->title();
}
return 0;
}
KDOM::DOMStringImpl *SVGDocumentImpl::referrer() const
{
// TODO
return 0;
}
KDOM::DOMStringImpl *SVGDocumentImpl::domain() const
{
// TODO
return 0;
}
KDOM::DOMStringImpl *SVGDocumentImpl::URL() const
{
return new KDOM::DOMStringImpl(m_url.prettyURL());
}
SVGElementImpl *SVGDocumentImpl::createSVGElement(KDOM::DOMStringImpl *prefix, KDOM::DOMStringImpl *localName)
{
SVGElementImpl *element = 0;
QString local = (localName ? localName->string() : QString::null);
KDOM::NodeImpl::Id id = implementation()->cdfInterface()->getTagID(local.ascii(), local.length());
switch(id)
{
case ID_SVG:
{
element = new SVGSVGElementImpl(docPtr(), id, prefix);
break;
}
case ID_STYLE:
{
element = new SVGStyleElementImpl(docPtr(), id, prefix);
break;
}
case ID_SCRIPT:
{
element = new SVGScriptElementImpl(docPtr(), id, prefix);
break;
}
case ID_RECT:
{
element = new SVGRectElementImpl(docPtr(), id, prefix);
break;
}
case ID_CIRCLE:
{
element = new SVGCircleElementImpl(docPtr(), id, prefix);
break;
}
case ID_ELLIPSE:
{
element = new SVGEllipseElementImpl(docPtr(), id, prefix);
break;
}
case ID_POLYLINE:
{
element = new SVGPolylineElementImpl(docPtr(), id, prefix);
break;
}
case ID_POLYGON:
{
element = new SVGPolygonElementImpl(docPtr(), id, prefix);
break;
}
case ID_G:
{
element = new SVGGElementImpl(docPtr(), id, prefix);
break;
}
case ID_SWITCH:
{
element = new SVGSwitchElementImpl(docPtr(), id, prefix);
break;
}
case ID_DEFS:
{
element = new SVGDefsElementImpl(docPtr(), id, prefix);
break;
}
case ID_STOP:
{
element = new SVGStopElementImpl(docPtr(), id, prefix);
break;
}
case ID_PATH:
{
element = new SVGPathElementImpl(docPtr(), id, prefix);
break;
}
case ID_IMAGE:
{
element = new SVGImageElementImpl(docPtr(), id, prefix);
break;
}
case ID_CLIPPATH:
{
element = new SVGClipPathElementImpl(docPtr(), id, prefix);
break;
}
case ID_A:
{
element = new SVGAElementImpl(docPtr(), id, prefix);
break;
}
case ID_LINE:
{
element = new SVGLineElementImpl(docPtr(), id, prefix);
break;
}
case ID_LINEARGRADIENT:
{
element = new SVGLinearGradientElementImpl(docPtr(), id, prefix);
break;
}
case ID_RADIALGRADIENT:
{
element = new SVGRadialGradientElementImpl(docPtr(), id, prefix);
break;
}
case ID_TITLE:
{
element = new SVGTitleElementImpl(docPtr(), id, prefix);
break;
}
case ID_DESC:
{
element = new SVGDescElementImpl(docPtr(), id, prefix);
break;
}
case ID_SYMBOL:
{
element = new SVGSymbolElementImpl(docPtr(), id, prefix);
break;
}
case ID_USE:
{
element = new SVGUseElementImpl(docPtr(), id, prefix);
break;
}
case ID_PATTERN:
{
element = new SVGPatternElementImpl(docPtr(), id, prefix);
break;
}
case ID_ANIMATECOLOR:
{
element = new SVGAnimateColorElementImpl(docPtr(), id, prefix);
break;
}
case ID_ANIMATETRANSFORM:
{
element = new SVGAnimateTransformElementImpl(docPtr(), id, prefix);
break;
}
case ID_SET:
{
element = new SVGSetElementImpl(docPtr(), id, prefix);
break;
}
case ID_ANIMATE:
{
element = new SVGAnimateElementImpl(docPtr(), id, prefix);
break;
}
case ID_MARKER:
{
element = new SVGMarkerElementImpl(docPtr(), id, prefix);
break;
}
case ID_VIEW:
{
element = new SVGViewElementImpl(docPtr(), id, prefix);
break;
}
case ID_FILTER:
{
element = new SVGFilterElementImpl(docPtr(), id, prefix);
break;
}
case ID_FEGAUSSIANBLUR:
{
element = new SVGFEGaussianBlurElementImpl(docPtr(), id, prefix);
break;
}
case ID_FEFLOOD:
{
element = new SVGFEFloodElementImpl(docPtr(), id, prefix);
break;
}
case ID_FEBLEND:
{
element = new SVGFEBlendElementImpl(docPtr(), id, prefix);
break;
}
case ID_FEOFFSET:
{
element = new SVGFEOffsetElementImpl(docPtr(), id, prefix);
break;
}
case ID_FECOMPOSITE:
{
element = new SVGFECompositeElementImpl(docPtr(), id, prefix);
break;
}
case ID_FECOLORMATRIX:
{
element = new SVGFEColorMatrixElementImpl(docPtr(), id, prefix);
break;
}
case ID_FEIMAGE:
{
element = new SVGFEImageElementImpl(docPtr(), id, prefix);
break;
}
case ID_FEMERGE:
{
element = new SVGFEMergeElementImpl(docPtr(), id, prefix);
break;
}
case ID_FEMERGENODE:
{
element = new SVGFEMergeNodeElementImpl(docPtr(), id, prefix);
break;
}
case ID_FECOMPONENTTRANSFER:
{
element = new SVGFEComponentTransferElementImpl(docPtr(), id, prefix);
break;
}
case ID_FEFUNCR:
{
element = new SVGFEFuncRElementImpl(docPtr(), id, prefix);
break;
}
case ID_FEFUNCG:
{
element = new SVGFEFuncGElementImpl(docPtr(), id, prefix);
break;
}
case ID_FEFUNCB:
{
element = new SVGFEFuncRElementImpl(docPtr(), id, prefix);
break;
}
case ID_FEFUNCA:
{
element = new SVGFEFuncAElementImpl(docPtr(), id, prefix);
break;
}
case ID_FETURBULENCE:
{
element = new SVGFETurbulenceElementImpl(docPtr(), id, prefix);
break;
}
case ID_TEXT:
{
element = new SVGTextElementImpl(docPtr(), id, prefix);
break;
}
case ID_TSPAN:
{
element = new SVGTSpanElementImpl(docPtr(), id, prefix);
break;
}
case ID_CURSOR:
{
element = new SVGCursorElementImpl(docPtr(), id, prefix);
break;
}
default:
element = 0;
};
return element;
}
KDOM::ElementImpl *SVGDocumentImpl::createElement(KDOM::DOMStringImpl *tagName)
{
if(tagName)
tagName->ref();
SVGElementImpl *elem = createSVGElement(0, tagName);
if(!elem)
return KDOM::DocumentImpl::createElement(tagName);
if(tagName)
tagName->deref();
return elem;
}
KDOM::ElementImpl *SVGDocumentImpl::createElementNS(KDOM::DOMStringImpl *namespaceURIImpl, KDOM::DOMStringImpl *qualifiedName)
{
KDOM::ElementImpl *elem = NULL;
if(namespaceURIImpl)
namespaceURIImpl->ref();
if(qualifiedName)
qualifiedName->ref();
KDOM::DOMStringImpl *prefix = 0, *localName = 0;
KDOM::Helper::SplitPrefixLocalName(qualifiedName, prefix, localName);
if(prefix)
prefix->ref();
if(localName)
localName->ref();
KDOM::DOMString namespaceURI(namespaceURIImpl);
if(!((!prefix || prefix->length() == 0) || KDOM::DOMString(prefix) == "svg") &&
(namespaceURI == KDOM::NS_SVG || namespaceURI.isEmpty()))
elem = KDOM::DocumentImpl::createElementNS(namespaceURIImpl, qualifiedName);
else {
int dummy;
KDOM::Helper::CheckQualifiedName(qualifiedName, namespaceURIImpl, dummy, false, false);
elem = createSVGElement(prefix, localName);
if(!elem)
elem = KDOM::DocumentImpl::createElementNS(namespaceURIImpl, qualifiedName);
}
if(prefix)
prefix->deref();
if(localName)
localName->deref();
if(namespaceURIImpl)
namespaceURIImpl->deref();
if(qualifiedName)
qualifiedName->deref();
return elem;
}
SVGSVGElementImpl *SVGDocumentImpl::rootElement() const
{
KDOM::ElementImpl *elem = documentElement();
if(elem && elem->id() == ID_SVG)
return static_cast<SVGSVGElementImpl *>(elem);
return 0;
}
KDOM::EventImpl *SVGDocumentImpl::createEvent(KDOM::DOMStringImpl *eventTypeImpl)
{
QString eventType = (eventTypeImpl ? eventTypeImpl->string() : QString::null);
if(eventType == QString::fromLatin1("SVGEvents"))
return new SVGEventImpl();
else if(eventType == QString::fromLatin1("SVGZoomEvents"))
return new SVGZoomEventImpl();
return DocumentEventImpl::createEvent(eventTypeImpl);
}
void SVGDocumentImpl::notifyFinished(KDOM::CachedObject *finishedObj)
{
// This is called when a script has finished loading that was requested from
// executeScripts(). We execute the script, and then call executeScripts()
// again to continue iterating through the list of scripts in the document
if(finishedObj == m_cachedScript)
{
KDOM::DOMString scriptSource = m_cachedScript->script();
m_cachedScript->deref(this);
m_cachedScript = 0;
SVGScriptElementImpl::executeScript(this, KDOM::DOMString(scriptSource.string()).handle());
executeScripts(true);
}
}
KSVGView *SVGDocumentImpl::svgView() const
{
return static_cast<KSVGView *>(m_view);
}
#if 0
KDOM::Ecma *SVGDocumentImpl::ecmaEngine() const
{
if(m_ecmaEngine) // No need to initialize anymore...
return m_ecmaEngine;
m_ecmaEngine = new Ecma(const_cast<SVGDocumentImpl *>(this));
m_ecmaEngine->setup(implementation()->cdfInterface());
return m_ecmaEngine;
}
#endif
void SVGDocumentImpl::finishedParsing()
{
addScripts(rootElement());
m_scriptsIt = new Q3PtrListIterator<SVGScriptElementImpl>(m_scripts);
executeScripts(false);
}
void SVGDocumentImpl::dispatchRecursiveEvent(KDOM::EventImpl *event, KDOM::NodeImpl *obj)
{
// Iterate the tree, backwards, and dispatch the event to every child
for(KDOM::NodeImpl *n = obj; n != 0; n = n->previousSibling())
{
if(n->hasChildNodes())
{
// Dispatch to all children
dispatchRecursiveEvent(event, n->lastChild());
// Dispatch, locally
n->dispatchEvent(event);
}
else
n->dispatchEvent(event);
}
}
void SVGDocumentImpl::dispatchZoomEvent(float prevScale, float newScale)
{
// dispatch zoom event
SVGZoomEventImpl *event = static_cast<SVGZoomEventImpl *>(createEvent(KDOM::DOMString("SVGZoomEvents").handle()));
event->ref();
event->initEvent(KDOM::DOMString("zoom").handle(), true, false);
event->setPreviousScale(prevScale);
event->setNewScale(newScale);
rootElement()->dispatchEvent(event);
event->deref();
}
void SVGDocumentImpl::dispatchScrollEvent()
{
// dispatch zoom event
SVGEventImpl *event = static_cast<SVGEventImpl *>(createEvent(KDOM::DOMString("SVGEvents").handle()));
event->ref();
event->initEvent(KDOM::DOMString("scroll").handle(), true, false);
rootElement()->dispatchEvent(event);
event->deref();
}
bool SVGDocumentImpl::dispatchKeyEvent(KDOM::EventTargetImpl *target, QKeyEvent *key, bool keypress)
{
// dispatch key event
KDOM::KeyboardEventImpl *keyEventImpl = static_cast<KDOM::KeyboardEventImpl *>(createEvent(KDOM::DOMString("KeyboardEvents").handle()));
keyEventImpl->ref();
keyEventImpl->initKeyboardEvent(key);
target->dispatchEvent(keyEventImpl);
bool r = /*keyEventImpl->defaultHandled() ||*/ keyEventImpl->defaultPrevented();
keyEventImpl->deref();
return r;
}
KDOM::DOMStringImpl *SVGDocumentImpl::defaultNS() const
{
return KDOM::NS_SVG.handle();
}
void SVGDocumentImpl::recalcStyleSelector()
{
if(!attached())
return;
assert(m_pendingStylesheets == 0);
QString sheetUsed; // Empty sheet
Q3PtrList<KDOM::StyleSheetImpl> oldStyleSheets = m_styleSheets->styleSheets;
m_styleSheets->styleSheets.clear();
for(int i = 0; i < 2; i++)
{
m_availableSheets.clear();
#ifndef APPLE_COMPILE_HACK
m_availableSheets << i18n("Basic Page Style");
#endif
for(KDOM::NodeImpl *n = this; n != 0; n = n->traverseNextNode())
{
KDOM::StyleSheetImpl *sheet = 0;
if(n->nodeType() == KDOM::PROCESSING_INSTRUCTION_NODE)
{
// Processing instruction (XML documents only)
KDOM::ProcessingInstructionImpl *pi = static_cast<KDOM::ProcessingInstructionImpl *>(n);
sheet = pi->sheet();
if(!sheet && pi->localHref() && !pi->localHref()->isEmpty())
{
// Processing instruction with reference to an element in this document
// - e.g. <?xml-stylesheet href="#mystyle">, with the element
// <foo id="mystyle">heading { color: red; }</foo> at some location in the document
KDOM::ElementImpl *elem = getElementById(pi->localHref());
if(elem)
{
KDOM::DOMString sheetText("");
for(KDOM::NodeImpl *c = elem->firstChild(); c != 0; c = c->nextSibling())
{
if(c->nodeType() == KDOM::TEXT_NODE ||
c->nodeType() == KDOM::CDATA_SECTION_NODE)
{
sheetText += KDOM::DOMString(c->nodeValue());
}
}
KDOM::CSSStyleSheetImpl *cssSheet = createCSSStyleSheet(this, 0);
cssSheet->parseString(sheetText.handle());
pi->setStyleSheet(cssSheet);
sheet = cssSheet;
}
}
}
else
{
QString title;
if(n && n->id() == ID_STYLE)
{
// <STYLE> element
SVGStyleElementImpl *s = static_cast<SVGStyleElementImpl*>(n);
if(!s->isLoading())
{
sheet = s->sheet();
if(sheet)
title = KDOM::DOMString(s->getAttribute(ATTR_TITLE)).string();
}
}
if(!title.isEmpty() && sheetUsed.isEmpty())
sheetUsed = title;
if(!title.isEmpty())
{
if(title != sheetUsed)
sheet = 0; // don't use it
title = title.replace('&', QString::fromLatin1("&&"));
if(!m_availableSheets.contains(title))
m_availableSheets.append(title);
}
}
if(sheet)
{
sheet->ref();
m_styleSheets->styleSheets.append(sheet);
}
}
// we're done if we don't select an alternative sheet or we found the sheet we selected
if(sheetUsed.isEmpty() || m_availableSheets.contains(sheetUsed))
break;
}
// De-reference all the stylesheets in the old list
Q3PtrListIterator<KDOM::StyleSheetImpl> it(oldStyleSheets);
for(;it.current(); ++it)
it.current()->deref();
QString userSheet = m_userSheet;
if(m_view && m_view->mediaType() == QString::fromLatin1("print"))
userSheet += m_printSheet;
// Create a new style selector
delete m_styleSelector;
m_styleSelector = createStyleSelector(userSheet);
}
KDOM::CSSStyleSelector *SVGDocumentImpl::createStyleSelector(const QString &usersheet)
{
return new SVGCSSStyleSelector(this, usersheet, m_styleSheets, m_url, false);
}
KCanvas *SVGDocumentImpl::canvas() const
{
return m_canvasView ? m_canvasView->canvas() : 0;
}
void SVGDocumentImpl::attach()
{
if(!canvasView())
return;
assert(!m_styleSelector);
m_styleSelector = createStyleSelector(m_userSheet);
NodeBaseImpl::attach();
}
void SVGDocumentImpl::addScripts(KDOM::NodeImpl *n)
{
if(!n)
return;
// Recursively go through the entire document tree, looking for html <script> tags.
// For each of these that is found, add it to the m_scripts list from which they will be executed
if(n->localId() == ID_SCRIPT)
m_scripts.append(static_cast<SVGScriptElementImpl *>(n));
NodeImpl *child;
for(child = n->firstChild(); child != 0; child = child->nextSibling())
addScripts(child);
}
void SVGDocumentImpl::executeScripts(bool needsStyleSelectorUpdate)
{
// Iterate through all of the svg <script> tags in the document. For those that have a src attribute,
// start loading the script and return (executeScripts() will be called again once the script is loaded
// and continue where it left off). For scripts that don't have a src attribute, execute the code inside the tag
SVGScriptElementImpl *script = 0;
while((script = m_scriptsIt->current()))
{
KDOM::DOMString hrefAttr(script->href()->baseVal());
QString charset; // TODO m_scriptsIt->current()->getAttribute(ATTR_CHARSET).string();
if(!hrefAttr.isEmpty())
{
// we have a src attribute
KURL fullUrl(documentKURI(), hrefAttr.string());
m_cachedScript = docLoader()->requestScript(fullUrl, charset);
++(*m_scriptsIt);
m_cachedScript->ref(this); // will call executeScripts() again if already cached
return;
}
else
{
// no src attribute - execute from contents of tag
SVGScriptElementImpl::executeScript(this, script->textContent());
++(*m_scriptsIt);
needsStyleSelectorUpdate = true;
}
}
// Fire LOAD_EVENT after all scripts are evaluated
if(KDOM::DocumentImpl::hasListenerType(KDOM::LOAD_EVENT) && !m_scriptsIt->current())
{
SVGEventImpl *event = static_cast<SVGEventImpl *>(createEvent(KDOM::DOMString("SVGEvents").handle()));
event->ref();
event->initEvent(KDOM::DOMString("load").handle(), false, false);
dispatchRecursiveEvent(event, lastChild());
event->deref();
}
// All scripts have finished executing, so calculate the
// style for the document and close the last element
if(canvas() && needsStyleSelectorUpdate)
updateStyleSelector();
// Start animations, as "load" scripts are executed.
m_timeScheduler->startAnimations();
}
void SVGDocumentImpl::recalcStyle(StyleChange change)
{
for(NodeImpl *n = firstChild(); n; n = n->nextSibling())
{
if(change >= Inherit || n->hasChangedChild() || n->changed())
n->recalcStyle(change);
}
}
KDOM::CSSStyleSheetImpl *SVGDocumentImpl::createCSSStyleSheet(KDOM::NodeImpl *parent, KDOM::DOMStringImpl *url) const
{
SVGCSSStyleSheetImpl *sheet = new SVGCSSStyleSheetImpl(parent, url);
sheet->ref();
return sheet;
}
KDOM::CSSStyleSheetImpl *SVGDocumentImpl::createCSSStyleSheet(KDOM::CSSRuleImpl *ownerRule, KDOM::DOMStringImpl *url) const
{
SVGCSSStyleSheetImpl *sheet = new SVGCSSStyleSheetImpl(ownerRule, url);
sheet->ref();
return sheet;
}
bool SVGDocumentImpl::prepareMouseEvent(bool, int x, int y, KDOM::MouseEventImpl *event)
{
if(!canvas() || !event)
return false;
QPoint coordinate(x, y);
SVGStyledElementImpl *current = 0;
KCanvasItemList hits;
canvas()->collisions(coordinate, hits);
KCanvasItemList::ConstIterator it = hits.begin();
KCanvasItemList::ConstIterator end = hits.end();
// Check for mouseout/focusout events first..
if(event->id() == KDOM::MOUSEMOVE_EVENT && hits.isEmpty())
{
if(m_lastTarget && m_lastTarget != current)
{
if(m_lastTarget->hasListenerType(KDOM::DOMFOCUSOUT_EVENT))
dispatchUIEvent(m_lastTarget, KDOM::DOMString("DOMFocusOut").handle());
if(m_lastTarget->hasListenerType(KDOM::MOUSEOUT_EVENT))
dispatchMouseEvent(m_lastTarget, KDOM::DOMString("mouseout").handle());
m_lastTarget = 0;
}
}
for(; it != end; ++it)
{
current = static_cast<SVGStyledElementImpl *>((*it)->userData());
if(current)
{
SVGRenderStyle *style = static_cast<SVGRenderStyle *>(current->renderStyle());
if(!style || style->pointerEvents() == PE_NONE)
return false;
bool isStroked = (style->strokePaint() != 0);
bool isVisible = (style->visibility() == KDOM::VS_VISIBLE);
KCanvasItem *canvasItem = current->canvasItem();
bool testFill = false;
bool testStroke = false;
switch(style->pointerEvents())
{
case PE_VISIBLE:
{
testFill = isVisible;
testStroke = isVisible;
break;
}
case PE_VISIBLE_PAINTED:
testStroke = (isVisible && isStroked);
case PE_VISIBLE_FILL:
{
testFill = isVisible;
break;
}
case PE_VISIBLE_STROKE:
{
testStroke = (isVisible && isStroked);
break;
}
case PE_PAINTED:
testStroke = isStroked;
case PE_FILL:
{
testFill = true;
break;
}
case PE_STROKE:
{
testStroke = isStroked;
break;
}
case PE_ALL:
default:
{
testFill = true;
testStroke = true;
}
};
if(testFill || testStroke)
{
if((testFill && canvasItem->fillContains(coordinate)) ||
(testStroke && canvasItem->strokeContains(coordinate)))
{
if(event->id() == KDOM::MOUSEUP_EVENT)
{
if(current->hasListenerType(KDOM::CLICK_EVENT))
dispatchMouseEvent(current, KDOM::DOMString("click").handle());
if(current->hasListenerType(KDOM::DOMACTIVATE_EVENT))
dispatchUIEvent(current, KDOM::DOMString("DOMActivate").handle());
if(current->hasListenerType(KDOM::DOMFOCUSIN_EVENT))
dispatchUIEvent(current, KDOM::DOMString("DOMFocusIn").handle());
}
else if(event->id() == KDOM::MOUSEMOVE_EVENT)
{
if(m_lastTarget && m_lastTarget != current)
{
if(m_lastTarget->hasListenerType(KDOM::DOMFOCUSOUT_EVENT))
dispatchUIEvent(m_lastTarget, KDOM::DOMString("DOMFocusOut").handle());
if(m_lastTarget->hasListenerType(KDOM::MOUSEOUT_EVENT))
dispatchMouseEvent(m_lastTarget, KDOM::DOMString("mouseout").handle());
m_lastTarget = 0;
}
if(current->hasListenerType(KDOM::MOUSEOVER_EVENT))
{
if(m_lastTarget != current)
dispatchMouseEvent(current, KDOM::DOMString("mouseover").handle());
}
}
m_lastTarget = current;
event->setRelatedTarget(current);
// TODO : investigate hasListenerType
// in some cases default actions need
// to take place when there are no
// listeners, so dispatchEvent has to be
// called, that is the problem here...
//if(current->hasListenerType(event->id()))
current->dispatchEvent(event);
return true;
}
}
}
}
return false;
}
TimeScheduler *SVGDocumentImpl::timeScheduler() const
{
return m_timeScheduler;
}
void SVGDocumentImpl::dispatchUIEvent(KDOM::EventTargetImpl *target, KDOM::DOMStringImpl *type)
{
// Setup kdom 'UIEvent'...
KDOM::DOMStringImpl *eventType = new KDOM::DOMStringImpl("UIEvents");
eventType->ref();
KDOM::UIEventImpl *event = static_cast<KDOM::UIEventImpl *>(createEvent(eventType));
event->ref();
event->initUIEvent(type, true, true, 0, 0);
target->dispatchEvent(event);
event->deref();
eventType->deref();
}
void SVGDocumentImpl::dispatchMouseEvent(KDOM::EventTargetImpl *target, KDOM::DOMStringImpl *type)
{
// Setup kdom 'MouseEvent'...
KDOM::DOMStringImpl *eventType = new KDOM::DOMStringImpl("MouseEvents");
eventType->ref();
KDOM::MouseEventImpl *event = static_cast<KDOM::MouseEventImpl *>(createEvent(eventType));
event->ref();
event->initEvent(type, true, true);
target->dispatchEvent(event);
event->deref();
eventType->deref();
}
// vim:ts=4:noet