X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.scenegraph%2Fsrc%2Forg%2Fsimantics%2Fscenegraph%2Fg2d%2FG2DSceneGraph.java;h=204181c3786fe01631e4b333cce06a7e681bdfd3;hp=c491ff01f2fdaae58c2678814a9ca55823cebee3;hb=bdb7180f6425b460b8566a3223b319caf9dbc56f;hpb=969bd23cab98a79ca9101af33334000879fb60c5 diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/G2DSceneGraph.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/G2DSceneGraph.java index c491ff01f..204181c37 100644 --- a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/G2DSceneGraph.java +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/G2DSceneGraph.java @@ -1,187 +1,186 @@ -/******************************************************************************* - * Copyright (c) 2007, 2010 Association for Decentralized Information Management - * in Industry THTH ry. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * VTT Technical Research Centre of Finland - initial API and implementation - *******************************************************************************/ +/******************************************************************************* + * Copyright (c) 2007, 2010 Association for Decentralized Information Management + * in Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ package org.simantics.scenegraph.g2d; -import java.awt.Component; -import java.awt.Container; -import java.awt.Graphics2D; -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.swing.JComponent; -import javax.swing.RepaintManager; - -import org.simantics.scenegraph.ILookupService; -import org.simantics.scenegraph.INode; -import org.simantics.scenegraph.LookupService; -import org.simantics.scenegraph.ParentNode; -import org.simantics.scenegraph.g2d.events.EventDelegator; -import org.simantics.scenegraph.g2d.events.INodeEventHandlerProvider; -import org.simantics.scenegraph.g2d.events.NodeEventHandler; - -/** - * The root node of a 2D scene graph. - * - * Implements {@link ILookupService} according to the reference implementation - * {@link LookupService}. - * - * @author J-P Laine - */ -@SuppressWarnings("deprecation") -public class G2DSceneGraph extends G2DParentNode implements ILookupService, INodeEventHandlerProvider { - - private static final long serialVersionUID = -7066146333849901429L; - - public static final String IGNORE_FOCUS = "ignoreFocus"; - - protected transient Container rootPane = null; - // TODO: swing dependency in here might not be a good idea - - // This variable is actually used in remote use, when rendering is not performed locally - private transient final Object treeLock = new Object(); - - private HashMap pending = new HashMap(); - private HashMap globalProperties = new HashMap(); - - /** - * For preventing duplicates on the nodesToRemove queue. - */ - protected transient Set nodesToRemoveSet = new HashSet(); - protected Deque nodesToRemove = new ArrayDeque(); - - private transient EventDelegator eventDelegator = new EventDelegator(this); - private transient NodeEventHandler eventHandler = new NodeEventHandler(this); - - /** - * The node that has input focus in the scene graph. The input node will - * receive key and command events. - */ - private transient IG2DNode focusNode; - - /** - * The custom repaint manager of this scene graph. - */ - private transient G2DRepaintManager repaintManager; - - /** - * Returns the event delegator, that is responsible for delegating events to nodes in the sg tree - * - * @return EventDelegator instance, always not null - */ - public EventDelegator getEventDelegator() { - return eventDelegator; - } - - /** - * Returns the node event handler, that is responsible for delegating events - * to nodes in the sg tree. - * - * @return NodeEventHandler instance for this scene graph, always non-null - */ - public NodeEventHandler getEventHandler() { - return eventHandler; - } - - public void setFocusNode(IG2DNode focusNode) { - this.focusNode = focusNode; - } - - public IG2DNode getFocusNode() { - return focusNode; - } +import java.awt.Component; +import java.awt.Container; +import java.awt.Graphics2D; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.swing.JComponent; +import javax.swing.RepaintManager; + +import org.simantics.scenegraph.ILookupService; +import org.simantics.scenegraph.INode; +import org.simantics.scenegraph.LookupService; +import org.simantics.scenegraph.ParentNode; +import org.simantics.scenegraph.g2d.events.EventDelegator; +import org.simantics.scenegraph.g2d.events.INodeEventHandlerProvider; +import org.simantics.scenegraph.g2d.events.NodeEventHandler; + +/** + * The root node of a 2D scene graph. + * + * Implements {@link ILookupService} according to the reference implementation + * {@link LookupService}. + * + * @author J-P Laine + */ +@SuppressWarnings("deprecation") +public class G2DSceneGraph extends G2DParentNode implements ILookupService, INodeEventHandlerProvider { + + private static final long serialVersionUID = -7066146333849901429L; + + public static final String IGNORE_FOCUS = "ignoreFocus"; + + protected transient Container rootPane = null; + // TODO: swing dependency in here might not be a good idea + + // This variable is actually used in remote use, when rendering is not performed locally + private transient final Object treeLock = new Object(); + + private HashMap pending = new HashMap(); + private HashMap globalProperties = new HashMap(); + + /** + * For preventing duplicates on the nodesToRemove queue. + */ + protected transient Set nodesToRemoveSet = new HashSet(); + protected Deque nodesToRemove = new ArrayDeque(); + + private transient EventDelegator eventDelegator = new EventDelegator(this); + private transient NodeEventHandler eventHandler = new NodeEventHandler(this); + + /** + * The node that has input focus in the scene graph. The input node will + * receive key and command events. + */ + private transient IG2DNode focusNode; + + /** + * The custom repaint manager of this scene graph. + */ + private transient G2DRepaintManager repaintManager; + + /** + * Returns the event delegator, that is responsible for delegating events to nodes in the sg tree + * + * @return EventDelegator instance, always not null + */ + public EventDelegator getEventDelegator() { + return eventDelegator; + } + + /** + * Returns the node event handler, that is responsible for delegating events + * to nodes in the sg tree. + * + * @return NodeEventHandler instance for this scene graph, always non-null + */ + public NodeEventHandler getEventHandler() { + return eventHandler; + } + + public void setFocusNode(IG2DNode focusNode) { + this.focusNode = focusNode; + } + + public IG2DNode getFocusNode() { + return focusNode; + } @Override public void render(Graphics2D g2d) { - performCleanup(); - Component rootPane = getRootPane(); - if (rootPane != null) - g2d.setRenderingHint(G2DRenderingHints.KEY_COMPONENT, rootPane); + performCleanup(); + Component rootPane = getRootPane(); + if (rootPane != null) + g2d.setRenderingHint(G2DRenderingHints.KEY_COMPONENT, rootPane); synchronized(treeLock) { super.render(g2d); } - } - - /** - * Util method for executing updates to scenegraph tree - * NOTE: You should really consider performance issues when using this - * - * @param r Runnable to be executed while rendering is not performed - */ - public void syncExec(Runnable r) { - synchronized(treeLock) { - r.run(); - } } - - /** - * Set rootpane for swing components. This is used as parent for the components created by ComponentNode. - * - * @param rootPane Component that is used as a parent for the swing component (This shouldn't be visible) + + /** + * Util method for executing updates to scenegraph tree + * NOTE: You should really consider performance issues when using this + * + * @param r Runnable to be executed while rendering is not performed + */ + public void syncExec(Runnable r) { + synchronized(treeLock) { + r.run(); + } + } + + /** + * Set rootpane for swing components. This is used as parent for the components created by ComponentNode. + * + * @param rootPane Component that is used as a parent for the swing component (This shouldn't be visible) */ public void setRootPane(JComponent rootPane) { - synchronized (RepaintManager.class) { + synchronized (RepaintManager.class) { RepaintManager old = RepaintManager.currentManager(rootPane); - old = findProperRepaintManager(old); - this.repaintManager = new G2DRepaintManager(rootPane.getClass(), old); - RepaintManager.setCurrentManager(repaintManager); - } + old = findProperRepaintManager(old); + this.repaintManager = new G2DRepaintManager(rootPane.getClass(), old); + RepaintManager.setCurrentManager(repaintManager); + } this.rootPane = rootPane; - } - - /** - * Set rootpane for swing components. This is used as parent for the components created by ComponentNode. - * Supports separate component that is responsible for repainting the scenegraph. - * - * @param rootPane Component that is used as a parent for the swing component (This shouldn't be visible) - * @param paintContext Component that is responsible for repainting the scenegraph + } + + /** + * Set rootpane for swing components. This is used as parent for the components created by ComponentNode. + * Supports separate component that is responsible for repainting the scenegraph. + * + * @param rootPane Component that is used as a parent for the swing component (This shouldn't be visible) + * @param paintContext Component that is responsible for repainting the scenegraph */ - public void setRootPane(Container rootPane, Component paintContext) { - synchronized (RepaintManager.class) { - RepaintManager old = RepaintManager.currentManager(paintContext); - old = findProperRepaintManager(old); - this.repaintManager = new G2DRepaintManager(paintContext.getClass(), old); - RepaintManager.setCurrentManager(repaintManager); - } - this.rootPane = rootPane; - eventHandler.setRootPane(rootPane); - } - - private RepaintManager findProperRepaintManager(RepaintManager old) { - while (old instanceof G2DRepaintManager) { - G2DRepaintManager g2drm = (G2DRepaintManager) old; - old = g2drm.getDelegate(); - } - return old; - } - - public G2DRepaintManager getRepaintManager() { - return repaintManager; - } - + public void setRootPane(Container rootPane, Component paintContext) { + synchronized (RepaintManager.class) { + RepaintManager old = RepaintManager.currentManager(paintContext); + old = findProperRepaintManager(old); + this.repaintManager = new G2DRepaintManager(paintContext.getClass(), old); + RepaintManager.setCurrentManager(repaintManager); + } + this.rootPane = rootPane; + } + + private RepaintManager findProperRepaintManager(RepaintManager old) { + while (old instanceof G2DRepaintManager) { + G2DRepaintManager g2drm = (G2DRepaintManager) old; + old = g2drm.getDelegate(); + } + return old; + } + + public G2DRepaintManager getRepaintManager() { + return repaintManager; + } + /** * Put the node to the remove queue */ @Override public void asyncRemoveNode(INode node) { - synchronized(nodesToRemove) { - // Prevent nodes from winding up twice on the nodesToRemove queue + synchronized(nodesToRemove) { + // Prevent nodes from winding up twice on the nodesToRemove queue if (nodesToRemoveSet.add(node)) { - nodesToRemove.add(node); // This is performed when called inside the render + nodesToRemove.add(node); // This is performed when called inside the render } } } @@ -193,183 +192,183 @@ public class G2DSceneGraph extends G2DParentNode implements ILookupService, INod synchronized(nodesToRemove) { while(nodesToRemove.size() > 0) { INode node = nodesToRemove.removeFirst(); - ParentNode parent = node.getParent(); - // This works around issue #2071 + ParentNode parent = node.getParent(); + // This works around issue #2071 if (parent != null) parent.removeNode(node); - } - if (!nodesToRemoveSet.isEmpty()) - nodesToRemoveSet.clear(); + } + if (!nodesToRemoveSet.isEmpty()) + nodesToRemoveSet.clear(); } } - - @Override - public void cleanup() { - super.cleanup(); - nodesToRemove.clear(); - nodesToRemove = null; - nodesToRemoveSet.clear(); - nodesToRemoveSet = null; - eventHandler.dispose(); - eventHandler = null; - eventDelegator.dispose(); - eventDelegator = null; - } + + @Override + public void cleanup() { + super.cleanup(); + nodesToRemove.clear(); + nodesToRemove = null; + nodesToRemoveSet.clear(); + nodesToRemoveSet = null; + eventHandler.dispose(); + eventHandler = null; + eventDelegator.dispose(); + eventDelegator = null; + } public Container getRootPane() { return (Container) this.rootPane; - } - + } + @Override public String toString() { return super.toString() + " [root pane=" + rootPane + "]"; } - @Override - public ParentNode getRootNode() { - // This is a root node! - return this; - } - - // ILookupService implementation - - private final Object lookupLock = new Object(); - private final Map toNode = new HashMap(); - private final Map toId = new HashMap(); - - transient Logger logger = Logger.getLogger(getClass().getName()); - - @Override - public INode map(String id, INode node) { - if (id == null) - throw new NullPointerException("null id"); - if (node == null) - throw new NullPointerException("null node"); - - INode oldNode; - String oldId; - synchronized (lookupLock) { - oldNode = toNode.put(id, node); - oldId = toId.put(node, id); - - // Keep the mapping a consistent bijection: - // If ID => INode mapping is removed, the INode => ID mappings must - // removed also. - - if (oldNode != null && !oldNode.equals(node)) { - String removedId = toId.remove(oldNode); - if (!id.equals(removedId)) - toNode.remove(removedId); - } - if (oldId != null && !oldId.equals(id)) { - INode removedNode = toNode.remove(oldId); - if (removedNode != node) - toId.remove(removedNode); - } - } - if (logger.isLoggable(Level.FINER)) - logger.fine("map(" + id + ", " + node + ")"); - if (oldNode != null || oldId != null) { - if (logger.isLoggable(Level.FINE)) { - logger.info("replaced mappings for ID " + oldId + " and node " + oldNode); - } - } - return oldNode; - } - - @Override - public INode unmap(String id) { - INode node; - String mappedId; - synchronized (lookupLock) { - node = toNode.remove(id); - if (node == null) - return null; - mappedId = toId.remove(node); - } - if (logger.isLoggable(Level.FINER)) - logger.fine("unmap(" + id + "): " + node); - if (mappedId != null && !mappedId.equals(id)) { - if (logger.isLoggable(Level.WARNING)) - logger.log(Level.WARNING, "mapping was out-of-sync: " + id + " => " + node + " & " + mappedId + " => " + node, new Exception("trace")); - } - return node; - } - - @Override - public String unmap(INode node) { - String id; - INode mappedNode; - synchronized (lookupLock) { - id = toId.remove(node); - if (node == null) - return null; - mappedNode = toNode.remove(id); - } - if (logger.isLoggable(Level.FINER)) - logger.fine("unmap(" + node + "): " + id); - if (mappedNode != null && node != mappedNode) { - if (logger.isLoggable(Level.WARNING)) - logger.log(Level.WARNING, "mapping was out-of-sync: " + node + " => " + id + " & " + id + " => " + mappedNode, new Exception("trace")); - } - return id; - } - - @Override - public INode lookupNode(String id) { - synchronized (lookupLock) { - return toNode.get(id); - } - } - - @Override - public String lookupId(INode node) { - synchronized (lookupLock) { - return toId.get(node); - } - } - - public boolean isPending() { - return !pending.isEmpty(); - } - - synchronized public void increasePending(Object object) { - Integer ref = pending.get(object); - if (ref == null) pending.put(object, 1); - else pending.put(object, ref+1); - } - - synchronized public void setPending(Object object) { - pending.put(object, 1); - } - - synchronized public void clearPending(Object object) { - pending.remove(object); - } - - synchronized public void decreasePending(Object object) { - Integer ref = pending.get(object); - if (ref == null) { - return; - //throw new IllegalStateException("Ref count in unregister was 0 for " + object); - } - if (ref > 1) pending.put(object, ref-1); - else if (ref==1) pending.remove(object); - else { - return; - //throw new IllegalStateException("Ref count in unregister was 0 for " + object); - } - } - - synchronized public void setGlobalProperty(String key, Object value) { - globalProperties.put(key, value); - } - - @SuppressWarnings("unchecked") - synchronized public T getGlobalProperty(String key, T defaultValue) { - T t = (T)globalProperties.get(key); - if(t == null) return defaultValue; - return t; - } - + @Override + public ParentNode getRootNode() { + // This is a root node! + return this; + } + + // ILookupService implementation + + private final Object lookupLock = new Object(); + private final Map toNode = new HashMap(); + private final Map toId = new HashMap(); + + transient Logger logger = Logger.getLogger(getClass().getName()); + + @Override + public INode map(String id, INode node) { + if (id == null) + throw new NullPointerException("null id"); + if (node == null) + throw new NullPointerException("null node"); + + INode oldNode; + String oldId; + synchronized (lookupLock) { + oldNode = toNode.put(id, node); + oldId = toId.put(node, id); + + // Keep the mapping a consistent bijection: + // If ID => INode mapping is removed, the INode => ID mappings must + // removed also. + + if (oldNode != null && !oldNode.equals(node)) { + String removedId = toId.remove(oldNode); + if (!id.equals(removedId)) + toNode.remove(removedId); + } + if (oldId != null && !oldId.equals(id)) { + INode removedNode = toNode.remove(oldId); + if (removedNode != node) + toId.remove(removedNode); + } + } + if (logger.isLoggable(Level.FINER)) + logger.fine("map(" + id + ", " + node + ")"); + if (oldNode != null || oldId != null) { + if (logger.isLoggable(Level.FINE)) { + logger.info("replaced mappings for ID " + oldId + " and node " + oldNode); + } + } + return oldNode; + } + + @Override + public INode unmap(String id) { + INode node; + String mappedId; + synchronized (lookupLock) { + node = toNode.remove(id); + if (node == null) + return null; + mappedId = toId.remove(node); + } + if (logger.isLoggable(Level.FINER)) + logger.fine("unmap(" + id + "): " + node); + if (mappedId != null && !mappedId.equals(id)) { + if (logger.isLoggable(Level.WARNING)) + logger.log(Level.WARNING, "mapping was out-of-sync: " + id + " => " + node + " & " + mappedId + " => " + node, new Exception("trace")); + } + return node; + } + + @Override + public String unmap(INode node) { + String id; + INode mappedNode; + synchronized (lookupLock) { + id = toId.remove(node); + if (node == null) + return null; + mappedNode = toNode.remove(id); + } + if (logger.isLoggable(Level.FINER)) + logger.fine("unmap(" + node + "): " + id); + if (mappedNode != null && node != mappedNode) { + if (logger.isLoggable(Level.WARNING)) + logger.log(Level.WARNING, "mapping was out-of-sync: " + node + " => " + id + " & " + id + " => " + mappedNode, new Exception("trace")); + } + return id; + } + + @Override + public INode lookupNode(String id) { + synchronized (lookupLock) { + return toNode.get(id); + } + } + + @Override + public String lookupId(INode node) { + synchronized (lookupLock) { + return toId.get(node); + } + } + + public boolean isPending() { + return !pending.isEmpty(); + } + + synchronized public void increasePending(Object object) { + Integer ref = pending.get(object); + if (ref == null) pending.put(object, 1); + else pending.put(object, ref+1); + } + + synchronized public void setPending(Object object) { + pending.put(object, 1); + } + + synchronized public void clearPending(Object object) { + pending.remove(object); + } + + synchronized public void decreasePending(Object object) { + Integer ref = pending.get(object); + if (ref == null) { + return; + //throw new IllegalStateException("Ref count in unregister was 0 for " + object); + } + if (ref > 1) pending.put(object, ref-1); + else if (ref==1) pending.remove(object); + else { + return; + //throw new IllegalStateException("Ref count in unregister was 0 for " + object); + } + } + + synchronized public void setGlobalProperty(String key, Object value) { + globalProperties.put(key, value); + } + + @SuppressWarnings("unchecked") + synchronized public T getGlobalProperty(String key, T defaultValue) { + T t = (T)globalProperties.get(key); + if(t == null) return defaultValue; + return t; + } + }