/* * NodeEvent.java * JCollider * * Copyright (c) 2004-2010 Hanns Holger Rutz. All rights reserved. * * This software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either * version 2, june 1991 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License (gpl.txt) along with this software; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * For further information, please contact Hanns Holger Rutz at * contact@sciss.de , or visit http://www.sciss.de/jcollider * * * JCollider is closely modelled after SuperCollider Language, * often exhibiting a direct translation from Smalltalk to Java. * SCLang is a software originally developed by James McCartney, * which has become an Open Source project. * See http://www.audiosynth.com/ for details. * * * Changelog: * 02-Oct-05 created * 17-Sep-06 fixed bug in incorporate (now always returns false for simplicity) */ package de.sciss.jcollider; import java.util.Collections; import java.util.ArrayList; import java.util.List; import de.sciss.app.BasicEvent; import de.sciss.net.OSCMessage; /** * These kind of events get delivered by a * node watcher to inform listeners about * node status changes. * * @author Hanns Holger Rutz * @version 0.33, 19-Mar-08 */ public class NodeEvent extends BasicEvent { // --- ID values --- /** * returned by getID() : the node was created */ public static final int GO = 0; /** * returned by getID() : the node was destroyed */ public static final int END = 1; /** * returned by getID() : the node was resumed */ public static final int ON = 2; /** * returned by getID() : the node was paused */ public static final int OFF = 3; /** * returned by getID() : the node has moved */ public static final int MOVE = 4; /** * returned by getID() : the event was created by a /n_query command */ public static final int INFO = 5; /** * returned by getType() : the node is a synth */ public static final int SYNTH = 0; /** * returned by getType() : the node is a group */ public static final int GROUP = 1; /** * returned by getType() : the node type is unknown */ public static final int UNKNOWN = -1; private final Node node; private final int nodeID; private final int nodeType; private final int parentID; private final int predID; private final int succID; private final int headID; private final int tailID; private final int oldParentID; private final int oldPredID; private final int oldSuccID; // private final int oldHeadID; // private final int oldTailID; private static final List collValidCmds; static { final List coll = new ArrayList( 6 ); coll.add( "/n_go" ); coll.add( "/n_end" ); coll.add( "/n_on" ); coll.add( "/n_off" ); coll.add( "/n_move" ); coll.add( "/n_info" ); collValidCmds = Collections.unmodifiableList( coll ); } // /** // * Constructs a <code>NodeEvent</code> from a valid node. // * All list fields of the node must be valid, i.e. parent group, // * predecessor, successor etc. // * // * @param source who fired the event // * @param ID the type of status change, e.g. <code>GO</code> // * @param when timestamp of the event (e.g. <code>System.currentTimeMillis()</code>) // * @param node the representation of the node whose status changed // */ // protected NodeEvent( Object source, int ID, long when, Node node ) // { // super( source, ID, when ); // // this.node = node; // nodeID = node.getNodeID(); // parentID = node.getGroup().getNodeID(); // predID = node.getPredNode() == null ? -1 : node.getPredNode().getNodeID(); // succID = node.getSuccNode() == null ? -1 : node.getSuccNode().getNodeID(); // if( node instanceof Synth ) { // nodeType = SYNTH; // } else if( node instanceof Group ) { // nodeType = GROUP; // } else { // nodeType = UNKNOWN; // } // if( nodeType == GROUP ) { // final Group g = (Group) node; // headID = g.getHeadNode() == null ? -1 : g.getHeadNode().getNodeID(); // tailID = g.getTailNode() == null ? -1 : g.getTailNode().getNodeID(); // } else { // headID = -1; // tailID = -1; // } // } /** * Constructs a <code>NodeEvent</code> from a detailed description. * Note that the provided node (which may be <code>null</code>) is considered * not to be up-to-date, it is examined to fill in the old-fields (oldParentID, * oldPredID) etc. It will be returned by * <code>getNode</code>. The caller is responsible for updating the node's * fields accordingly, after processing the event. * * @param source who fired the event * @param ID the type of status change, e.g. <code>GO</code> * @param when timestamp of the event (e.g. <code>System.currentTimeMillis()</code>) * @param node the representation of the node whose status changed * @param nodeID the ID of the node whose status changed * @param parentID the ID of the node's parent group * @param predID the node's predecessor or -1 * @param succID the node's successor or -1 * @param nodeType either of <code>GROUP</code> or <code>SYNTH</code> * @param headID (for groups) ID of the group's head (first) node * @param tailID (for groups) ID of the group's tail (last) node */ protected NodeEvent( Object source, int ID, long when, Node node, int nodeID, int parentID, int predID, int succID, int nodeType, int headID, int tailID ) { super( source, ID, when ); this.node = node; this.nodeID = nodeID; this.nodeType = nodeType; this.parentID = parentID; this.predID = predID; this.succID = succID; this.headID = headID; this.tailID = tailID; if( node == null ) { this.oldParentID = -1; this.oldPredID = -1; this.oldSuccID = -1; // this.oldHeadID = -1; // this.oldTailID = -1; } else { this.oldParentID = node.getGroup() == null ? -1 : node.getGroup().getNodeID(); this.oldPredID = node.getPredNode() == null ? -1 : node.getPredNode().getNodeID(); this.oldSuccID = node.getSuccNode() == null ? -1 : node.getSuccNode().getNodeID(); // if( nodeType == GROUP ) { // final Group g = (Group) node; // this.oldHeadID = g.getHeadNode() == null ? -1 : g.getHeadNode().getNodeID(); // this.oldTailID = g.getTailNode() == null ? -1 : g.getTailNode().getNodeID(); // } else { // this.oldHeadID = -1; // this.oldTailID = -1; // } } } /** * Constructs a <code>NodeEvent</code> from a valid node * notification OSC message. The provided node object is simply * stored for future reference through <code>getNode</code> and must * be updated by the caller according to the returned event. * * @param msg OSC message such as <code>/n_go</code> * @param source who shall be known as the source of the generated event * @param when what is proposed time of the event generation * @param node a client side representation node to use for the event, * or <code>null</code> if no representation is known. The caller is * responsible for updating the node's status from the returned * event. * * @throws IllegalArgumentException if the message doesn't contain a valid node message; you * can use <code>getIDFromOSCMessage</code> to determine if the * message is valid. */ public static NodeEvent fromOSCMessage( OSCMessage msg, Object source, long when, Node node ) { final int eventID = collValidCmds.indexOf( msg.getName() ); if( eventID == -1 ) throw new IllegalArgumentException( "Not a valid node notification message : " + msg.getName() ); final int nodeID = ((Number) msg.getArg( 0 )).intValue(); final int parentID = ((Number) msg.getArg( 1 )).intValue(); final int predID = ((Number) msg.getArg( 2 )).intValue(); final int succID = ((Number) msg.getArg( 3 )).intValue(); final int nodeType = ((Number) msg.getArg( 4 )).intValue(); final int headID = nodeType == GROUP ? ((Number) msg.getArg( 5 )).intValue() : -1; final int tailID = nodeType == GROUP ? ((Number) msg.getArg( 6 )).intValue() : -1; // let's trust the programmer for the sake of speed // if( node != null ) { // if( node.getNodeID() != nodeID ) throw new IllegalArgumentException( "Message and Node have different nodeIDs" ); // if( nodeType == SYNTH ) { // if( !(node instanceof Synth) ) throw new IllegalArgumentException( "Message and Node have different nodeTypes" ); // } else if( nodeType == GROUP ) { // if( !(node instanceof Group) ) throw new IllegalArgumentException( "Message and Node have different nodeTypes" ); // } // } return new NodeEvent( source, eventID, when, node, nodeID, parentID, predID, succID, nodeType, headID, tailID ); } public static NodeEvent fromOSCMessage( OSCMessage msg, Object source, long when ) { return fromOSCMessage( msg, source, when, null ); } /** * Queries the event ID which would be used if the event was * generated from a provided OSC message. * * @param msg the message to parse * @return the corresponding event ID or <code>-1</code> if the message command * is not in the list of valid notification commands */ public static int getIDFromOSCMessage( OSCMessage msg ) { return collValidCmds.indexOf( msg.getName() ); } /** * @return the representation of the node whose status changed. this may return <code>null</code> * if the client side object is not known. in this case, use <code>getNodeID</code> * to query the node's identifier. */ public Node getNode() { return node; } /** * @return the ID of the node whose status changed */ public int getNodeID() { return nodeID; } /** * @return the ID of the group in which this node sits. Note that if <code>getNode</code> * returns a valid node, you can also use <code>getParentGroup</code> on the * returned node. */ public int getParentGroupID() { return parentID; } /** * @return the ID of the group in which this node was sitting before the modification * occurred, or <code>-1</code>, if the node was not placed in a group. */ public int getOldParentGroupID() { return oldParentID; } /** * @return the ID of the node sitting just before the modified node in the graph, * or <code>-1</code> if there is no predecessor. Note that if <code>getNode</code> * returns a valid node, you can also use <code>getPredNode</code> on the * returned node. */ public int getPredNodeID() { return predID; } /** * @return the ID of the node which was sitting just before the modified node before the * modification occured, or <code>-1</code> if there was no predecessor. */ public int getOldPredNodeID() { return oldPredID; } /** * @return the ID of the node which was sitting just after the modified node before the * modification occured, or <code>-1</code> if there was no successor. */ public int getOldSuccNodeID() { return oldSuccID; } /** * @return the ID of the node sitting just after the modified node in the graph, * or <code>-1</code> if there is no successor. Note that if <code>getNode</code> * returns a valid node, you can also use <code>getSuccNode</code> on the * returned node. */ public int getSuccNodeID() { return succID; } /** * @return the type node that was modified, one of <code>SYNTH</code> or <code>GROUP</code>. * other values might be returned if a new version of supercollider introduces * other node classes. */ public int getNodeType() { return nodeType; } /** * @return if the modified node is a group, returns the ID of the node being the group's * head element. otherwise (or when the group is empty) returns * <code>-1</code>. Note that if <code>getNode</code> * returns a valid group, you can also use <code>getHeadNode</code> on the * returned group. */ public int getHeadNodeID() { return headID; } /** * @return if the modified node is a group, returns the ID of the node being the group's * tail element. otherwise (or when the group is empty) returns * <code>-1</code>. Note that if <code>getNode</code> * returns a valid group, you can also use <code>getTailNode</code> on the * returned group. */ public int getTailNodeID() { return tailID; } /** * Returns a list of strings describing * all known OSC command names that form * valid node notification messages, that is * <code>"/n_go"</code>, <code>"/n_end"</code> etc. * The returned list is immutable. * * @return a list whose elements are of class <code>String</code>, each element * corresponding to a valid OSC command. Note that the element index * reflects the event IDs, so <code>returnedList.get( GO ) == "/n_go"</code> etc. */ public static java.util.List getValidOSCCommands() { return collValidCmds; } /** * Used by the <code>EventManager</code> to * fuse successive events together when they queue. * Do not call this method. */ public boolean incorporate( BasicEvent oldEvent ) { // if( (oldEvent instanceof NodeEvent) && // (this.getSource() == oldEvent.getSource()) && // (this.getID() == oldEvent.getID()) ) { // // return ((NodeEvent) oldEvent).nodeID == this.nodeID; // // } else return false; } }