X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.g2d%2Fsrc%2Forg%2Fsimantics%2Fg2d%2Fcanvas%2Fimpl%2FAbstractCanvasParticipant.java;h=cc298f3aa20e1f7ad05d6d47ab213a63a0e9183d;hp=a7950d26d6cf268939661b4120abf2b37917ff0a;hb=refs%2Fchanges%2F38%2F238%2F2;hpb=24e2b34260f219f0d1644ca7a138894980e25b14 diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/canvas/impl/AbstractCanvasParticipant.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/canvas/impl/AbstractCanvasParticipant.java index a7950d26d..cc298f3aa 100644 --- a/bundles/org.simantics.g2d/src/org/simantics/g2d/canvas/impl/AbstractCanvasParticipant.java +++ b/bundles/org.simantics.g2d/src/org/simantics/g2d/canvas/impl/AbstractCanvasParticipant.java @@ -1,563 +1,563 @@ -/******************************************************************************* - * 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 java.util.HashSet; -import java.util.Set; - -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: - *

- *
- *   @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) {
- *       ...
- *   }
- * 
- * - * @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 ctxListener = - new IContextListener() { - @Override - public void itemAdded(IContext sender, ICanvasParticipant item) { - _itemAdded(sender, item); - if (!depsSatisfied) depsSatisfied = checkDependencies(); - } - @Override - public void itemRemoved(IContext sender, ICanvasParticipant item) { - _itemRemoved(sender, item); - AbstractCanvasParticipant.this.depsSatisfied = depsSatisfied; - } - }; - - Set missingDependencies = new HashSet<>(); - - @SuppressWarnings("unchecked") - private void _itemAdded(IContext sender, ICanvasParticipant item) - { - // This code handles @Dependency annotation - // Synchronizing because the calling thread may be anything - synchronized ( ctxListener ) { - Class c = (Class) item.getClass(); - try { - for (ReferenceDefinition def : refDefs) - { - Class defClass = def.requirement; - if (!defClass.isAssignableFrom(c)) continue; - Field f = def.field; - Object value = f.get(AbstractCanvasParticipant.this); - assert(value==null); - f.set(AbstractCanvasParticipant.this, item); - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } - } - @SuppressWarnings("unchecked") - private void _itemRemoved(IContext sender, ICanvasParticipant item) - { - synchronized ( ctxListener ) { - Class c = (Class) item.getClass(); - //boolean depsSatisfied = true; - try { - for (ReferenceDefinition def : refDefs) - { - Class defClass = def.requirement; - Field f = def.field; - //Object value = f.get(AbstractCanvasParticipant.this); - if (defClass.isAssignableFrom(c)) { - //value = null; - f.set(AbstractCanvasParticipant.this, null); - } - //depsSatisfied &= !def.dependency || value!=null; - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } - } - - /** - * AbstractInteractor adds itself to the context set in the constructor - * argument. - * - * The constructor creates hint constructor and adds it into the context - * with priority value Integer.MIN_VALUE. Generally, base service interactors - * only read hints. Those hints are usually overrideable and thus the default - * hint context priority is as small value as possible. Use this constructor for - * BASE SERVICE interactors that only read values. - * - * @param ctx - */ - public AbstractCanvasParticipant() - { - sghandlers = SGNodeReflection.getSGHandlers(this); - eventHandlers = EventHandlerReflection.getEventHandlers(this); - refDefs = DependencyReflection.getDependencies(this, ReferenceType.CanvasParticipant); - hintListeners = HintReflection.getDependencies(this); - if (refDefs.length==0) depsSatisfied = true; - } - - @Override - public void addedToContext(ICanvasContext ctx) - { - assert(ctx!=null); - assert(context==null); - this.context = ctx; - this.hintStack = ctx.getHintStack(); - this.thread = ctx.getThreadAccess(); - - // Add event handlers - IPriorityStack eventHandlerStack = getContext().getEventHandlerStack(); - for (EventHandlerDefinition eventHandler : eventHandlers) - eventHandlerStack.add(eventHandler.eventHandler, eventHandler.priority); - - // Fill References & Monitor for changes - if (refDefs.length!=0) { - getContext().addContextListener(ctxListener); - for (ICanvasParticipant cp : getContext().toArray()) - _itemAdded(getContext(), cp); - depsSatisfied = checkDependencies(); - } - - // Add hint reflections - IHintStack stack = getContext().getHintStack(); - IThreadWorkQueue thread = getContext().getThreadAccess(); - for (HintListenerDefinition def : hintListeners) - stack.addKeyHintListener(thread, def.key, def); - - - // Create SceneGraph nodes - for (CanvasSGNodeDefinition sg : sghandlers) { - if (sg.initDesignation != null) { - switch (sg.initDesignation) { - case CONTROL: - sg.init(ctx.getSceneGraph()); - break; - case CANVAS: - sg.init(ctx.getCanvasNode()); - break; - } - } - } - } - - @Override - public void removedFromContext(ICanvasContext ctx) - { - assert(ctx!=null); - if (context==null) - throw new RuntimeException("Interactor was not in any context"); - - // Remove context listener - if (refDefs.length!=0) { - getContext().removeContextListener(ctxListener); - } - - // Clean up SceneGraph nodes - for (CanvasSGNodeDefinition sg : sghandlers) { - sg.cleanup(); - } - - IPriorityStack eventHandlerStack = context.getEventHandlerStack(); - for (EventHandlerDefinition eventHandler : eventHandlers) - eventHandlerStack.remove(eventHandler.eventHandler); - - IHintStack stack = getContext().getHintStack(); - IThreadWorkQueue thread = getContext().getThreadAccess(); - for (HintListenerDefinition def : hintListeners) - stack.removeKeyHintListener(thread, def.key, def); - - if (localHintCtx!=null) { - context.getHintStack().removeHintContext(localHintCtx); - } - context = null; - } - - /** - * Has this interactor been removed from the context - * @return true if interactor has been removed from the context - */ - public boolean isRemoved() - { - return context==null; - } - - /** - * Remove Self. - * - * Removes this interactor from its designed context. - * This methods can be invoked only once. - */ - public void remove() - { - if (isRemoved()) - throw new RuntimeException("Interactor has already been removed from the context"); - context.remove(this); - } - - /** - * Returns the hint stack of the context. - * @return hint stack - */ - public IHintStack getHintStack() - { - return hintStack; - } - - /** - * Get local hint context. This context contains all hints set by this - * particular interactor. Modifications affect the hint stack of the - * interaction context, although some modifications may not become effective - * in case the key is overridden by another hint context in the stack. - *

- * 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 E getHint(Key key) - { - return hintStack.getHint(key); - } - - public boolean hasHint(Key key) - { - return hintStack.getHint(key)!=null; - } - - /** - * Set hint to the local hint stack. - * - * Thread safe - switches to context thread - * - * @param key the key - * @param value the value - */ - public void setHint(final Key key, final Object value) - { - if (getThread().currentThreadAccess()) - getWriteableHintContext().setHint(key, value); - else { -// syncExec(new Runnable() { -// @Override -// public void run() { -// getWriteableHintContext().setHint(key, value); -// }}); - throw new IllegalStateException("illegal thread access, expected " + getThread().getThread() + ", was in " - + Thread.currentThread()); - } - } - - /** - * Remove hint from local hint context / stack's default context - * - * Thread safe - switches to context thread - * - * @param key the key - */ - public void removeHint(final Key key) - { - if (getThread().currentThreadAccess()) - getWriteableHintContext().removeHint(key); - else -// syncExec(new Runnable() { -// @Override -// public void run() { -// getWriteableHintContext().removeHint(key); -// }}); - throw new IllegalStateException("illegal thread access"); - } - - - public void syncExec(Runnable r) - { - ThreadUtils.syncExec(getThread(), r); - } - - public void asyncExec(Runnable r) - { - ThreadUtils.asyncExec(getThread(), r); - } - - /** - * Create local hint context and add it to the hint stack. It will be - * automatically removed when this participant is removed from the canvas. - *

- * 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) { - missingDependencies.add(f); - return false; - } else { - missingDependencies.remove(f); - } - } - } 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(); - } - -} +/******************************************************************************* + * 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 java.util.HashSet; +import java.util.Set; + +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: + *

+ *
+ *   @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) {
+ *       ...
+ *   }
+ * 
+ * + * @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 ctxListener = + new IContextListener() { + @Override + public void itemAdded(IContext sender, ICanvasParticipant item) { + _itemAdded(sender, item); + if (!depsSatisfied) depsSatisfied = checkDependencies(); + } + @Override + public void itemRemoved(IContext sender, ICanvasParticipant item) { + _itemRemoved(sender, item); + AbstractCanvasParticipant.this.depsSatisfied = depsSatisfied; + } + }; + + Set missingDependencies = new HashSet<>(); + + @SuppressWarnings("unchecked") + private void _itemAdded(IContext sender, ICanvasParticipant item) + { + // This code handles @Dependency annotation + // Synchronizing because the calling thread may be anything + synchronized ( ctxListener ) { + Class c = (Class) item.getClass(); + try { + for (ReferenceDefinition def : refDefs) + { + Class defClass = def.requirement; + if (!defClass.isAssignableFrom(c)) continue; + Field f = def.field; + Object value = f.get(AbstractCanvasParticipant.this); + assert(value==null); + f.set(AbstractCanvasParticipant.this, item); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + @SuppressWarnings("unchecked") + private void _itemRemoved(IContext sender, ICanvasParticipant item) + { + synchronized ( ctxListener ) { + Class c = (Class) item.getClass(); + //boolean depsSatisfied = true; + try { + for (ReferenceDefinition def : refDefs) + { + Class defClass = def.requirement; + Field f = def.field; + //Object value = f.get(AbstractCanvasParticipant.this); + if (defClass.isAssignableFrom(c)) { + //value = null; + f.set(AbstractCanvasParticipant.this, null); + } + //depsSatisfied &= !def.dependency || value!=null; + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + /** + * AbstractInteractor adds itself to the context set in the constructor + * argument. + * + * The constructor creates hint constructor and adds it into the context + * with priority value Integer.MIN_VALUE. Generally, base service interactors + * only read hints. Those hints are usually overrideable and thus the default + * hint context priority is as small value as possible. Use this constructor for + * BASE SERVICE interactors that only read values. + * + * @param ctx + */ + public AbstractCanvasParticipant() + { + sghandlers = SGNodeReflection.getSGHandlers(this); + eventHandlers = EventHandlerReflection.getEventHandlers(this); + refDefs = DependencyReflection.getDependencies(this, ReferenceType.CanvasParticipant); + hintListeners = HintReflection.getDependencies(this); + if (refDefs.length==0) depsSatisfied = true; + } + + @Override + public void addedToContext(ICanvasContext ctx) + { + assert(ctx!=null); + assert(context==null); + this.context = ctx; + this.hintStack = ctx.getHintStack(); + this.thread = ctx.getThreadAccess(); + + // Add event handlers + IPriorityStack eventHandlerStack = getContext().getEventHandlerStack(); + for (EventHandlerDefinition eventHandler : eventHandlers) + eventHandlerStack.add(eventHandler.eventHandler, eventHandler.priority); + + // Fill References & Monitor for changes + if (refDefs.length!=0) { + getContext().addContextListener(ctxListener); + for (ICanvasParticipant cp : getContext().toArray()) + _itemAdded(getContext(), cp); + depsSatisfied = checkDependencies(); + } + + // Add hint reflections + IHintStack stack = getContext().getHintStack(); + IThreadWorkQueue thread = getContext().getThreadAccess(); + for (HintListenerDefinition def : hintListeners) + stack.addKeyHintListener(thread, def.key, def); + + + // Create SceneGraph nodes + for (CanvasSGNodeDefinition sg : sghandlers) { + if (sg.initDesignation != null) { + switch (sg.initDesignation) { + case CONTROL: + sg.init(ctx.getSceneGraph()); + break; + case CANVAS: + sg.init(ctx.getCanvasNode()); + break; + } + } + } + } + + @Override + public void removedFromContext(ICanvasContext ctx) + { + assert(ctx!=null); + if (context==null) + throw new RuntimeException("Interactor was not in any context"); + + // Remove context listener + if (refDefs.length!=0) { + getContext().removeContextListener(ctxListener); + } + + // Clean up SceneGraph nodes + for (CanvasSGNodeDefinition sg : sghandlers) { + sg.cleanup(); + } + + IPriorityStack eventHandlerStack = context.getEventHandlerStack(); + for (EventHandlerDefinition eventHandler : eventHandlers) + eventHandlerStack.remove(eventHandler.eventHandler); + + IHintStack stack = getContext().getHintStack(); + IThreadWorkQueue thread = getContext().getThreadAccess(); + for (HintListenerDefinition def : hintListeners) + stack.removeKeyHintListener(thread, def.key, def); + + if (localHintCtx!=null) { + context.getHintStack().removeHintContext(localHintCtx); + } + context = null; + } + + /** + * Has this interactor been removed from the context + * @return true if interactor has been removed from the context + */ + public boolean isRemoved() + { + return context==null; + } + + /** + * Remove Self. + * + * Removes this interactor from its designed context. + * This methods can be invoked only once. + */ + public void remove() + { + if (isRemoved()) + throw new RuntimeException("Interactor has already been removed from the context"); + context.remove(this); + } + + /** + * Returns the hint stack of the context. + * @return hint stack + */ + public IHintStack getHintStack() + { + return hintStack; + } + + /** + * Get local hint context. This context contains all hints set by this + * particular interactor. Modifications affect the hint stack of the + * interaction context, although some modifications may not become effective + * in case the key is overridden by another hint context in the stack. + *

+ * 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 E getHint(Key key) + { + return hintStack.getHint(key); + } + + public boolean hasHint(Key key) + { + return hintStack.getHint(key)!=null; + } + + /** + * Set hint to the local hint stack. + * + * Thread safe - switches to context thread + * + * @param key the key + * @param value the value + */ + public void setHint(final Key key, final Object value) + { + if (getThread().currentThreadAccess()) + getWriteableHintContext().setHint(key, value); + else { +// syncExec(new Runnable() { +// @Override +// public void run() { +// getWriteableHintContext().setHint(key, value); +// }}); + throw new IllegalStateException("illegal thread access, expected " + getThread().getThread() + ", was in " + + Thread.currentThread()); + } + } + + /** + * Remove hint from local hint context / stack's default context + * + * Thread safe - switches to context thread + * + * @param key the key + */ + public void removeHint(final Key key) + { + if (getThread().currentThreadAccess()) + getWriteableHintContext().removeHint(key); + else +// syncExec(new Runnable() { +// @Override +// public void run() { +// getWriteableHintContext().removeHint(key); +// }}); + throw new IllegalStateException("illegal thread access"); + } + + + public void syncExec(Runnable r) + { + ThreadUtils.syncExec(getThread(), r); + } + + public void asyncExec(Runnable r) + { + ThreadUtils.asyncExec(getThread(), r); + } + + /** + * Create local hint context and add it to the hint stack. It will be + * automatically removed when this participant is removed from the canvas. + *

+ * 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) { + missingDependencies.add(f); + return false; + } else { + missingDependencies.remove(f); + } + } + } 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(); + } + +}