/*
* This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
*
* Copyright 2008-2015 Geosparc nv, http://www.geosparc.com/, Belgium.
*
* The program is available in open source according to the GNU Affero
* General Public License. All contributions in this program are covered
* by the Geomajas Contributors License Agreement. For full licensing
* details, see LICENSE.txt in the project root.
*/
package org.geomajas.gwt.client.gfx.context;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
import org.geomajas.geometry.Coordinate;
import org.geomajas.gwt.client.controller.GraphicsController;
import org.geomajas.gwt.client.gfx.PaintableGroup;
import org.geomajas.gwt.client.gfx.style.FontStyle;
import org.geomajas.gwt.client.gfx.style.PictureStyle;
import org.geomajas.gwt.client.gfx.style.ShapeStyle;
import org.geomajas.gwt.client.gfx.style.Style;
import org.geomajas.gwt.client.spatial.Matrix;
import org.geomajas.gwt.client.util.Dom;
import org.geomajas.gwt.client.util.Log;
import com.google.gwt.dom.client.Node;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.dom.client.DoubleClickEvent;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseWheelEvent;
import com.google.gwt.event.dom.client.TouchCancelEvent;
import com.google.gwt.event.dom.client.TouchEndEvent;
import com.google.gwt.event.dom.client.TouchMoveEvent;
import com.google.gwt.event.dom.client.TouchStartEvent;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.HasHandlers;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.EventListener;
/**
* Helper class that provides a mapping between a DOM tree - consisting of groups (DIV, SVG or VML group) and elements
* (HTML, SVG or VML) - and a set of Java objects. Shields the caller from id management by mapping object references to
* unique document ids.
*
* @see org.geomajas.gwt.client.gfx.GraphicsContext
*
* @author Kristof Heirwegh
* @author Jan De Moerloose
*/
public class DomHelper {
/**
* Mapping between objects and DOM id's.
*/
private Map<Object, String> groupToId = new IdentityHashMap<Object, String>();
/**
* Mapping between DOM id's and objects.
*/
private Map<String, Object> idToGroup = new HashMap<String, Object>();
/**
* Mapping between objects and DOM id's.
*/
private Map<String, String> elementToName = new HashMap<String, String>();
/**
* The root element.
*/
private Element rootElement;
/**
* The name space.
*/
private Namespace namespace;
/**
* Namespace enumeration.
*
* @author Jan De Moerloose
*
*/
public enum Namespace {
HTML, VML, SVG
}
public DomHelper(Element rootElement, Namespace namespace) {
this.rootElement = rootElement;
this.namespace = namespace;
}
/**
* Create or update a one-of child element in the DOM. Needs no id as it will be created/deleted with the parent.
*
* @param parent the parent element
* @param type the type of the element (tag name, e.g. 'image')
* @return the created or updated element or null if creation failed
*/
public Element createOrUpdateSingleChild(Element parent, String type) {
Element result = null;
if (parent.getElementsByTagName(type).getLength() == 0) {
switch (namespace) {
case HTML:
result = Dom.createElementNS(Dom.NS_HTML, type);
break;
case SVG:
result = Dom.createElementNS(Dom.NS_SVG, type);
break;
case VML:
result = Dom.createElementNS(Dom.NS_VML, type);
break;
}
parent.appendChild(result);
return result;
} else {
return (Element) (parent.getElementsByTagName(type).getItem(0));
}
}
/**
* Create or update an element in the DOM. The id will be generated.
*
* @param parent
* the parent group
* @param name
* the local group name of the element (should be unique within the group)
* @param type
* the type of the element (tag name, e.g. 'image')
* @param style
* The style to apply on the element.
* @return the created or updated element or null if creation failed
*/
public Element createOrUpdateElement(Object parent, String name, String type, Style style) {
return createOrUpdateElement(parent, name, type, style, true);
}
/**
* Create or update an element in the DOM. The id will be generated.
*
* @param parent
* the parent group
* @param name
* the local group name of the element (should be unique within the group)
* @param type
* the type of the element (tag name, e.g. 'image')
* @param style
* The style to apply on the element.
* @param generateId
* true if a unique id may be generated. If false, the name will be used as id and should therefore be
* unique
* @return the created or updated element or null if creation failed or name was null
*/
public Element createOrUpdateElement(Object parent, String name, String type, Style style, boolean generateId) {
Element element;
// check existence
if (name != null) {
if (!generateId) {
element = Dom.getElementById(name);
} else {
element = getElement(parent, name);
}
} else {
return null;
}
if (element == null) {
// Element was not found, so create it:
element = createElement(parent, name, type, style, generateId);
} else {
// Element was found, so update it:
applyStyle(element, style);
}
// no luck !
if (element == null) {
return null;
}
return element;
}
/**
* Creates a group element in the technology (SVG/VML/...) of this context. A group is meant to group other elements
* together.
*
* @param parent
* parent group object
* @param object
* group object
*/
public void drawGroup(Object parent, Object object) {
createOrUpdateGroup(parent, object, null, null);
}
/**
* Creates a group element in the technology (SVG/VML/...) of this context with the specified tag name.
*
* @param parent
* parent group object
* @param object
* group object
* @param tagName
* the tag name
* @return element for the group
*/
public Element drawGroup(Object parent, Object object, String tagName) {
switch (namespace) {
case SVG:
return createGroup(Dom.NS_SVG, parent, object, tagName);
case VML:
return createGroup(Dom.NS_VML, parent, object, tagName);
case HTML:
default:
return createGroup(Dom.NS_HTML, parent, object, tagName);
}
}
/**
* Creates a group element in the technology (SVG/VML/...) of this context. A group is meant to group other elements
* together, possibly applying a transformation upon them.
*
* @param parent
* parent group object
* @param object
* group object
* @param transformation
* On each group, it is possible to apply a matrix transformation (currently translation only). This is
* the real strength of a group element.
*/
public void drawGroup(Object parent, Object object, Matrix transformation) {
createOrUpdateGroup(parent, object, transformation, null);
}
/**
* Creates a group element in the technology (SVG/VML/...) of this context. A group is meant to group other elements
* together, and in this case applying a style on them.
*
* @param parent
* parent group object
* @param object
* group object
* @param style
* Add a style to a group.
*/
public void drawGroup(Object parent, Object object, Style style) {
createOrUpdateGroup(parent, object, null, style);
}
/**
* Creates a group element in the technology (SVG/VML/...) of this context. A group is meant to group other elements
* together, possibly applying a transformation upon them.
*
* @param parent
* parent group object
* @param object
* group object
* @param transformation
* On each group, it is possible to apply a matrix transformation (currently translation only). This is
* the real strength of a group element.
* @param style
* Add a style to a group.
*/
public void drawGroup(Object parent, Object object, Matrix transformation, Style style) {
createOrUpdateGroup(parent, object, transformation, style);
}
/**
* Move an element from on group to another. The elements name will remain the same.
*
* @param name
* The name of the element within the sourceParent group.
* @param sourceParent
* The original parent group of the element.
* @param targetParent
* The target parent group for the element.
* @return true when move was successful
*/
public boolean moveElement(String name, Object sourceParent, Object targetParent) {
Element sourceGroup = null;
Element targetGroup = null;
Element element = null;
if (sourceParent != null) {
sourceGroup = getGroup(sourceParent);
element = getElement(sourceParent, name);
}
if (targetParent != null) {
targetGroup = getGroup(targetParent);
}
if (sourceGroup == null || targetGroup == null) {
return false;
}
if (Dom.isOrHasChild(sourceGroup, element)) {
Dom.removeChild(sourceGroup, element);
String newId = Dom.assembleId(targetGroup.getId(), name);
elementToName.remove(element.getId());
elementToName.put(newId, name);
Dom.setElementAttribute(element, "id", newId);
Dom.appendChild(targetGroup, element);
return true;
}
return false;
}
/**
* Return the id of the specified group.
*
* @param group
* the group object
* @return the corresponding element id or null if the group has not been drawn.
*/
public String getId(Object group) {
return groupToId.get(group);
}
/**
* Creates a group element in the technology (SVG/VML/...) of this context. A group is meant to group other elements
* together. Also this method gives you the opportunity to specify a specific width and height.
*
* @param parent
* parent group object
* @param object
* group object
* @param transformation
* On each group, it is possible to apply a matrix transformation (currently translation only). This is
* the real strength of a group element. Never apply transformations on any other kind of element.
* @param style
* Add a style to a group.
* @return the group element
*/
public Element createOrUpdateGroup(Object parent, Object object, Matrix transformation, Style style) {
switch (namespace) {
case SVG:
return createSvgGroup(parent, object, transformation, style);
case VML:
return createVmlGroup(parent, object, transformation);
case HTML:
default:
return createHtmlGroup(parent, object, transformation, style);
}
}
/**
* Within a certain group, bring an element to the front. This will make sure it's visible (within that group).
*
* @param object
* The group wherein to search for the element.
* @param name
* The name of the element to bring to the front.
* @since 1.10.0
*/
public void bringToFront(Object parent, String name) {
Element parentElement = getGroup(parent);
if (parentElement == null) {
throw new IllegalArgumentException("bringToFront failed: could not find parent group.");
}
Element element = getElement(parent, name);
if (element == null) {
throw new IllegalArgumentException("bringToFront failed: could not find element within group.");
}
if (parentElement.getLastChild() != element) {
parentElement.appendChild(element);
}
}
/**
* Within a certain group, move an element to the back. All siblings will be rendered after this one.
*
* @param object
* The group wherein to search for the element.
* @param name
* The name of the element to move to the back.
* @since 1.10.0
*/
public void moveToBack(Object parent, String name) {
Element parentElement = getGroup(parent);
if (parentElement == null) {
throw new IllegalArgumentException("moveToBack failed: could not find parent group.");
}
Element element = getElement(parent, name);
if (element == null) {
throw new IllegalArgumentException("moveToBack failed: could not find element within group.");
}
if (parentElement.getFirstChildElement() != element) {
parentElement.insertFirst(element);
}
}
private Element createSvgGroup(Object parent, Object object, Matrix transformation, Style style) {
Element group = null;
// check existence
if (object != null) {
group = getGroup(object);
}
// create if necessary
if (group == null) {
group = createGroup(Dom.NS_SVG, parent, object, "g");
}
// Apply transformation on the element:
if (transformation != null) {
Dom.setElementAttribute(group, "transform", parse(transformation));
}
// SVG style is just CSS, so ok for both
if (style != null) {
applyStyle(group, style);
}
return group;
}
private Element createVmlGroup(Object parent, Object object, Matrix transformation) {
Element group = null;
// check existence
if (object != null) {
group = getGroup(object);
}
// create if necessary
if (group == null) {
group = createGroup(Dom.NS_VML, parent, object, "group");
}
if (group != null) {
// Get the parent element:
Element parentElement;
if (parent == null) {
parentElement = getRootElement();
} else {
parentElement = getGroup(parent);
}
// Inherit size from parent if not specified
if (parentElement != null) {
String width = Dom.getStyleAttribute(parentElement, "width");
String height = Dom.getStyleAttribute(parentElement, "height");
// sizes should be numbers + px
int w = Integer.parseInt(width.substring(0, width.indexOf('p')));
int h = Integer.parseInt(height.substring(0, height.indexOf('p')));
applyElementSize(group, w, h, true);
}
// Apply element transformation:
if (transformation != null) {
applyAbsolutePosition(group, new Coordinate(transformation.getDx(), transformation.getDy()));
} else {
applyAbsolutePosition(group, new Coordinate(0, 0));
}
}
return group;
}
private Element createHtmlGroup(Object parent, Object object, Matrix transformation, Style style) {
Element group = null;
// check existence
if (object != null) {
group = getGroup(object);
}
// create if necessary
if (group == null) {
group = createGroup(Dom.NS_HTML, parent, object, "div");
}
// Apply width, height default 100%
Dom.setStyleAttribute(group, "width", "100%");
Dom.setStyleAttribute(group, "height", "100%");
// Apply transformation on the element:
if (transformation != null) {
applyAbsolutePosition(group, new Coordinate(transformation.getDx(), transformation.getDy()));
} else {
applyAbsolutePosition(group, new Coordinate(0, 0));
}
// SVG style is just CSS, so ok for both
if (style != null) {
applyStyle(group, style);
}
return group;
}
/**
* Apply an absolute position on an element.
*
* @param element
* The element that needs an absolute position.
* @param position
* The position as a Coordinate.
*/
private void applyAbsolutePosition(Element element, Coordinate position) {
Dom.setStyleAttribute(element, "position", "absolute");
Dom.setStyleAttribute(element, "left", (int) position.getX() + "px");
Dom.setStyleAttribute(element, "top", (int) position.getY() + "px");
}
/**
* Apply a size on an element.
*
* @param element
* The element that needs sizing.
* @param width
* The new width to apply on the element.
* @param height
* The new height to apply on the element.
* @param addCoordSize
* Should a coordsize attribute be added as well?
*/
private void applyElementSize(Element element, int width, int height, boolean addCoordSize) {
if (width >= 0 && height >= 0) {
if (addCoordSize) {
Dom.setElementAttribute(element, "coordsize", width + " " + height);
}
Dom.setStyleAttribute(element, "width", width + "px");
Dom.setStyleAttribute(element, "height", height + "px");
}
}
/**
* Parse a matrix object into a string, suitable for the SVG 'transform' attribute.
*
* @param matrix
* The matrix to parse.
* @return The transform string.
*/
private String parse(Matrix matrix) {
String transform = "";
if (matrix != null) {
double dx = matrix.getDx();
double dy = matrix.getDy();
if (matrix.getXx() != 0 && matrix.getYy() != 0 && matrix.getXx() != 1 && matrix.getYy() != 1) {
transform += "scale(" + matrix.getXx() + ", " + matrix.getYy() + ")"; // scale first
// no space between 'scale' and '(' !!!
dx /= matrix.getXx();
dy /= matrix.getYy();
}
transform += " translate(" + (float) dx + ", " + (float) dy + ")";
// no space between 'translate' and '(' !!!
}
return transform;
}
/**
* Set the controller on an element of this <code>GraphicsContext</code> so it can react to events.
*
* @param object
* the element on which the controller should be set.
* @param controller
* The new <code>GraphicsController</code>
*/
public void setController(Object object, GraphicsController controller) {
// set them all
doSetController(getGroup(object), controller, Event.MOUSEEVENTS | Event.ONDBLCLICK | Event.ONMOUSEWHEEL);
}
/**
* Set the controller on an element of this <code>GraphicsContext</code> so it can react to events.
*
* @param parent
* the parent of the element on which the controller should be set.
* @param name
* the name of the child element on which the controller should be set
* @param controller
* The new <code>GraphicsController</code>
*/
public void setController(Object parent, String name, GraphicsController controller) {
// set them all
doSetController(getElement(parent, name), controller, Event.TOUCHEVENTS | Event.MOUSEEVENTS | Event.ONDBLCLICK
| Event.ONMOUSEWHEEL);
}
/**
* Set the controller on an element of this <code>GraphicsContext</code> so it can react to events.
*
* @param object
* the element on which the controller should be set.
* @param controller
* The new <code>GraphicsController</code>
* @param eventMask
* a bitmask to specify which events to listen for {@link com.google.gwt.user.client.Event}
*/
public void setController(Object object, GraphicsController controller, int eventMask) {
doSetController(getGroup(object), controller, eventMask);
}
/**
* Set the controller on an element of this <code>GraphicsContext</code> so it can react to events.
*
* @param parent
* the parent of the element on which the controller should be set.
* @param name
* the name of the child element on which the controller should be set
* @param controller
* The new <code>GraphicsController</code>
* @param eventMask
* a bitmask to specify which events to listen for {@link com.google.gwt.user.client.Event}
*/
public void setController(Object parent, String name, GraphicsController controller, int eventMask) {
doSetController(getElement(parent, name), controller, eventMask);
}
/**
* Set the controller on an element of this <code>GraphicsContext</code> so it can react to events.
*
* @param element
* the element on which the controller should be set
* @param controller
* The new <code>GraphicsController</code>
* @param eventMask
* a bitmask to specify which events to listen for {@link com.google.gwt.user.client.Event}
*/
protected void doSetController(Element element, GraphicsController controller, int eventMask) {
if (element != null) {
Dom.setEventListener(element, new EventListenerHelper(element, controller, eventMask));
Dom.sinkEvents(element, eventMask);
}
}
/**
* Set a specific cursor on an element of this <code>GraphicsContext</code>.
*
* @param object
* the element on which the controller should be set.
* @param cursor
* The string representation of the cursor to use.
*/
public void setCursor(Object object, String cursor) {
if (object == null) {
Dom.setStyleAttribute(getRootElement(), "cursor", cursor);
} else {
Element element = getGroup(object);
if (element != null) {
Dom.setStyleAttribute(element, "cursor", cursor);
}
}
}
/**
* Set a specific cursor on an element of this <code>GraphicsContext</code>.
*
* @param parent
* the parent of the element on which the cursor should be set.
* @param name
* the name of the child element on which the cursor should be set
* @param cursor
* The string representation of the cursor to use.
*/
public void setCursor(Object parent, String name, String cursor) {
Element element = getElement(parent, name);
if (element != null) {
Dom.setStyleAttribute(element, "cursor", cursor);
}
}
// ----------------------------------------------------------
/**
* Internal class to pass DOM-events to a GraphicsController
*
* @author Kristof Heirwegh
*/
private class EventListenerHelper implements EventListener {
private final Element e;
private final HandlerManager hm;
public EventListenerHelper(Element e, GraphicsController gc, int eventMask) {
this.e = e;
this.hm = new HandlerManager(e);
if ((Event.ONMOUSEDOWN & eventMask) > 0) {
hm.addHandler(MouseDownEvent.getType(), gc);
}
if ((Event.ONMOUSEUP & eventMask) > 0) {
hm.addHandler(MouseUpEvent.getType(), gc);
}
if ((Event.ONMOUSEOUT & eventMask) > 0) {
hm.addHandler(MouseOutEvent.getType(), gc);
}
if ((Event.ONMOUSEOVER & eventMask) > 0) {
hm.addHandler(MouseOverEvent.getType(), gc);
}
if ((Event.ONMOUSEMOVE & eventMask) > 0) {
hm.addHandler(MouseMoveEvent.getType(), gc);
}
if ((Event.ONMOUSEWHEEL & eventMask) > 0) {
hm.addHandler(MouseWheelEvent.getType(), gc);
}
if ((Event.ONDBLCLICK & eventMask) > 0) {
hm.addHandler(DoubleClickEvent.getType(), gc);
}
if ((Event.ONTOUCHSTART & eventMask) > 0) {
hm.addHandler(TouchStartEvent.getType(), gc);
}
if ((Event.ONTOUCHEND & eventMask) > 0) {
hm.addHandler(TouchEndEvent.getType(), gc);
}
if ((Event.ONTOUCHMOVE & eventMask) > 0) {
hm.addHandler(TouchMoveEvent.getType(), gc);
}
if ((Event.ONTOUCHCANCEL & eventMask) > 0) {
hm.addHandler(TouchCancelEvent.getType(), gc);
}
}
@SuppressWarnings("deprecation")
public void onBrowserEvent(Event event) {
// copied from Widget class to mimic behaviour of other widgets
switch (Dom.eventGetType(event)) {
case Event.ONMOUSEOVER:
// Only fire the mouse over event if it's coming from outside this
// widget.
case Event.ONMOUSEOUT:
// Only fire the mouse out event if it's leaving this
// widget.
Element related = event.getRelatedEventTarget().cast();
if (related != null && Dom.isOrHasChild(e, related)) {
return;
}
break;
}
DomEvent.fireNativeEvent(event, new HasHandlers() {
public void fireEvent(GwtEvent<?> event) {
hm.fireEvent(event);
}
}, e);
}
}
/**
* Returns the root element of this context.
*
* @return the root element
*/
public Element getRootElement() {
return rootElement;
}
/**
* Returns the group that corresponds with this group object.
*
* @param object
* group object
* @return the group or null if it does not exist
*/
public Element getGroup(Object object) {
return Dom.getElementById(groupToId.get(object));
}
/**
* Return the (enclosing) group for the specified element id.
*
* @param id
* element id
* @return the group object
*/
public Object getGroupById(String id) {
String name = elementToName.get(id);
if (name != null) {
return idToGroup.get(Dom.disAssembleId(id, name));
} else {
return null;
}
}
/**
* Return the element name for the specified id.
*
* @param id
* element id
* @return the name of the element
*/
public String getNameById(String id) {
return elementToName.get(id);
}
/**
* Generic creation method for group elements.
*
* @param namespace
* the name space (HTML, SVG or VML,...)
* @param parent
* the parent group
* @param group
* group
* @param type
* type
* @return the newly created group element or null if creation failed
*/
protected Element createGroup(String namespace, Object parent, Object group, String type) {
Element parentElement;
if (parent == null) {
parentElement = getRootElement();
} else {
parentElement = getGroup(parent);
}
if (parentElement == null) {
return null;
} else {
Element element;
String id = Dom.createUniqueId();
if (group instanceof PaintableGroup) {
id = Dom.assembleId(id, ((PaintableGroup) group).getGroupName());
}
groupToId.put(group, id);
idToGroup.put(id, group);
if (Dom.NS_HTML.equals(namespace)) {
element = Dom.createElement("div", id);
} else {
element = Dom.createElementNS(namespace, type, id);
}
parentElement.appendChild(element);
return element;
}
}
/**
* Returns the element that is a child of the specified parent and has the specified local group name.
*
* @param parent
* the parent group
* @param name
* the local group name
* @return the element or null if it does not exist or the name was null
*/
protected Element getElement(Object parent, String name) {
if (name == null) {
return null;
}
String id;
if (parent == null) {
id = getRootElement().getId();
} else {
id = groupToId.get(parent);
}
return Dom.getElementById(Dom.assembleId(id, name));
}
/**
* Generic creation method for non-group elements.
*
* @param parent
* the parent group
* @param name
* local group name of the element (should be unique within the group)
* @param type
* the type of the element (tag name, e.g. 'image')
* @param style
* The style to apply on the element.
* @param generateId
* true if a unique id may be generated, otherwise the name will be used as id
* @return the newly created element or null if creation failed or the name was null
*/
protected Element createElement(Object parent, String name, String type, Style style, boolean generateId) {
if (null == name) {
return null;
}
Element parentElement;
if (parent == null) {
parentElement = getRootElement();
} else {
parentElement = getGroup(parent);
}
if (parentElement == null) {
return null;
} else {
Element element;
String id = generateId ? Dom.assembleId(parentElement.getId(), name) : name;
elementToName.put(id, name);
switch (namespace) {
case SVG:
element = Dom.createElementNS(Dom.NS_SVG, type, id);
if (style != null) {
applyStyle(element, style);
}
break;
case VML:
element = Dom.createElementNS(Dom.NS_VML, type, id);
Element stroke = Dom.createElementNS(Dom.NS_VML, "stroke");
element.appendChild(stroke);
Element fill = Dom.createElementNS(Dom.NS_VML, "fill");
element.appendChild(fill);
if ("shape".equals(name)) {
// Set the size .....if the parent has a coordsize defined, take it over:
String coordsize = parentElement.getAttribute("coordsize");
if (coordsize != null && coordsize.length() > 0) {
element.setAttribute("coordsize", coordsize);
}
}
Dom.setStyleAttribute(element, "position", "absolute");
VmlStyleUtil.applyStyle(element, style);
break;
case HTML:
default:
element = Dom.createElementNS(Dom.NS_HTML, type, id);
if (style != null) {
applyStyle(element, style);
}
}
parentElement.appendChild(element);
Dom.setElementAttribute(element, "id", id);
return element;
}
}
/**
* Delete this element from the graphics DOM structure.
*
* @param parent
* parent group object
* @param name
* The element's name.
*/
public void deleteElement(Object parent, String name) {
Element element = getElement(parent, name);
if (element != null) {
Element group = (Element) element.getParentElement();
if (group != null) {
deleteRecursively(group, element);
}
}
}
/**
* Delete this group from the graphics DOM structure.
*
* @param object
* The group's object.
*/
public void deleteGroup(Object object) {
Element element = getGroup(object);
if (element != null) {
Element parent = (Element) element.getParentElement();
if (parent != null) {
deleteRecursively(parent, element);
}
}
}
private void deleteRecursively(Element parent, Element element) {
for (int i = element.getChildCount() - 1; i >= 0; i--) {
Node node = element.getChild(i);
if (node instanceof Element) {
deleteRecursively(element, (Element) node);
}
}
try {
groupToId.remove(element);
if (element.getId() != null) {
Log.logDebug("Removing element " + element.getId());
elementToName.remove(element.getId());
}
if (Dom.getEventListener(element) != null) {
Dom.setEventListener(element, null);
}
Dom.removeChild(parent, element);
} catch (Exception e) {
Log.logError("Problem during recursive delete of " + element.getId() + " from " + parent.getId() + ", " +
e.getMessage());
}
}
/**
* Apply the style.
*
* @param element
* DOM element
* @param style
* style
*/
public void applyStyle(Element element, Style style) {
if (element != null && style != null) {
switch (namespace) {
case VML:
VmlStyleUtil.applyStyle(element, style);
break;
case SVG:
if (style instanceof ShapeStyle) {
applySvgStyle(element, (ShapeStyle) style);
} else if (style instanceof FontStyle) {
applySvgStyle(element, (FontStyle) style);
} else if (style instanceof PictureStyle) {
applySvgStyle(element, (PictureStyle) style);
}
break;
case HTML:
if (style instanceof ShapeStyle) {
applyHtmlStyle(element, (ShapeStyle) style);
} else if (style instanceof FontStyle) {
applyHtmlStyle(element, (FontStyle) style);
} else if (style instanceof PictureStyle) {
applyHtmlStyle(element, (PictureStyle) style);
}
break;
}
}
}
// -------------------------------------------------------------------------
// Private decode methods for each Style class:
// -------------------------------------------------------------------------
private void applyHtmlStyle(Element element, ShapeStyle style) {
if (style.getFillColor() != null && !"".equals(style.getFillColor())) {
Dom.setStyleAttribute(element, "fillColor", style.getFillColor());
}
Dom.setStyleAttribute(element, "fillOpacity", Float.toString(style.getFillOpacity()));
if (style.getStrokeColor() != null && !"".equals(style.getStrokeColor())) {
Dom.setStyleAttribute(element, "stroke", style.getStrokeColor());
}
Dom.setStyleAttribute(element, "strokeOpacity", Float.toString(style.getStrokeOpacity()));
if (style.getStrokeWidth() >= 0) {
Dom.setStyleAttribute(element, "strokeWidth", Float.toString(style.getStrokeWidth()));
}
}
private void applyHtmlStyle(Element element, FontStyle style) {
if (style.getFillColor() != null && !"".equals(style.getFillColor())) {
Dom.setStyleAttribute(element, "color", style.getFillColor());
}
if (style.getFontFamily() != null && !"".equals(style.getFontFamily())) {
Dom.setStyleAttribute(element, "fontFamily", style.getFontFamily());
}
if (style.getFontStyle() != null && !"".equals(style.getFontStyle())) {
Dom.setStyleAttribute(element, "fontStyle", style.getFontStyle());
}
if (style.getFontWeight() != null && !"".equals(style.getFontWeight())) {
Dom.setStyleAttribute(element, "fontWeight", style.getFontWeight());
}
if (style.getFontSize() >= 0) {
Dom.setStyleAttribute(element, "fontSize", style.getFontSize() + "px");
}
}
private void applyHtmlStyle(Element element, PictureStyle style) {
double opacity = style.getOpacity();
if (opacity >= 0.0 && opacity < 1.0) {
Dom.setStyleAttribute(element, "filter", "alpha(opacity=" + Double.toString((opacity * 100)) + ")");
Dom.setStyleAttribute(element, "opacity", Double.toString(opacity));
} else if (opacity == 1.0) {
Dom.setStyleAttribute(element, "filter", "");
Dom.setStyleAttribute(element, "opacity", "");
}
if (style.getDisplay() != null) {
Dom.setStyleAttribute(element, "display", style.getDisplay());
}
}
private void applySvgStyle(Element element, ShapeStyle style) {
StringBuilder css = new StringBuilder();
if (style.getFillColor() != null && !"".equals(style.getFillColor())) {
css.append("fill:");
css.append(style.getFillColor());
css.append(";");
}
css.append("fill-opacity:");
css.append(style.getFillOpacity());
css.append(";");
if (style.getStrokeColor() != null && !"".equals(style.getStrokeColor())) {
css.append("stroke:");
css.append(style.getStrokeColor());
css.append(";");
}
css.append("stroke-opacity:");
css.append(style.getStrokeOpacity());
css.append(";");
if (style.getStrokeWidth() >= 0) {
css.append("stroke-width:");
css.append(style.getStrokeWidth());
css.append(";");
}
Dom.setElementAttribute(element, "style", css.toString());
}
private void applySvgStyle(Element element, FontStyle style) {
StringBuilder css = new StringBuilder();
if (style.getFillColor() != null && !"".equals(style.getFillColor())) {
css.append("fill:");
css.append(style.getFillColor());
css.append(";");
}
if (style.getFontFamily() != null && !"".equals(style.getFontFamily())) {
css.append("font-family:");
css.append(style.getFontFamily());
css.append(";");
}
if (style.getFontStyle() != null && !"".equals(style.getFontStyle())) {
css.append("font-style:");
css.append(style.getFontStyle());
css.append(";");
}
if (style.getFontWeight() != null && !"".equals(style.getFontWeight())) {
css.append("font-weight:");
css.append(style.getFontWeight());
css.append(";");
}
if (style.getFontSize() >= 0) {
css.append("font-size:");
css.append(style.getFontSize());
css.append(";");
}
Dom.setElementAttribute(element, "style", css.toString());
}
private void applySvgStyle(Element element, PictureStyle style) {
StringBuilder css = new StringBuilder();
css.append("opacity:");
css.append(style.getOpacity());
css.append(";");
if (style.getDisplay() != null) {
css.append("display:");
css.append(style.getDisplay());
}
Dom.setElementAttribute(element, "style", css.toString());
if (style.getClassName() != null) {
Dom.setElementAttribute(element, "class", style.getClassName());
}
}
}