/* ========================
* JSynoptic : a free Synoptic editor
* ========================
*
* Project Info: http://jsynoptic.sourceforge.net/index.html
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 of the License, or (at your option) any later version.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* (C) Copyright 2001-2007, by :
* Corporate:
* EADS Astrium
* Individual:
* Claude Cazenave
*
* $Id: SceneGraphTreeNode.java,v 1.2 2008/11/14 17:03:14 cazenave Exp $
*
* Changes
* -------
* 9 janv. 08 : Initial public release
*
*/
package jsynoptic.plugins.java3d.tree;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.Enumeration;
import javax.media.j3d.Group;
import javax.media.j3d.Node;
import javax.media.j3d.NodeComponent;
import javax.media.j3d.SceneGraphObject;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.KeyStroke;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.tree.TreeNode;
import javax.swing.undo.CompoundEdit;
import simtools.ui.MenuResourceBundle;
import simtools.ui.ResourceFinder;
import jsynoptic.plugins.java3d.SceneGraphObjectHolder;
import jsynoptic.plugins.java3d.AttachDetach;
import jsynoptic.plugins.java3d.edit.AddEdit;
import jsynoptic.plugins.java3d.edit.RemoveEdit;
import jsynoptic.plugins.java3d.panels.SceneGraphObjectDialog;
import jsynoptic.ui.JSynoptic;
/**
* A Node to hold a Scene Graph Object
*/
public class SceneGraphTreeNode extends AbstractNode implements SceneGraphObjectHolder{
/**
* Register nodes resources and actions
*/
static void loadResources(){
addResources("javax.media.j3d.SceneGraphObject", "SceneGraphObject");
}
// Reuse some jsynoptic main ui resources
public static MenuResourceBundle sharedResources = ResourceFinder.getMenu(JSynoptic.class);
public static final Object NoDefaultAction=new Object();
public static void addActions(String className, Object... actions) {
if (className != null) {
try {
Class<?> c = Class.forName(className);
boolean noAction=actions!=null && actions.length>0 && actions[0]==NoDefaultAction;
if (SceneGraphObject.class.isAssignableFrom(c) && !noAction) {
Object[] actions2 = new Object[actions.length + 10];
System.arraycopy(actions, 0, actions2, 0, actions.length);
actions2[actions.length] = null;
actions2[actions.length + 1] = CutAction.class;
actions2[actions.length + 2] = CopyAction.class;
actions2[actions.length + 3] = PasteAction.class;
actions2[actions.length + 4] = RemoveAction.class;
actions2[actions.length + 5] = null;
actions2[actions.length + 6] = DeepCopyAction.class;
actions2[actions.length + 7] = DeepPasteAction.class;
actions2[actions.length + 8] = null;
actions2[actions.length + 9] = PropertiesAction.class;
actions = actions2;
}
} catch (ClassNotFoundException e) {
}
}
AbstractNodeAction.addActions(className, actions);
}
protected AttachDetach _ad;
// required for dynamic node creation
public SceneGraphTreeNode(Tree tree, Object graphObject, boolean getChildren) {
super(tree, graphObject, getChildren);
_ad=null;
}
@Override
public Node getSceneGraphNode(){
if(getGraphObject() instanceof Node){
return (Node)getGraphObject();
}
if(getParent()==null){
return null;
}
if(getParent() instanceof SceneGraphTreeNode){
return ((SceneGraphTreeNode)getParent()).getSceneGraphNode();
}
return null;
}
@Override
public Group getSceneGraphGroup(){
if(getGraphObject() instanceof Group){
return (Group)getGraphObject();
}
if(getParent()==null){
return null;
}
if(getParent() instanceof SceneGraphTreeNode){
return ((SceneGraphTreeNode)getParent()).getSceneGraphGroup();
}
return null;
}
@Override
public SceneGraphObject getSceneGraphObject() {
return (SceneGraphObject)getGraphObject();
}
@Override
public SceneGraphObjectHolder getParentHolder(){
TreeNode p=getParent();
if(p instanceof SceneGraphObjectHolder){
return (SceneGraphObjectHolder)p;
}
return null;
}
@Override
public void getChildrenHolders(ArrayList<SceneGraphObjectHolder> list){
Enumeration<?> e=children();
while(e.hasMoreElements()){
Object o=e.nextElement();
if(o instanceof SceneGraphObjectHolder){
list.add((SceneGraphObjectHolder)o);
}
}
}
@Override
public void getRootHolders(ArrayList<SceneGraphObjectHolder> list){
UniverseNode un=null;
TreeNode tn=this;
while(un==null && tn!=null){
tn=tn.getParent();
if(tn instanceof UniverseNode){
un=(UniverseNode)tn;
}
}
if(un!=null){
Enumeration<?> e=un.children();
while(e.hasMoreElements()){
Object o=e.nextElement();
if(o instanceof SceneGraphObjectHolder){
list.add((SceneGraphObjectHolder)o);
}
}
}
}
@Override
public UndoableEditListener getUndoableEditListener() {
return getTree().getUndoableEditListener();
}
@Override
public String getName(){
String sgoName=((SceneGraphObject)getGraphObject()).getName();
if(sgoName!=null){
return _name+"["+sgoName+"]";
}
return _name;
}
@Override
protected void getSceneGraphChildren(ArrayList<Object> list) {
return; // no child
}
/**
* This default implementation simply checks
* if a tree node class exists as a children of the current node
* This is used a default rule used to check the compatibility of
* the scene graph object with the current object
*/
@Override
public boolean canAddSceneGraphObject(SceneGraphObject obj){
return getChildrenNodeClass(obj) != null;
}
@Override
public SceneGraphObject addSceneGraphObject(SceneGraphObject obj){
throw new RuntimeException("addSceneGraphObject invalid argument ="+obj.getClass());
}
/**
* This default implementation simply checks
* if a tree node class exists as a children of the current node
* This is used a default rule used to check the compatibility of
* the scene graph object with the current object
*/
@Override
public boolean canRemoveSceneGraphObject(SceneGraphObject obj, SceneGraphObject oldObj){
return getChildrenNodeClass(obj) != null;
}
@Override
public void removeSceneGraphObject(SceneGraphObject obj, SceneGraphObject oldObj){
throw new RuntimeException("removeSceneGraphObject invalid argument ="+obj.getClass());
}
public void paste(boolean deepClone){
ArrayList<SceneGraphTreeNode> v=getTree().getClipBoard().get();
CompoundEdit compoundEdit = new CompoundEdit();
for(SceneGraphTreeNode o: v){
SceneGraphTreeNode cn=o.cloneNode(deepClone, this);
SceneGraphObject replaced=addSceneGraphObject((SceneGraphObject)cn.getGraphObject());
compoundEdit.addEdit(new AddEdit(this,
(SceneGraphObject)cn.getGraphObject(),
replaced));
}
compoundEdit.end();
getTree().getUndoableEditListener().undoableEditHappened(
new UndoableEditEvent(this, compoundEdit));
}
public boolean canPaste(){
ArrayList<SceneGraphTreeNode> v=getTree().getClipBoard().get();
if(v.size()==0){
return false;
}
for(SceneGraphTreeNode o: v){
if(!canAddSceneGraphObject((SceneGraphObject)o.getGraphObject())){
return false;
}
}
return true;
}
public void copy(boolean deepClone){
getTree().getClipBoard().set(cloneNode(deepClone, (AbstractNode)getParent()));
}
public void cut(){
copy(false);
remove();
}
public boolean canRemove(){
if(getParent() instanceof SceneGraphObjectHolder){
SceneGraphObjectHolder owner=(SceneGraphObjectHolder)getParent();
return owner.canRemoveSceneGraphObject((SceneGraphObject)getGraphObject(), null);
}
return false;
}
public void remove(){
if(getParent() instanceof SceneGraphObjectHolder){
SceneGraphObjectHolder owner=(SceneGraphObjectHolder)getParent();
owner.removeSceneGraphObject((SceneGraphObject)getGraphObject(), null);
RemoveEdit re=new RemoveEdit(owner, (SceneGraphObject)getGraphObject());
getTree().getUndoableEditListener().undoableEditHappened(
new UndoableEditEvent(this, re));
}
else{
throw new RuntimeException("Can not remove node from "+ getParent().getClass());
}
}
/**
* A generic tree node clone method
* A new tree node is created with inside a clone of the original scene graph
* One can override if needed
* @param forceDuplicate if true force duplication of nodes and sub graph nodes
* @param parent used to create a new tree node according to the cloned
* scene graph object
* @return a clone of the tree node with a clone of the scene graph object in
*/
public SceneGraphTreeNode cloneNode(boolean deepClone, AbstractNode parent){
SceneGraphObject o=(SceneGraphObject)getGraphObject();
Node n=getSceneGraphNode();
AttachDetach ad=null;
if(n!=null && n.isLive()){
ad=new AttachDetach(n);
ad.detach();
}
Object clone=null;
if(o instanceof Group){
clone=((Group)o).cloneTree(deepClone);
}
else if(o instanceof Node){
clone=((Node)o).cloneNode(deepClone);
}
else if(o instanceof NodeComponent){
clone=((NodeComponent)o).cloneNodeComponent(deepClone);
}
else{
if(ad!=null) ad.attach();
throw new RuntimeException("Can not clone "+o);
}
if(ad!=null) ad.attach();
return (SceneGraphTreeNode)parent.createNode(clone);
}
public boolean forceCapability(int bit){
if(!((SceneGraphObject)getGraphObject()).getCapability(bit)){
if(((SceneGraphObject)getGraphObject()).isLive()){
Node n= getSceneGraphNode();
if(_ad!=null){
throw new RuntimeException("Unexpected attach detach operation");
}
_ad=new AttachDetach(n);
_ad.detach();
}
((SceneGraphObject)getGraphObject()).setCapability(bit);
return true;
}
return false;
}
public void restoreCapability(int bit){
((SceneGraphObject)getGraphObject()).clearCapability(bit);
if(_ad!=null){
_ad.attach();
_ad=null;
}
}
public static class PropertiesAction extends AbstractNodeAction {
// required for dynamic action creation
public PropertiesAction() {
}
@Override
public void actionPerformed(ActionEvent e) {
SceneGraphObjectDialog.createDialog((SceneGraphTreeNode)getNode(),
getLocation(), null);
}
}
public static class DeepCopyAction extends AbstractNodeAction {
// required for dynamic action creation
public DeepCopyAction() {
}
@Override
public void actionPerformed(ActionEvent e) {
((SceneGraphTreeNode)getNode()).copy(true);
}
}
public static class DeepPasteAction extends AbstractNodeAction {
// required for dynamic action creation
public DeepPasteAction() {
}
@Override
public void actionPerformed(ActionEvent e) {
((SceneGraphTreeNode)getNode()).paste(true);
}
}
static abstract class SharedAction extends AbstractNodeAction {
// required for dynamic action creation
public SharedAction(String resourceKey) {
_resourceKey=resourceKey;
}
protected void init() {
putValue(Action.NAME, sharedResources.getStringValue(_resourceKey
+ "Action"));
ImageIcon i = sharedResources.getIcon(_resourceKey + "Image");
if (i != null) {
putValue(Action.SMALL_ICON, i);
}
KeyStroke k = sharedResources.getKeyStroke(_resourceKey + "Shortcut");
if (k != null) {
putValue(Action.ACCELERATOR_KEY, k);
}
String tip = sharedResources.getStringValue(_resourceKey + "Tip");
putValue(Action.SHORT_DESCRIPTION, tip);
validate();
}
protected void validate(){
if(!(getNode() instanceof SceneGraphTreeNode)){
setEnabled(false);
}
else{
if(_resourceKey.equals("paste")){
setEnabled(((SceneGraphTreeNode)getNode()).canPaste());
}
else if(_resourceKey.equals("copy")){
SceneGraphObject o=(SceneGraphObject)getNode().getGraphObject();
if((o instanceof Node)||(o instanceof NodeComponent)){
setEnabled(true);
}
else{
setEnabled(false);
}
}
else if(_resourceKey.equals("cut")){
setEnabled(((SceneGraphTreeNode)getNode()).canRemove());
}
else if(_resourceKey.equals("remove")){
setEnabled(((SceneGraphTreeNode)getNode()).canRemove());
}
}
}
@Override
public void actionPerformed(ActionEvent e) {
if(_resourceKey.equals("paste")){
((SceneGraphTreeNode)getNode()).paste(false);
}
else if(_resourceKey.equals("cut")){
((SceneGraphTreeNode)getNode()).cut();
}
else if(_resourceKey.equals("copy")){
((SceneGraphTreeNode)getNode()).copy(false);
}
else if(_resourceKey.equals("remove")){
((SceneGraphTreeNode)getNode()).remove();
}
}
}
public static class CutAction extends SharedAction {public CutAction() {super("cut");}}
public static class RemoveAction extends SharedAction {public RemoveAction() {super("remove");}}
public static class PasteAction extends SharedAction {public PasteAction() {super("paste");}}
public static class CopyAction extends SharedAction {public CopyAction() {super("copy");}}
}