blob: dba752fdf71c437aa43b2dcf8c61a810d823afa8 [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 "config.h"
#include <kdom/kdom.h>
#include <kdom/core/AttrImpl.h>
#include <kdom/core/domattrs.h>
#include <kdom/core/CDFInterface.h>
#include <kdom/DOMString.h>
#include <kcanvas/KCanvas.h>
#include <kcanvas/KCanvasItem.h>
#include <kcanvas/KCanvasCreator.h>
#include <kcanvas/device/KRenderingDevice.h>
#include "ksvg.h"
#include "KSVGSettings.h"
#include "SVGMatrixImpl.h"
#include "SVGRenderStyle.h"
#include "SVGElementImpl.h"
#include "SVGDocumentImpl.h"
#include "SVGSVGElementImpl.h"
#include "SVGCSSStyleSelector.h"
#include "SVGTransformableImpl.h"
#include "SVGStyledElementImpl.h"
#include "SVGAnimatedStringImpl.h"
#include "KCanvasRenderingStyle.h"
#include "SVGDOMImplementationImpl.h"
#include "SVGCSSStyleDeclarationImpl.h"
#include "svgattrs.h"
#include <ksvg2/css/cssproperties.h>
using namespace KSVG;
SVGStyledElementImpl::SVGStyledElementImpl(KDOM::DocumentPtr *doc, KDOM::NodeImpl::Id id, KDOM::DOMStringImpl *prefix)
: SVGElementImpl(doc, id, prefix), SVGStylableImpl(), m_pa(0), m_className(0)
{
m_canvasItem = 0;
m_renderStyle = 0;
m_updateVectorial = false;
}
SVGStyledElementImpl::~SVGStyledElementImpl()
{
if(m_className)
m_className->deref();
if(m_pa)
m_pa->deref();
delete m_renderStyle;
}
SVGAnimatedStringImpl *SVGStyledElementImpl::className() const
{
if(!m_className)
{
m_className = new SVGAnimatedStringImpl(0); // TODO: use notification context?
m_className->ref();
}
return m_className;
}
KDOM::CSSStyleDeclarationImpl *SVGStyledElementImpl::style()
{
return styleRules();
}
KDOM::CSSStyleDeclarationImpl *SVGStyledElementImpl::pa() const
{
if(!m_pa)
{
m_pa = new SVGCSSStyleDeclarationImpl(ownerDocument()->implementation()->cdfInterface(), 0);
m_pa->ref();
}
return m_pa;
}
KDOM::CSSValueImpl *SVGStyledElementImpl::getPresentationAttribute(KDOM::DOMStringImpl *name)
{
return pa()->getPropertyCSSValue(name);
}
KCanvasItem *SVGStyledElementImpl::createCanvasItem(KCanvas *canvas, KRenderingStyle *style) const
{
KCPathDataList pathData = toPathData();
if(pathData.isEmpty())
return 0;
KCanvasItem *item = KCanvasCreator::self()->createPathItem(canvas, style, pathData);
// Associate 'KCanvasItem' with 'SVGStyledElementImpl'
item->setUserData(const_cast<SVGStyledElementImpl *>(this));
return item;
}
void SVGStyledElementImpl::parseAttribute(KDOM::AttributeImpl *attr)
{
int id = (attr->id() & NodeImpl_IdLocalMask);
KDOM::DOMStringImpl *value = attr->value();
switch(id)
{
case ATTR_CLASS:
{
className()->setBaseVal(value);
break;
}
case ATTR_STYLE:
{
style()->setProperty(value);
break;
}
default:
{
KDOM::CDFInterface *interface = (ownerDocument() ? ownerDocument()->implementation()->cdfInterface() : 0);
if(interface)
{
QString qProp = QString::fromLatin1(interface->getAttrName(id));
int svgPropId = interface->getPropertyID(qProp.ascii(), qProp.length());
if(svgPropId > 0)
{
if(value)
pa()->setProperty(svgPropId, value);
else
pa()->removeProperty(svgPropId);
if(!ownerDocument()->parsing())
setChanged(true);
return;
}
}
SVGElementImpl::parseAttribute(attr);
}
};
}
KDOM::RenderStyle *SVGStyledElementImpl::renderStyle() const
{
if(!m_renderStyle)
{
kdDebug(26002) << "[SVGStyledElementImpl::renderStyle] Updating rendering style of " << KDOM::DOMString(localName()) << " ..." << endl;
SVGDocumentImpl *doc = static_cast<SVGDocumentImpl *>(ownerDocument());
if(!doc)
return 0;
KDOM::CSSStyleSelector *styleSelector = doc->styleSelector();
if(!styleSelector)
return 0;
m_renderStyle = styleSelector->styleForElement(const_cast<SVGStyledElementImpl *>(this));
}
else
kdDebug(26002) << "[SVGStyledElementImpl::renderStyle] Getting cached rendering style of " << KDOM::DOMString(localName()) << " ..." << endl;
return m_renderStyle;
}
void SVGStyledElementImpl::notifyAttributeChange() const
{
// For most cases we need to handle vectorial data changes (ie. rect x changed)
if(!ownerDocument()->parsing())
{
// TODO: Use a more optimized way of updating, means not calling updateCanvasItem() here!
const_cast<SVGStyledElementImpl *>(this)->m_updateVectorial = true;
const_cast<SVGStyledElementImpl *>(this)->updateCanvasItem();
}
}
void SVGStyledElementImpl::finalizeStyle(KCanvasRenderingStyle *style, bool needFillStrokeUpdate)
{
if(needFillStrokeUpdate)
{
style->updateFill(m_canvasItem);
style->updateStroke(m_canvasItem);
}
}
void SVGStyledElementImpl::attach()
{
kdDebug(26002) << "[SVGStyledElementImpl::attach] About to attach canvas item for element " << KDOM::DOMString(localName()) << endl;
SVGDocumentImpl *doc = static_cast<SVGDocumentImpl *>(ownerDocument());
KCanvas *canvas = (doc ? doc->canvas() : 0);
if(canvas && implementsCanvasItem())
{
SVGRenderStyle *svgStyle = static_cast<SVGRenderStyle *>(renderStyle());
KCanvasRenderingStyle *renderingStyle = new KCanvasRenderingStyle(canvas, svgStyle);
if (m_canvasItem)
fprintf(stderr, "DOUBLE ATTACH: <%s> %p\n", KDOM::DOMString(nodeName()).string().ascii(), this);
m_canvasItem = createCanvasItem(canvas, renderingStyle);
if(m_canvasItem)
{
finalizeStyle(renderingStyle);
// Apply transformations if possible...
SVGLocatableImpl *locatable = dynamic_cast<SVGLocatableImpl *>(this);
if(locatable)
{
SVGMatrixImpl *ctm = locatable->getScreenCTM();
renderingStyle->setObjectMatrix(KCanvasMatrix(ctm->qmatrix()));
ctm->deref();
}
SVGElementImpl *parentElement = svg_dynamic_cast(parentNode());
SVGStyledElementImpl *styledParent = NULL;
if (parentElement && parentElement->isStyled())
styledParent = static_cast<SVGStyledElementImpl *>(parentElement);
if(styledParent && styledParent->canvasItem() && styledParent->allowAttachChildren(this))
styledParent->canvasItem()->appendItem(m_canvasItem);
else if (styledParent && (styledParent->id() != ID_PATTERN)) {
// FIXME: This exists until we can find a better way to create root && pattern nodes. -- ecs 8/7/05
delete m_canvasItem;
m_canvasItem = NULL;
//fprintf(stderr, "FAILED CANVAS INSERTION: <%s>\n", KDOM::DOMString(nodeName()).string().ascii())
}
#ifndef APPLE_COMPILE_HACK
KSVGSettings settings;
if(settings.progressiveRendering() && m_canvasItem->isVisible())
m_canvasItem->invalidate(); // Causes defered redraw...
#endif
}
else
{
delete renderingStyle;
renderingStyle = NULL;
}
kdDebug(26002) << "[SVGStyledElementImpl::attach] Attachted canvas item: " << m_canvasItem << endl;
}
else
kdDebug(26002) << "[SVGStyledElementImpl::attach] Unable to attach canvas item. (not necessarily wrong!)" << endl;
KDOM::NodeBaseImpl::attach();
}
void SVGStyledElementImpl::detach()
{
kdDebug(26002) << "[SVGStyledElementImpl::detach] About to detach canvas item for element " << KDOM::DOMString(localName()) << endl;
if(m_canvasItem)
{
SVGElementImpl *parent = svg_dynamic_cast(parentNode());
SVGStyledElementImpl *styled = NULL;
if (parent && parent->isStyled())
styled = static_cast<SVGStyledElementImpl *>(parent);
if(styled && styled->canvasItem())
styled->canvasItem()->removeItem(m_canvasItem);
}
kdDebug(26002) << "[SVGStyledElementImpl::detach] Detached canvas item: " << m_canvasItem << endl;
m_canvasItem = 0;
delete m_renderStyle;
m_renderStyle = 0;
KDOM::NodeBaseImpl::detach();
}
KCanvasItem *SVGStyledElementImpl::canvasItem() const
{
return m_canvasItem;
}
KCanvas *SVGStyledElementImpl::canvas() const
{
SVGDocumentImpl *doc = static_cast<SVGDocumentImpl *>(ownerDocument());
return doc ? doc->canvas() : 0;
}
KCanvasView *SVGStyledElementImpl::canvasView() const
{
SVGDocumentImpl *doc = static_cast<SVGDocumentImpl *>(ownerDocument());
return doc ? doc->canvasView() : 0;
}
void SVGStyledElementImpl::updateCanvasItem()
{
if(!m_canvasItem || !m_updateVectorial)
return;
if(canvas())
{
bool renderSection = false;
SVGStyledElementImpl *parent = dynamic_cast<SVGStyledElementImpl *>(parentNode());
if(parent && parent->canvasItem() && parent->allowAttachChildren(this))
renderSection = true;
// Invalidate before the change...
if(renderSection)
m_canvasItem->invalidate();
KCanvasUserData newPath = KCanvasCreator::self()->createCanvasPathData(canvas(), toPathData());
m_canvasItem->changePath(newPath);
// Invalidate after the change...
if(renderSection)
m_canvasItem->invalidate();
m_updateVectorial = false;
}
}
void SVGStyledElementImpl::recalcStyle(StyleChange change)
{
KDOM::RenderStyle *_style = m_renderStyle;
bool hasParentRenderer = parentNode() ? parentNode()->attached() : false;
if(hasParentRenderer && (change >= Inherit) || changed())
{
KDOM::EDisplay oldDisplay = _style ? _style->display() : KDOM::DS_NONE;
KDOM::RenderStyle *newStyle = ownerDocument()->styleSelector()->styleForElement(this);
newStyle->ref();
StyleChange ch = diff(_style, newStyle);
if(ch != NoChange)
{
if(oldDisplay != newStyle->display())
{
if(attached()) detach();
// ### uuhm, suboptimal. style gets calculated again
attach();
// attach recalulates the style for all children. No need to do it twice.
setChanged(false);
setHasChangedChild(false);
newStyle->deref();
return;
}
if(newStyle)
setStyle(newStyle);
}
newStyle->deref();
if(change != Force)
{
if(ownerDocument()->usesDescendantRules())
change = Force;
else
change = ch;
}
}
for(NodeImpl *n = firstChild(); n; n = n->nextSibling())
{
if(change >= Inherit || n->hasChangedChild() || n->changed())
n->recalcStyle(change);
}
}
const SVGStyledElementImpl *SVGStyledElementImpl::pushAttributeContext(const SVGStyledElementImpl *)
{
if(canvas())
{
KCanvasUserData newPath = KCanvasCreator::self()->createCanvasPathData(canvas(), toPathData());
m_canvasItem->changePath(newPath);
}
return 0;
}
void SVGStyledElementImpl::setStyle(KDOM::RenderStyle *newStyle)
{
KDOM_SAFE_SET(m_renderStyle, newStyle);
KCanvasRenderingStyle *style = (m_canvasItem ? static_cast<KCanvasRenderingStyle *>(m_canvasItem->style()) : 0);
if(style)
{
bool renderSection = false;
SVGStyledElementImpl *parent = dynamic_cast<SVGStyledElementImpl *>(parentNode());
if(parent && parent->canvasItem() && parent->allowAttachChildren(this))
renderSection = true;
// Invalidate before the change...
if(renderSection)
m_canvasItem->invalidate();
style->removeClipPaths();
style->updateStyle(static_cast<SVGRenderStyle *>(m_renderStyle), m_canvasItem);
finalizeStyle(style, false);
// Invalidate after the change...
if(renderSection)
m_canvasItem->invalidate();
}
}
void SVGStyledElementImpl::updateCTM(const QWMatrix &ctm)
{
if(!m_canvasItem)
return;
KCanvas *canvas = (m_canvasItem ? m_canvasItem->canvas() : 0);
if(canvas)
{
// Invalidate before the change...
m_canvasItem->invalidate();
KCanvasRenderingStyle *style = static_cast<KCanvasRenderingStyle *>(m_canvasItem->style());
style->setObjectMatrix(KCanvasMatrix(ctm));
// Invalidate after the change...
m_canvasItem->invalidate();
}
}
// vim:ts=4:noet