Sfoglia il codice sorgente

SDK:
- MAJOR overhaul of the SceneExplorer node updates, updates are no longer blocking or reading from another thread
- Some issues with enums still
- simplified, improved and more versatile SceneExplorerProperty class


git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10083 75d07b2b-3a1a-0410-a2c5-0572b91ccdca

nor..67 12 anni fa
parent
commit
d385d5f3d3
25 ha cambiato i file con 403 aggiunte e 233 eliminazioni
  1. 12 9
      jme3-core/src/com/jme3/gde/core/appstates/AppStateNode.java
  2. 1 1
      jme3-core/src/com/jme3/gde/core/filters/actions/EnableFiterAction.java
  3. 0 18
      jme3-core/src/com/jme3/gde/core/navigator/DataScanner.java
  4. 0 43
      jme3-core/src/com/jme3/gde/core/navigator/SceneChangeListener.java
  5. 0 52
      jme3-core/src/com/jme3/gde/core/navigator/SceneProperty.java
  6. 1 1
      jme3-core/src/com/jme3/gde/core/properties/AudioTrackProperty.java
  7. 1 1
      jme3-core/src/com/jme3/gde/core/properties/EffectTrackEmitterProperty.java
  8. 1 1
      jme3-core/src/com/jme3/gde/core/properties/ParticleInfluencerProperty.java
  9. 183 57
      jme3-core/src/com/jme3/gde/core/properties/SceneExplorerProperty.java
  10. 1 1
      jme3-core/src/com/jme3/gde/core/properties/ScenePropertyChangeListener.java
  11. 1 1
      jme3-core/src/com/jme3/gde/core/properties/UserDataProperty.java
  12. 11 0
      jme3-core/src/com/jme3/gde/core/scene/FakeApplication.java
  13. 125 0
      jme3-core/src/com/jme3/gde/core/scene/NodeSyncAppState.java
  14. 11 0
      jme3-core/src/com/jme3/gde/core/scene/SceneApplication.java
  15. 2 1
      jme3-core/src/com/jme3/gde/core/sceneexplorer/SceneExplorerTopComponent.form
  16. 1 0
      jme3-core/src/com/jme3/gde/core/sceneexplorer/SceneExplorerTopComponent.java
  17. 36 13
      jme3-core/src/com/jme3/gde/core/sceneexplorer/nodes/AbstractSceneExplorerNode.java
  18. 5 4
      jme3-core/src/com/jme3/gde/core/sceneexplorer/nodes/JmeGenericControl.java
  19. 2 3
      jme3-core/src/com/jme3/gde/core/sceneexplorer/nodes/JmeGeometry.java
  20. 2 2
      jme3-core/src/com/jme3/gde/core/sceneexplorer/nodes/JmeParticleEmitter.java
  21. 1 1
      jme3-core/src/com/jme3/gde/core/sceneexplorer/nodes/JmeSpatial.java
  22. 2 2
      jme3-core/src/com/jme3/gde/core/sceneexplorer/nodes/JmeTrack.java
  23. 1 1
      jme3-core/src/com/jme3/gde/core/sceneexplorer/nodes/actions/UserDataDialog.form
  24. 3 8
      jme3-core/src/com/jme3/gde/core/sceneexplorer/nodes/actions/UserDataDialog.java
  25. 0 13
      jme3-core/src/com/jme3/gde/core/util/PropertyUtils.java

+ 12 - 9
jme3-core/src/com/jme3/gde/core/appstates/AppStateNode.java

@@ -113,10 +113,13 @@ public class AppStateNode extends AbstractNode implements ScenePropertyChangeLis
 
     @Override
     public void destroy() throws IOException {
+        AppStateManagerNode parentNode = (AppStateManagerNode) getParentNode();
         super.destroy();
         parent.detach(appState);
         //TODO:hack
-        ((AppStateManagerNode) getParentNode()).refresh();
+        if (parentNode != null) {
+            parentNode.refresh();
+        }
     }
 
     @Override
@@ -130,12 +133,6 @@ public class AppStateNode extends AbstractNode implements ScenePropertyChangeLis
             return sheet;
         }
 
-//        Sheet.Set set2 = Sheet.createPropertiesSet();
-//        set2.setDisplayName("AppStateMethods");
-//        set2.setName(appState.getClass().getName() + ".hack");
-//        createMethods(appState.getClass(), set2, obj);
-//        sheet.put(set2);
-
         Sheet.Set set = Sheet.createPropertiesSet();
         set.setDisplayName("AppState");
         set.setName(appState.getClass().getName());
@@ -148,7 +145,6 @@ public class AppStateNode extends AbstractNode implements ScenePropertyChangeLis
     protected Property<?> makeProperty(Object obj, Class returntype, String method, String name) {
         Property<?> prop = null;
         try {
-            //getExplorerObjectClass().cast(obj)
             prop = new SceneExplorerProperty(appState.getClass().cast(obj), returntype, method, null, this);
             prop.setName(name);
         } catch (NoSuchMethodException ex) {
@@ -186,6 +182,13 @@ public class AppStateNode extends AbstractNode implements ScenePropertyChangeLis
         }
     }
 
-    public void propertyChange(String property, Object oldValue, Object newValue) {
+    public void propertyChange(final String type, final String name, final Object before, final Object after) {
+        if (SceneExplorerProperty.PROP_USER_CHANGE.equals(type)) {
+            firePropertyChange(name, before, after);
+        } else if (SceneExplorerProperty.PROP_SCENE_CHANGE.equals(type)) {
+            firePropertyChange(name, before, after);
+        } else if (SceneExplorerProperty.PROP_INIT_CHANGE.equals(type)) {
+            firePropertyChange(name, before, after);
+        }
     }
 }

+ 1 - 1
jme3-core/src/com/jme3/gde/core/filters/actions/EnableFiterAction.java

@@ -59,7 +59,7 @@ public class EnableFiterAction implements ActionListener {
                 Filter filter=context.getFilter();
                 filter.setEnabled(!filter.isEnabled());                        
                 Logger.getLogger(EnableFiterAction.class.getName()).info( (filter.isEnabled()?"Enabled":"Disabled")+" "+filter.getName());
-                context.propertyChange("Enabled", !filter.isEnabled(), filter.isEnabled());
+                context.propertyChange("PROP_USER_CHANGE", "Enabled", !filter.isEnabled(), filter.isEnabled());
               
                 return null;
             }

+ 0 - 18
jme3-core/src/com/jme3/gde/core/navigator/DataScanner.java

@@ -1,18 +0,0 @@
-package com.jme3.gde.core.navigator;
-
-import com.jme3.app.state.AbstractAppState;
-
-/**
- *
- * @author normenhansen
- */
-
-
-public class DataScanner extends AbstractAppState{
-
-    @Override
-    public void update(float tpf) {
-        super.update(tpf);
-    }
-    
-}

+ 0 - 43
jme3-core/src/com/jme3/gde/core/navigator/SceneChangeListener.java

@@ -1,43 +0,0 @@
-/*
- *  Copyright (c) 2009-2010 jMonkeyEngine
- *  All rights reserved.
- * 
- *  Redistribution and use in source and binary forms, with or without
- *  modification, are permitted provided that the following conditions are
- *  met:
- * 
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 
- *  * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- * 
- *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- *  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- *  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.jme3.gde.core.navigator;
-
-import com.jme3.gde.core.properties.*;
-
-/**
- *
- * @author normenhansen
- */
-public interface SceneChangeListener {
-    public void propertyChange(String property, Object oldValue, Object newValue);
-}

+ 0 - 52
jme3-core/src/com/jme3/gde/core/navigator/SceneProperty.java

@@ -1,52 +0,0 @@
-/*
- *  Copyright (c) 2009-2010 jMonkeyEngine
- *  All rights reserved.
- * 
- *  Redistribution and use in source and binary forms, with or without
- *  modification, are permitted provided that the following conditions are
- *  met:
- * 
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 
- *  * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- * 
- *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- *  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- *  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package com.jme3.gde.core.navigator;
-
-import org.openide.nodes.PropertySupport;
-import org.openide.util.Lookup;
-
-/**
- *
- * @author normenhansen
- */
-@SuppressWarnings("unchecked")
-public class SceneProperty<T> extends PropertySupport.Reflection<T> {
-
-    Lookup lookup;
-    Object _originalValue;
-
-    public SceneProperty(Lookup lookup, Object instance, Class<T> valueType, String property) throws NoSuchMethodException {
-        super(instance, valueType, property);
-        DataScanner scanner = lookup.lookup(DataScanner.class);
-    }
-    
-}

+ 1 - 1
jme3-core/src/com/jme3/gde/core/properties/AudioTrackProperty.java

@@ -85,7 +85,7 @@ public class AudioTrackProperty extends PropertySupport.ReadWrite<AudioNode> {
     private void notifyListeners(Object before, Object after) {
         for (Iterator<ScenePropertyChangeListener> it = listeners.iterator(); it.hasNext();) {
             ScenePropertyChangeListener propertyChangeListener = it.next();
-            propertyChangeListener.propertyChange(getName(), before, after);
+            propertyChangeListener.propertyChange("PROP_USER_CHANGE", getName(), before, after);
         }
 
     }

+ 1 - 1
jme3-core/src/com/jme3/gde/core/properties/EffectTrackEmitterProperty.java

@@ -85,7 +85,7 @@ public class EffectTrackEmitterProperty extends PropertySupport.ReadWrite<Partic
     private void notifyListeners(Object before, Object after) {
         for (Iterator<ScenePropertyChangeListener> it = listeners.iterator(); it.hasNext();) {
             ScenePropertyChangeListener propertyChangeListener = it.next();
-            propertyChangeListener.propertyChange(getName(), before, after);
+            propertyChangeListener.propertyChange("PROP_USER_CHANGE", getName(), before, after);
         }
 
     }

+ 1 - 1
jme3-core/src/com/jme3/gde/core/properties/ParticleInfluencerProperty.java

@@ -88,7 +88,7 @@ public class ParticleInfluencerProperty extends PropertySupport.ReadWrite<Partic
     private void notifyListeners(Object before, Object after) {
         for (Iterator<ScenePropertyChangeListener> it = listeners.iterator(); it.hasNext();) {
             ScenePropertyChangeListener propertyChangeListener = it.next();
-            propertyChangeListener.propertyChange(getName(), before, after);
+            propertyChangeListener.propertyChange("PROP_USER_CHANGE", getName(), before, after);
         }
 
     }

+ 183 - 57
jme3-core/src/com/jme3/gde/core/properties/SceneExplorerProperty.java

@@ -41,16 +41,18 @@ import com.jme3.math.Matrix3f;
 import com.jme3.math.Quaternion;
 import com.jme3.math.Vector2f;
 import com.jme3.math.Vector3f;
+import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import org.openide.nodes.PropertySupport;
 import org.openide.util.Exceptions;
 import org.openide.util.Lookup;
+import org.openide.util.Mutex;
 
 /**
  *
@@ -59,6 +61,16 @@ import org.openide.util.Lookup;
 @SuppressWarnings("unchecked")
 public class SceneExplorerProperty<T> extends PropertySupport.Reflection<T> {
 
+    public static final String PROP_SCENE_CHANGE = "PROP_SCENE_CHANGE";
+    public static final String PROP_USER_CHANGE = "PROP_USER_CHANGE";
+    public static final String PROP_INIT_CHANGE = "PROP_INIT_CHANGE";
+    private T objectLocal;
+    private final boolean cloneable;
+    private final boolean instantiable;
+    private final boolean primitive;
+    private final Mutex mutex = new Mutex();
+    private boolean inited = false;
+    private final boolean editable;
     protected LinkedList<ScenePropertyChangeListener> listeners = new LinkedList<ScenePropertyChangeListener>();
 
     public SceneExplorerProperty(T instance, Class valueType, String getter, String setter) throws NoSuchMethodException {
@@ -67,7 +79,14 @@ public class SceneExplorerProperty<T> extends PropertySupport.Reflection<T> {
 
     public SceneExplorerProperty(T instance, Class valueType, String getter, String setter, ScenePropertyChangeListener listener) throws NoSuchMethodException {
         super(instance, valueType, getter, setter);
-        addPropertyChangeListener(listener);
+        primitive = isPrimitive(instance, getter);
+        if (!primitive) {
+            cloneable = canClone(instance, getter);
+            instantiable = canRecreate(instance, getter);
+        } else {
+            cloneable = false;
+            instantiable = false;
+        }
         if (valueType == Vector3f.class) {
             setPropertyEditorClass(Vector3fPropertyEditor.class);
         } else if (valueType == Quaternion.class) {
@@ -87,70 +106,171 @@ public class SceneExplorerProperty<T> extends PropertySupport.Reflection<T> {
         for (SceneExplorerPropertyEditor di : Lookup.getDefault().lookupAll(SceneExplorerPropertyEditor.class)) {
             di.setEditor(valueType, this);
         }
+        //TODO: instantiates editor?
+        editable = getPropertyEditor() != null;
+        addPropertyChangeListener(listener);
+    }
+
+    /**
+     * synchronizes the local and scene value
+     */
+    public void syncValue() {
+        if (!editable) {
+            return;
+        }
+        mutex.postWriteRequest(new Runnable() {
+            public void run() {
+                T realValue = getSuperValue();
+                if (objectLocal == null && !inited) {
+                    inited = true;
+                    objectLocal = duplicateObject(realValue);
+                    notifyListeners(PROP_INIT_CHANGE, null, objectLocal);
+                } else if (objectLocal != null && !objectLocal.equals(realValue)) {
+                    T oldObject = objectLocal;
+                    T newObject = duplicateObject(realValue);
+                    notifyListeners(PROP_SCENE_CHANGE, oldObject, newObject);
+                    objectLocal = newObject;
+                }
+            }
+        });
     }
 
     @Override
     public T getValue() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
-        return super.getValue();
-//        try {
-//            return SceneApplication.getApplication().enqueue(new Callable<T>() {
-//
-//                public T call() throws Exception {
-//                    return getSuperValue();
-//                }
-//            }).get();
-//        } catch (InterruptedException ex) {
-//            Exceptions.printStackTrace(ex);
-//        } catch (ExecutionException ex) {
-//            Exceptions.printStackTrace(ex);
-//        }
-//        return null;
+        return objectLocal;
     }
 
-    private T getSuperValue() {
+    @Override
+    public void setValue(final T val) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+        mutex.postWriteRequest(new Runnable() {
+            public void run() {
+                final T oldObject = (T) objectLocal;
+                objectLocal = val;
+                final T sceneObject = (T) duplicateObject(val);
+                notifyListeners(PROP_USER_CHANGE, oldObject, objectLocal);
+                SceneApplication.getApplication().enqueue(new Callable<Void>() {
+                    public Void call() throws Exception {
+                        setSuperValue(sceneObject);
+                        return null;
+                    }
+                });
+            }
+        });
+    }
+
+    private boolean isPrimitive(Object obj, String getter) {
         try {
-            return super.getValue();
-        } catch (IllegalAccessException ex) {
-            Exceptions.printStackTrace(ex);
-        } catch (IllegalArgumentException ex) {
-            Exceptions.printStackTrace(ex);
-        } catch (InvocationTargetException ex) {
+            Class objClass = obj.getClass().getMethod(getter).getReturnType();
+            if (objClass.isPrimitive()) {
+                return true;
+            }
+        } catch (NoSuchMethodException ex) {
+        } catch (SecurityException ex) {
             Exceptions.printStackTrace(ex);
         }
-        return null;
+        return false;
     }
 
-    @Override
-    public void setValue(final T val) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+    private boolean canClone(Object obj, String getter) {
         try {
-            notifyListeners(getSuperValue(), val);
-            SceneApplication.getApplication().enqueue(new Callable<Void>() {
+            Class objClass = obj.getClass().getMethod(getter).getReturnType();
+            if (Enum.class.isAssignableFrom(objClass)) {
+                return false;
+            }
+            Method meth = objClass.getMethod("clone");
+            if (meth != null) {
+                if (meth.getParameterTypes().length == 0
+                        && meth.getReturnType().equals(obj.getClass())) {
+                    return true;
+                }
+            }
+        } catch (NoSuchMethodException ex) {
+        } catch (SecurityException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+        return false;
+    }
 
-                public Void call() throws Exception {
-                    setSuperValue(val);
-                    return null;
+    private boolean canRecreate(Object obj, String getter) {
+        try {
+            Class objClass = obj.getClass().getMethod(getter).getReturnType();
+            if (Enum.class.isAssignableFrom(objClass)) {
+                return false;
+            }
+            Constructor[] constructors = objClass.getConstructors();
+            for (Constructor constructor : constructors) {
+                Class[] types = constructor.getParameterTypes();
+                if (types.length == 1 && types[0].equals(obj.getClass())) {
+                    return true;
                 }
-            }).get();
-        } catch (InterruptedException ex) {
+            }
+        } catch (NoSuchMethodException ex) {
+            Exceptions.printStackTrace(ex);
+        } catch (SecurityException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+        return false;
+    }
+
+    private T duplicateObject(T a) {
+        T obj = a;
+        if (primitive) {
+            return obj;
+        } else if (cloneable) {
+            try {
+                obj = (T) a.getClass().getMethod("clone").invoke(a);
+            } catch (IllegalAccessException ex) {
+                Exceptions.printStackTrace(ex);
+            } catch (IllegalArgumentException ex) {
+                Exceptions.printStackTrace(ex);
+            } catch (InvocationTargetException ex) {
+                Exceptions.printStackTrace(ex);
+            } catch (NoSuchMethodException ex) {
+                Exceptions.printStackTrace(ex);
+            } catch (SecurityException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        } else if (instantiable) {
+            try {
+                obj = (T) a.getClass().getConstructor(a.getClass()).newInstance(a);
+            } catch (InstantiationException ex) {
+                Exceptions.printStackTrace(ex);
+            } catch (IllegalAccessException ex) {
+                Exceptions.printStackTrace(ex);
+            } catch (IllegalArgumentException ex) {
+                Exceptions.printStackTrace(ex);
+            } catch (InvocationTargetException ex) {
+                Exceptions.printStackTrace(ex);
+            } catch (NoSuchMethodException ex) {
+                Exceptions.printStackTrace(ex);
+            } catch (SecurityException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        }
+        return obj;
+    }
+
+    private void setSuperValue(T val) {
+        setSuperValue(val, true);
+    }
+
+    private T getSuperValue() {
+        try {
+            return super.getValue();
+        } catch (IllegalAccessException ex) {
             Exceptions.printStackTrace(ex);
-        } catch (ExecutionException ex) {
+        } catch (IllegalArgumentException ex) {
+            Exceptions.printStackTrace(ex);
+        } catch (InvocationTargetException ex) {
             Exceptions.printStackTrace(ex);
         }
+        return null;
     }
 
     private void setSuperValue(T val, boolean undo) {
         try {
             if (undo) {
-                try {
-                    Object oldValue = getSuperValue();
-                    if (oldValue.getClass().getMethod("clone") != null) {
-                        addUndo(oldValue.getClass().getMethod("clone").invoke(oldValue), val);
-                        Logger.getLogger(SceneExplorerProperty.class.getName()).log(Level.INFO, "Add cloned undo {0}", oldValue.getClass().getMethod("clone").invoke(oldValue));
-                    }
-                } catch (Exception e) {
-                    addUndo(getSuperValue(), val);
-                    Logger.getLogger(SceneExplorerProperty.class.getName()).log(Level.INFO, "Add undo {0}", getSuperValue());
-                }
+                addUndo(duplicateObject(getSuperValue()), val);
             }
             super.setValue(val);
         } catch (IllegalAccessException ex) {
@@ -162,10 +282,6 @@ public class SceneExplorerProperty<T> extends PropertySupport.Reflection<T> {
         }
     }
 
-    private void setSuperValue(T val) {
-        setSuperValue(val, true);
-    }
-
     protected void addUndo(final Object before, final Object after) {
         SceneUndoRedoManager undoRedo = Lookup.getDefault().lookup(SceneUndoRedoManager.class);
         if (undoRedo == null) {
@@ -173,15 +289,17 @@ public class SceneExplorerProperty<T> extends PropertySupport.Reflection<T> {
             return;
         }
         undoRedo.addEdit(this, new AbstractUndoableSceneEdit() {
-
             @Override
             public void sceneUndo() {
-                Logger.getLogger(SceneExplorerProperty.class.getName()).log(Level.INFO, "Do undo {0}", before);
+                Logger.getLogger(SceneExplorerProperty.class.getName()).log(Level.FINE, "Do undo {0}", before);
+                notifyListeners(PROP_USER_CHANGE, after, before);
                 setSuperValue((T) before, false);
             }
 
             @Override
             public void sceneRedo() {
+                Logger.getLogger(SceneExplorerProperty.class.getName()).log(Level.FINE, "Do redo {0}", before);
+                notifyListeners(PROP_USER_CHANGE, before, after);
                 setSuperValue((T) after, false);
             }
 
@@ -196,17 +314,25 @@ public class SceneExplorerProperty<T> extends PropertySupport.Reflection<T> {
     }
 
     public void addPropertyChangeListener(ScenePropertyChangeListener listener) {
-        listeners.add(listener);
+        if (listener != null) {
+            listeners.add(listener);
+        }
     }
 
     public void removePropertyChangeListener(ScenePropertyChangeListener listener) {
-        listeners.remove(listener);
+        if (listener != null) {
+            listeners.remove(listener);
+        }
     }
 
-    private void notifyListeners(Object before, Object after) {
-        for (Iterator<ScenePropertyChangeListener> it = listeners.iterator(); it.hasNext();) {
-            ScenePropertyChangeListener propertyChangeListener = it.next();
-            propertyChangeListener.propertyChange(getName(), before, after);
-        }
+    private void notifyListeners(final String type, final Object before, final Object after) {
+        java.awt.EventQueue.invokeLater(new Runnable() {
+            public void run() {
+                for (Iterator<ScenePropertyChangeListener> it = listeners.iterator(); it.hasNext();) {
+                    ScenePropertyChangeListener propertyChangeListener = it.next();
+                    propertyChangeListener.propertyChange(type, getName(), before, after);
+                }
+            }
+        });
     }
 }

+ 1 - 1
jme3-core/src/com/jme3/gde/core/properties/ScenePropertyChangeListener.java

@@ -37,5 +37,5 @@ package com.jme3.gde.core.properties;
  * @author normenhansen
  */
 public interface ScenePropertyChangeListener {
-    public void propertyChange(String property, Object oldValue, Object newValue);
+    public void propertyChange(String type, String name, Object oldValue, Object newValue);
 }

+ 1 - 1
jme3-core/src/com/jme3/gde/core/properties/UserDataProperty.java

@@ -159,7 +159,7 @@ public class UserDataProperty extends PropertySupport.ReadWrite<String> {
     private void notifyListeners(Object before, Object after) {
         for (Iterator<ScenePropertyChangeListener> it = listeners.iterator(); it.hasNext();) {
             ScenePropertyChangeListener propertyChangeListener = it.next();
-            propertyChangeListener.propertyChange(getName(), before, after);
+            propertyChangeListener.propertyChange("PROP_USER_CHANGE", getName(), before, after);
         }
     }
 }

+ 11 - 0
jme3-core/src/com/jme3/gde/core/scene/FakeApplication.java

@@ -83,6 +83,13 @@ public class FakeApplication extends SimpleApplication {
     private FakeAppStateManager appStateManager;
     private FakeRenderManager renderManager;
 
+    public FakeApplication(Node guiNode, AssetManager assetManager, Camera cam) {
+        this.guiNode = guiNode;
+        this.assetManager = assetManager;
+        this.cam = cam;
+        this.appStateManager = new FakeAppStateManager(this);
+    }
+    
     public FakeApplication(Node rootNode, Node guiNode, AssetManager assetManager, Camera cam) {
         this.rootNode = rootNode;
         this.guiNode = guiNode;
@@ -385,6 +392,10 @@ public class FakeApplication extends SimpleApplication {
      */
     private ScheduledThreadPoolExecutor fakeAppThread = new ScheduledThreadPoolExecutor(1);
 
+    public void setRootNode(Node rootNode) {
+        this.rootNode = rootNode;
+    }
+    
     public void stopFakeApp() {
         fakeAppThread.shutdown();
     }

+ 125 - 0
jme3-core/src/com/jme3/gde/core/scene/NodeSyncAppState.java

@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2003-2012 jMonkeyEngine
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * 
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * 
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.gde.core.scene;
+
+import com.jme3.app.Application;
+import com.jme3.app.state.AbstractAppState;
+import com.jme3.app.state.AppStateManager;
+import com.jme3.gde.core.sceneexplorer.nodes.AbstractSceneExplorerNode;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import org.openide.util.Lookup.Result;
+import org.openide.util.LookupEvent;
+import org.openide.util.LookupListener;
+import org.openide.util.Utilities;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class NodeSyncAppState extends AbstractAppState implements LookupListener {
+
+    private final List<AbstractSceneExplorerNode> newNodes = Collections.synchronizedList(new LinkedList<AbstractSceneExplorerNode>());
+    private final List<AbstractSceneExplorerNode> oldNodes = Collections.synchronizedList(new LinkedList<AbstractSceneExplorerNode>());
+    private final Result<AbstractSceneExplorerNode> nodeSelectionResult;
+    private AbstractSceneExplorerNode node;
+    private float timeStep = 1;
+    private float timer = 0;
+
+    public NodeSyncAppState() {
+        nodeSelectionResult = Utilities.actionsGlobalContext().lookupResult(AbstractSceneExplorerNode.class);
+    }
+
+    @Override
+    public void initialize(AppStateManager stateManager, Application app) {
+        super.initialize(stateManager, app);
+        nodeSelectionResult.addLookupListener(this);
+    }
+
+    @Override
+    public void update(float tpf) {
+        super.update(tpf);
+        synchronized (newNodes) {
+            for (Iterator<AbstractSceneExplorerNode> it = newNodes.iterator(); it.hasNext();) {
+                AbstractSceneExplorerNode abstractSceneExplorerNode = it.next();
+                abstractSceneExplorerNode.syncSceneData();
+                it.remove();
+            }
+        }
+        timer += tpf;
+        if (timer > timeStep) {
+            timer = 0;
+            AbstractSceneExplorerNode node = this.node;
+            if (initialized && node != null) {
+                node.syncSceneData();
+            }
+        }
+        synchronized (oldNodes) {
+            for (Iterator<AbstractSceneExplorerNode> it = oldNodes.iterator(); it.hasNext();) {
+                AbstractSceneExplorerNode abstractSceneExplorerNode = it.next();
+                abstractSceneExplorerNode.syncSceneData();
+                it.remove();
+            }
+        }
+    }
+
+    public void resultChanged(LookupEvent ev) {
+        Collection collection = nodeSelectionResult.allInstances();
+        AbstractSceneExplorerNode newNode = null;
+        for (Iterator it = collection.iterator(); it.hasNext();) {
+            Object object = it.next();
+            if (object instanceof AbstractSceneExplorerNode) {
+                if (object != null) {
+                    synchronized (newNodes) {
+                        newNodes.add((AbstractSceneExplorerNode) object);
+                    }
+                }
+                newNode = (AbstractSceneExplorerNode) object;
+            }
+        }
+        if (node != null) {
+            synchronized (oldNodes) {
+                oldNodes.add(node);
+            }
+        }
+        node = newNode;
+    }
+
+    @Override
+    public void cleanup() {
+        nodeSelectionResult.removeLookupListener(this);
+        super.cleanup();
+    }
+}

+ 11 - 0
jme3-core/src/com/jme3/gde/core/scene/SceneApplication.java

@@ -116,6 +116,7 @@ public class SceneApplication extends Application implements LookupProvider {
     boolean useCanvas = false;
     private BulletAppState physicsState;
     private Thread thread;
+    private NodeSyncAppState nodeSync;
     private FakeApplication fakeApp;
 
     public SceneApplication() {
@@ -140,6 +141,8 @@ public class SceneApplication extends Application implements LookupProvider {
                 createCanvas();
                 startCanvas(true);
             }
+            nodeSync = new NodeSyncAppState();
+            stateManager.attach(nodeSync);
             progressHandle.progress("initialize Base Application", 1);
         } catch (Exception e) {
             getProgressHandle().finish();
@@ -375,6 +378,7 @@ public class SceneApplication extends Application implements LookupProvider {
                 } else {
                     camController.disable();
                 }
+                //TODO: reuse fakeapp
                 fakeApp = new FakeApplication(rootNode, guiNode, request.getManager(), cam);
                 request.setFakeApp(fakeApp);
                 enqueue(new Callable() {
@@ -387,6 +391,13 @@ public class SceneApplication extends Application implements LookupProvider {
                             StatusDisplayer.getDefault().setStatusText("could not load Spatial from request: " + getCurrentSceneRequest().getWindowTitle());
                             return null;
                         }
+                        //TODO: bit dangerous, setting rootNode late
+                        //      still it should only be accessed from the
+                        //      update loop and be set until then.
+                                
+                        if (model instanceof Node) {
+                            fakeApp.setRootNode((Node) model);
+                        }
                         rootNode.attachChild(model);
                         if (request.getToolNode() != null) {
                             toolsNode.attachChild(request.getToolNode());

+ 2 - 1
jme3-core/src/com/jme3/gde/core/sceneexplorer/SceneExplorerTopComponent.form

@@ -1,4 +1,4 @@
-<?xml version="1.1" encoding="UTF-8" ?>
+<?xml version="1.0" encoding="UTF-8" ?>
 
 <Form version="1.4" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
   <AuxValues>
@@ -40,6 +40,7 @@
     </Container>
     <Container class="javax.swing.JToolBar" name="jToolBar1">
       <Properties>
+        <Property name="floatable" type="boolean" value="false"/>
         <Property name="rollover" type="boolean" value="true"/>
       </Properties>
 

+ 1 - 0
jme3-core/src/com/jme3/gde/core/sceneexplorer/SceneExplorerTopComponent.java

@@ -118,6 +118,7 @@ public final class SceneExplorerTopComponent extends TopComponent implements Exp
         jToolBar1 = new javax.swing.JToolBar();
         jButton1 = new javax.swing.JButton();
 
+        jToolBar1.setFloatable(false);
         jToolBar1.setRollover(true);
 
         org.openide.awt.Mnemonics.setLocalizedText(jButton1, org.openide.util.NbBundle.getMessage(SceneExplorerTopComponent.class, "SceneExplorerTopComponent.jButton1.text")); // NOI18N

+ 36 - 13
jme3-core/src/com/jme3/gde/core/sceneexplorer/nodes/AbstractSceneExplorerNode.java

@@ -31,18 +31,19 @@
  */
 package com.jme3.gde.core.sceneexplorer.nodes;
 
-import com.jme3.gde.core.util.DynamicLookup;
 import com.jme3.gde.core.properties.SceneExplorerProperty;
 import com.jme3.gde.core.properties.ScenePropertyChangeListener;
+import com.jme3.gde.core.util.DynamicLookup;
 import com.jme3.gde.core.util.PropertyUtils;
 import java.beans.PropertyDescriptor;
 import java.lang.reflect.Field;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
 import org.openide.loaders.DataObject;
 import org.openide.nodes.AbstractNode;
 import org.openide.nodes.Children;
 import org.openide.nodes.Node;
-import org.openide.nodes.NodeAdapter;
-import org.openide.nodes.NodeListener;
 import org.openide.nodes.Sheet;
 import org.openide.util.Exceptions;
 import org.openide.util.Lookup;
@@ -60,6 +61,7 @@ public abstract class AbstractSceneExplorerNode extends AbstractNode implements
     protected final InstanceContent lookupContents;
     protected boolean readOnly = false;
     protected DataObject dataObject;
+    private final List<Property> sceneProperties = Collections.synchronizedList(new LinkedList<Property>());
 
     public AbstractSceneExplorerNode() {
         super(Children.LEAF, new DynamicLookup(new InstanceContent()));
@@ -69,7 +71,7 @@ public abstract class AbstractSceneExplorerNode extends AbstractNode implements
     public AbstractSceneExplorerNode(Children children, DataObject dataObject) {
         super(children, new ProxyLookup(dataObject.getLookup(), new DynamicLookup(new InstanceContent())));
         this.dataObject = dataObject;
-        lookupContents = getLookup().lookup(DynamicLookup.class).getInstanceContent();        
+        lookupContents = getLookup().lookup(DynamicLookup.class).getInstanceContent();
     }
 
     public AbstractSceneExplorerNode(DataObject dataObject) {
@@ -106,9 +108,9 @@ public abstract class AbstractSceneExplorerNode extends AbstractNode implements
             dataObject.setModified(true);
         }
     }
-
+    
     /**
-     * returns the PropertySet with the given name (mostly Class.name)
+     * returns the PropertySet with the given name
      * @param name
      * @return The PropertySet or null if no PropertySet by that name exists
      */
@@ -167,8 +169,8 @@ public abstract class AbstractSceneExplorerNode extends AbstractNode implements
         }
         return prop;
     }
-    
-     protected Property makeEmbedProperty(Object obj,Class objectClass, Class returntype, String method, String setter, String name) {
+
+    protected Property makeEmbedProperty(Object obj, Class objectClass, Class returntype, String method, String setter, String name) {
         Property prop = null;
         try {
             if (readOnly) {
@@ -184,7 +186,6 @@ public abstract class AbstractSceneExplorerNode extends AbstractNode implements
         return prop;
     }
 
-
     protected void createFields(Class c, Sheet.Set set, Object obj) throws SecurityException {
         for (Field field : c.getDeclaredFields()) {
             PropertyDescriptor prop = PropertyUtils.getPropertyDescriptor(c, field);
@@ -194,9 +195,32 @@ public abstract class AbstractSceneExplorerNode extends AbstractNode implements
         }
     }
 
-    public void propertyChange(final String name, final Object before, final Object after) {
-        fireSave(true);
-        firePropertyChange(name, before, after);
+    @Override
+    protected Sheet createSheet() {
+        return Sheet.createDefault();
+    }
+    
+    public void syncSceneData() {
+        //TODO: precache structure to avoid locks?
+        for (PropertySet propertySet : getPropertySets()) {
+            for (Property<?> property : propertySet.getProperties()) {
+                if(property instanceof SceneExplorerProperty){
+                    SceneExplorerProperty prop = (SceneExplorerProperty)property;
+                    prop.syncValue();
+                }
+            }
+        }
+    }
+
+    public void propertyChange(final String type, final String name, final Object before, final Object after) {
+        if (SceneExplorerProperty.PROP_USER_CHANGE.equals(type)) {
+            fireSave(true);
+            firePropertyChange(name, before, after);
+        } else if (SceneExplorerProperty.PROP_SCENE_CHANGE.equals(type)) {
+            firePropertyChange(name, before, after);
+        } else if (SceneExplorerProperty.PROP_INIT_CHANGE.equals(type)) {
+            firePropertyChange(name, before, after);
+        }
     }
 
     public Class getExplorerNodeClass() {
@@ -208,5 +232,4 @@ public abstract class AbstractSceneExplorerNode extends AbstractNode implements
     public Node[] createNodes(Object key, DataObject dataObject, boolean readOnly) {
         return new Node[]{Node.EMPTY};
     }
-    
 }

+ 5 - 4
jme3-core/src/com/jme3/gde/core/sceneexplorer/nodes/JmeGenericControl.java

@@ -130,7 +130,6 @@ public class JmeGenericControl extends AbstractNode implements ScenePropertyChan
         final Spatial spat = getParentNode().getLookup().lookup(Spatial.class);
         try {
             SceneApplication.getApplication().enqueue(new Callable<Void>() {
-
                 public Void call() throws Exception {
                     spat.removeControl(control);
                     return null;
@@ -161,7 +160,7 @@ public class JmeGenericControl extends AbstractNode implements ScenePropertyChan
 //            if (readOnly) {
 //                prop = new SceneExplorerProperty(control.getClass().cast(obj), returntype, method, null);
 //            } else {
-                prop = new SceneExplorerProperty(control.getClass().cast(obj), returntype, method, setter, this);
+            prop = new SceneExplorerProperty(control.getClass().cast(obj), returntype, method, setter, this);
 //            }
             prop.setName(name);
 
@@ -180,8 +179,10 @@ public class JmeGenericControl extends AbstractNode implements ScenePropertyChan
         }
     }
 
-    public void propertyChange(String property, Object oldValue, Object newValue) {
-        dobject.setModified(true);
+    public void propertyChange(String type, String name, Object oldValue, Object newValue) {
+        if (type.equals("PROP_USER_CHANGE")) {
+            dobject.setModified(true);
+        }
 //        throw new UnsupportedOperationException("Not supported yet.");
     }
 }

+ 2 - 3
jme3-core/src/com/jme3/gde/core/sceneexplorer/nodes/JmeGeometry.java

@@ -41,7 +41,6 @@ import java.awt.Image;
 import java.io.IOException;
 import java.util.concurrent.Callable;
 import org.openide.loaders.DataObject;
-import org.openide.nodes.AbstractNode;
 import org.openide.nodes.Sheet;
 import org.openide.util.ImageUtilities;
 
@@ -144,8 +143,8 @@ public class JmeGeometry extends JmeSpatial implements MaterialChangeListener {
     }
 
     @Override
-    public void propertyChange(String name, final Object before, final Object after) {
-        super.propertyChange(name, before, after);        
+    public void propertyChange(String type, String name, final Object before, final Object after) {
+        super.propertyChange(type, name, before, after);        
         if (name.equals("Material")) {           
             java.awt.EventQueue.invokeLater(new Runnable() {
 

+ 2 - 2
jme3-core/src/com/jme3/gde/core/sceneexplorer/nodes/JmeParticleEmitter.java

@@ -154,8 +154,8 @@ public class JmeParticleEmitter extends JmeGeometry {
     }
 
     @Override
-    public void propertyChange(String name, Object before, Object after) {
-        super.propertyChange(name, before, after);
+    public void propertyChange(String type, String name, Object before, Object after) {
+        super.propertyChange(type, name, before, after);
         if (!name.equals("Emit all particles")) {
             fireSave(true);
             firePropertyChange(name, before, after);

+ 1 - 1
jme3-core/src/com/jme3/gde/core/sceneexplorer/nodes/JmeSpatial.java

@@ -294,7 +294,7 @@ public class JmeSpatial extends AbstractSceneExplorerNode {
 
     @Override
     protected Sheet createSheet() {
-        Sheet sheet = Sheet.createDefault();
+        Sheet sheet = super.createSheet();        
 
         //TODO: multithreading.. but we only read
         Collection<String> dataKeys = spatial.getUserDataKeys();

+ 2 - 2
jme3-core/src/com/jme3/gde/core/sceneexplorer/nodes/JmeTrack.java

@@ -253,8 +253,8 @@ public class JmeTrack extends AbstractSceneExplorerNode {
     }
 
     @Override
-    public void propertyChange(String name, Object before, Object after) {
-        super.propertyChange(name, before, after);
+    public void propertyChange(String type, String name, Object before, Object after) {
+        super.propertyChange(type, name, before, after);
         setName();
     }
     

+ 1 - 1
jme3-core/src/com/jme3/gde/core/sceneexplorer/nodes/actions/UserDataDialog.form

@@ -1,4 +1,4 @@
-<?xml version="1.1" encoding="UTF-8" ?>
+<?xml version="1.0" encoding="UTF-8" ?>
 
 <Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
   <NonVisualComponents>

+ 3 - 8
jme3-core/src/com/jme3/gde/core/sceneexplorer/nodes/actions/UserDataDialog.java

@@ -37,11 +37,9 @@
  */
 package com.jme3.gde.core.sceneexplorer.nodes.actions;
 
-import com.jme3.effect.influencers.ParticleInfluencer;
 import com.jme3.export.Savable;
 import com.jme3.gde.core.assets.ProjectAssetManager;
 import com.jme3.gde.core.properties.SceneExplorerProperty;
-import com.jme3.gde.core.properties.ScenePropertyChangeListener;
 import com.jme3.gde.core.scene.SceneApplication;
 import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
 import com.jme3.gde.core.util.PropertyUtils;
@@ -65,7 +63,7 @@ import org.openide.util.Exceptions;
  *
  * @author normenhansen
  */
-public class UserDataDialog extends javax.swing.JDialog implements ScenePropertyChangeListener {
+public class UserDataDialog extends javax.swing.JDialog {
 
     JmeSpatial spat;
     boolean initialized = false;
@@ -134,15 +132,11 @@ public class UserDataDialog extends javax.swing.JDialog implements SceneProperty
 
     }
 
-    public void propertyChange(String property, Object oldValue, Object newValue) {
-        //System.out.println(property + " changed");
-    }
-
     protected Property makeProperty(Object obj, Class returntype, String method, String setter, String name) {
         Property prop = null;
         try {
 
-            prop = new SceneExplorerProperty(obj.getClass().cast(obj), returntype, method, setter, this);
+            prop = new SceneExplorerProperty(obj.getClass().cast(obj), returntype, method, setter);
 
             prop.setName(name);
 
@@ -408,4 +402,5 @@ private void jComboBox1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FI
     private javax.swing.JTextField jTextField1;
     private javax.swing.JTextField jTextField2;
     // End of variables declaration//GEN-END:variables
+
 }

+ 0 - 13
jme3-core/src/com/jme3/gde/core/util/PropertyUtils.java

@@ -35,7 +35,6 @@ import java.beans.IntrospectionException;
 import java.beans.PropertyDescriptor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
-import org.openide.util.Exceptions;
 
 /**
  *
@@ -45,18 +44,6 @@ public class PropertyUtils {
 
     public static PropertyDescriptor getPropertyDescriptor(Class c, Field field) {
         try {
-            try {
-                try {
-                    PropertyDescriptor p = (PropertyDescriptor)c.getClassLoader().loadClass("java.beans.PropertyDescriptor").newInstance();
-                    
-                } catch (InstantiationException ex) {
-                    Exceptions.printStackTrace(ex);
-                } catch (IllegalAccessException ex) {
-                    Exceptions.printStackTrace(ex);
-                }
-            } catch (ClassNotFoundException ex) {
-                Exceptions.printStackTrace(ex);
-            }
             PropertyDescriptor prop = new PropertyDescriptor(field.getName(), c);
 
             prop.setDisplayName(splitCamelCase(field.getName()));