/******************************************************************************* * 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.g2d.canvas.impl; import java.lang.reflect.Field; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.canvas.ICanvasParticipant; import org.simantics.g2d.canvas.IContentContext; import org.simantics.g2d.canvas.impl.DependencyReflection.ReferenceDefinition; import org.simantics.g2d.canvas.impl.DependencyReflection.ReferenceType; import org.simantics.g2d.canvas.impl.HintReflection.HintListenerDefinition; import org.simantics.g2d.canvas.impl.SGNodeReflection.CanvasSGNodeDefinition; import org.simantics.scenegraph.g2d.events.EventHandlerReflection; import org.simantics.scenegraph.g2d.events.IEventHandler; import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandlerDefinition; import org.simantics.utils.datastructures.context.IContext; import org.simantics.utils.datastructures.context.IContextListener; import org.simantics.utils.datastructures.hints.HintContext; import org.simantics.utils.datastructures.hints.IHintContext; import org.simantics.utils.datastructures.hints.IHintStack; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.datastructures.prioritystack.IPriorityStack; import org.simantics.utils.threads.IThreadWorkQueue; import org.simantics.utils.threads.ThreadUtils; /** * AbstractCanvasParticipant is base implementation for canvas participants. * There is an assertion that states AbstractCanvasParticipant can be added * only once to a canvas. * *
* There is convenience mechanism for adding painter and event handler * listeners. Subclasses create listeners with usage of Painter and * EventHandler annotations. Listeners are automatically added and * removed to/from canvas context. *
* ** Example: *
** ** @EventHandler(priority=200) * public boolean handleEvent(Event e) { * return false; * } * * @EventHandler(priority=400) * public boolean handleMousePressEvent(MouseButtonPressedEvent e) { * return false; * } * * IG2DNode node; * * @SGInit * public void initSG(G2DParentNode parent) { * // Insert a node into the scene graph rendered by the canvas * // this participant is attached to and initialize it to render * // something. Later on, to modify what the node will render, just * // update the node's attributes. * node = parent.addNode("myNode", MyNode.class); * node.setZIndex(Integer.MIN_VALUE); * } * * @SGCleanup * public void cleanup() { * // Remove our node from the scene graph where it belonged. * // The node must not be used after this anymore. * if (node != null) { * node.remove(); * node = null; * } * } *
* Local fields are automatically assigned with ICanvasParticipant instances if they
* are annotated with either @Dependency
or @Reference
annotation tag.
* @Depedency
is a requirement, @Reference
is optional.
*
* assertDependencies() verifies that dependencies are satisfied.
* Local depsSatisfied field is true when dependencies are satisfied.
*
*
* Example: *
** ** class MyParticipant implements ICanvasParticipant { * @Reference MouseMonitor mouseMonitor; * @Dependency TimeParticipant timeParticipant; * * @Painter(priority=100) * public void paint(GraphicsContext gc) { * assertDependencies(); // timeParticipant != null * * timeParticipant.doSomething(); * * if (mouseMonitor!=null) doSomethingElse(); * } * }
* Hint listener annotation. *
* ** Example: *
** * @author Toni Kalajainen */ public abstract class AbstractCanvasParticipant implements ICanvasParticipant { /** The interactor/canvas context */ private ICanvasContext context; /** The thread used in the context */ private IThreadWorkQueue thread; /** the local hint context */ protected IHintContext localHintCtx = null; protected int localPriority = 0; /** wrapped local hint context. reads from hint stack */ protected IHintContext hintCtx = null; /** Cached hint stack value */ protected IHintStack hintStack; /** Painters found with reflection */ protected CanvasSGNodeDefinition[] sghandlers; /** Painters found with reflection */ protected EventHandlerDefinition[] eventHandlers; protected HintListenerDefinition[] hintListeners; /** Reference definitions */ protected ReferenceDefinition[] refDefs; protected boolean depsSatisfied = false; private final IContextListener* @HintListener(Class=Hints.class, Field="KEY_CANVAS_TRANSFORM") * public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) { * ... * } * @HintListener(Class=Hints.class, Field="KEY_CANVAS_TRANSFORM") * public void hintRemoved(IHintObservable sender, Key key, Object oldValue) { * ... * } *
* Local context must be created with createLocalHintContext() method. * constructor has defualt value Integer.MIN_VALUE. *
* Reading from this context returns only the local hint and not * from the shared hint stack of the canvas context. *
* To read from the shared hint stack, use getHint() or getHintStack() instead.
*
* @return the local hint context
*/
public synchronized IHintContext getLocalHintContext()
{
return localHintCtx;
}
/**
* Get hint context. Read operations are to hint stack, writes to local stack.
*
* @return
*/
public synchronized IHintContext getWriteableHintStack()
{
if (hintCtx==null)
hintCtx = getHintStack().createStackRead(getLocalHintContext());
return hintCtx;
}
/**
* Get hint context; local if exists, otherwise stack's default context.
*
* @return
*/
public IHintContext getWriteableHintContext()
{
ICanvasContext ctx = context;
assert(ctx!=null);
if (localHintCtx!=null) return localHintCtx;
return ctx.getDefaultHintContext();
}
/**
* Read hint from the hint stack.
*
* @param key
* @return
*/
public
* Local hint context overrides default context in the following convenience methods:
* getHint(), setHint(), setHintAsync()
*
* Q: What is the purpose of local hint stack?
* A: To override hints for the lifetime of the participant.
* For example, Special editing mode overrides viewport and toolmode.
*
* @param priority
*/
public void createLocalHintContext(int priority)
{
localHintCtx = new HintContext();
}
/**
* Set hint to the local hint stack if one exists otherwise to the default context.
* Switches thread to the appropriate context thread.
*
* @param key the key
* @param value the value
*/
public void setHintAsync(final Key key, final Object value)
{
assert(context!=null);
asyncExec(new Runnable() {
@Override
public void run() {
if (isRemoved())
return;
if (localHintCtx!=null)
localHintCtx.setHint(key, value);
else
context.getDefaultHintContext().setHint(key, value);
}});
}
/**
* Get the interactor context.
* @return the context
*/
public ICanvasContext getContext()
{
return context;
}
public IThreadWorkQueue getThread()
{
return thread;
}
/**
* Asserts that dependencies of participants are satisfied.
*/
public void assertDependencies()
{
assert(depsSatisfied);
}
private boolean checkDependencies() {
synchronized ( ctxListener ) {
try {
for (ReferenceDefinition rd : refDefs) {
if (!rd.dependency)
continue;
Field f = rd.field;
Object o = f.get(this);
if (o == null)
return false;
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return true;
}
}
public void setDirty()
{
ICanvasContext ctx = getContext();
if ( ctx==null ) return;
IContentContext cctx = ctx.getContentContext();
if ( cctx==null ) return;
cctx.setDirty();
}
}