Przeglądaj źródła

SDK:
- Add mother of all SDK features: Custom AppStates :O

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

nor..67 12 lat temu
rodzic
commit
60da1a661f

+ 66 - 0
jme3-core/src/com/jme3/gde/core/appstates/AppStateExplorerTopComponent.form

@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Component id="jToolBar1" alignment="0" pref="259" max="32767" attributes="0"/>
+          <Component id="jScrollPane1" alignment="0" max="32767" attributes="0"/>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="1" attributes="0">
+              <Component id="jToolBar1" min="-2" pref="25" max="-2" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="jScrollPane1" pref="193" max="32767" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Container class="javax.swing.JScrollPane" name="jScrollPane1">
+      <AuxValues>
+        <AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new BeanTreeView()"/>
+      </AuxValues>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+    </Container>
+    <Container class="javax.swing.JToolBar" name="jToolBar1">
+      <Properties>
+        <Property name="floatable" type="boolean" value="false"/>
+        <Property name="rollover" type="boolean" value="true"/>
+      </Properties>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JButton" name="jButton1">
+          <Properties>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="com/jme3/gde/core/appstates/Bundle.properties" key="AppStateExplorerTopComponent.jButton1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+            <Property name="enabled" type="boolean" value="false"/>
+            <Property name="focusable" type="boolean" value="false"/>
+            <Property name="horizontalTextPosition" type="int" value="0"/>
+            <Property name="verticalTextPosition" type="int" value="3"/>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton1ActionPerformed"/>
+          </Events>
+        </Component>
+      </SubComponents>
+    </Container>
+  </SubComponents>
+</Form>

+ 200 - 0
jme3-core/src/com/jme3/gde/core/appstates/AppStateExplorerTopComponent.java

@@ -0,0 +1,200 @@
+/*
+ * 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.appstates;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.gde.core.assets.ProjectAssetManager;
+import com.jme3.gde.core.scene.PreviewRequest;
+import com.jme3.gde.core.scene.SceneApplication;
+import com.jme3.gde.core.scene.SceneListener;
+import com.jme3.gde.core.scene.SceneRequest;
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Spatial;
+import javax.swing.ActionMap;
+import org.netbeans.api.settings.ConvertAsProperties;
+import org.openide.awt.ActionID;
+import org.openide.awt.ActionReference;
+import org.openide.explorer.ExplorerManager;
+import org.openide.explorer.ExplorerUtils;
+import org.openide.explorer.view.BeanTreeView;
+import org.openide.nodes.Node;
+import org.openide.windows.TopComponent;
+import org.openide.util.NbBundle.Messages;
+
+/**
+ * TODO: preliminary implementation 'til new scene system
+ */
+@ConvertAsProperties(
+    dtd = "-//com.jme3.gde.core.appstates//AppStateExplorer//EN",
+autostore = false)
[email protected](
+    preferredID = "AppStateExplorerTopComponent",
+//iconBase="SET/PATH/TO/ICON/HERE", 
+persistenceType = TopComponent.PERSISTENCE_NEVER)
[email protected](mode = "navigator", openAtStartup = true)
+@ActionID(category = "Window", id = "com.jme3.gde.core.appstates.AppStateExplorerTopComponent")
+@ActionReference(path = "Menu/Window" /*, position = 333 */)
[email protected](
+    displayName = "#CTL_AppStateExplorerAction",
+preferredID = "AppStateExplorerTopComponent")
+@Messages({
+    "CTL_AppStateExplorerAction=AppStateExplorer",
+    "CTL_AppStateExplorerTopComponent=AppStateExplorer Window",
+    "HINT_AppStateExplorerTopComponent=This is a AppStateExplorer window"
+})
+public final class AppStateExplorerTopComponent extends TopComponent implements ExplorerManager.Provider {
+
+    private transient ExplorerManager explorerManager = new ExplorerManager();
+    private FakeApplication fakeApp;
+    private ProjectAssetManager mgr;
+    private SceneListener listener = new SceneListener() {
+        public void sceneOpened(SceneRequest request) {
+            Spatial rootNode = request.getRootNode();
+            if (!(rootNode instanceof com.jme3.scene.Node)) {
+                return;
+            }
+            mgr = request.getManager();
+            AssetManager assetManager = request.getManager();
+            Camera cam = SceneApplication.getApplication().getCamera();
+            com.jme3.scene.Node guiNode = SceneApplication.getApplication().getGuiNode();
+            fakeApp = new FakeApplication((com.jme3.scene.Node) rootNode, guiNode, assetManager, cam);
+            //TODO: ermagherd, hackish
+            SceneApplication.getApplication().setFakeApp(fakeApp);
+            final AppStateManagerNode nod = new AppStateManagerNode(fakeApp.getStateManager());
+            jButton1.setEnabled(true);
+            explorerManager.setRootContext(nod);
+            setActivatedNodes(new Node[]{nod});
+        }
+
+        public void sceneClosed(SceneRequest request) {
+            SceneApplication.getApplication().setFakeApp(null);
+            mgr = null;
+            fakeApp = null;
+            jButton1.setEnabled(false);
+            explorerManager.setRootContext(Node.EMPTY);
+            setActivatedNodes(new Node[]{Node.EMPTY});
+        }
+
+        public void previewCreated(PreviewRequest request) {
+        }
+    };
+
+    public AppStateExplorerTopComponent() {
+        initComponents();
+        setName(Bundle.CTL_AppStateExplorerTopComponent());
+        setToolTipText(Bundle.HINT_AppStateExplorerTopComponent());
+        ActionMap map = getActionMap();
+        map.put("delete", ExplorerUtils.actionDelete(explorerManager, true));
+//        map.put("moveup", new MoveUpAction());
+//        map.put("movedown", new MoveDownAction());
+        associateLookup(ExplorerUtils.createLookup(explorerManager, map));
+        SceneApplication.getApplication().addSceneListener(listener);
+    }
+
+    /**
+     * This method is called from within the constructor to initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is always
+     * regenerated by the Form Editor.
+     */
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        jScrollPane1 = new BeanTreeView();
+        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(AppStateExplorerTopComponent.class, "AppStateExplorerTopComponent.jButton1.text")); // NOI18N
+        jButton1.setEnabled(false);
+        jButton1.setFocusable(false);
+        jButton1.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+        jButton1.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
+        jButton1.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                jButton1ActionPerformed(evt);
+            }
+        });
+        jToolBar1.add(jButton1);
+
+        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+        this.setLayout(layout);
+        layout.setHorizontalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addComponent(jToolBar1, javax.swing.GroupLayout.DEFAULT_SIZE, 259, Short.MAX_VALUE)
+            .addComponent(jScrollPane1)
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+                .addComponent(jToolBar1, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 193, Short.MAX_VALUE))
+        );
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
+        ProjectAssetManager projectAssetManager = mgr;
+        FakeApplication fakeApp = this.fakeApp;
+        if (fakeApp != null && mgr != null) {
+            new NewAppStateWizardAction(projectAssetManager, fakeApp).showWizard();
+        }
+    }//GEN-LAST:event_jButton1ActionPerformed
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JButton jButton1;
+    private javax.swing.JScrollPane jScrollPane1;
+    private javax.swing.JToolBar jToolBar1;
+    // End of variables declaration//GEN-END:variables
+
+    @Override
+    public void componentOpened() {
+    }
+
+    @Override
+    public void componentClosed() {
+    }
+
+    void writeProperties(java.util.Properties p) {
+        // better to version settings since initial version as advocated at
+        // http://wiki.apidesign.org/wiki/PropertyFiles
+        p.setProperty("version", "1.0");
+    }
+
+    void readProperties(java.util.Properties p) {
+        String version = p.getProperty("version");
+    }
+
+    public ExplorerManager getExplorerManager() {
+        return explorerManager;
+    }
+}

+ 109 - 0
jme3-core/src/com/jme3/gde/core/appstates/AppStateManagerNode.java

@@ -0,0 +1,109 @@
+/*
+ * 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.appstates;
+
+import com.jme3.app.state.AppState;
+import com.jme3.gde.core.appstates.FakeApplication.FakeAppStateManager;
+import java.util.LinkedList;
+import java.util.List;
+import org.openide.nodes.AbstractNode;
+import org.openide.nodes.Children;
+import org.openide.nodes.Node;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class AppStateManagerNode extends AbstractNode {
+
+//    private static Image smallImage =
+//            ImageUtilities.loadImage("com/jme3/gde/core/filters/icons/eye.gif");
+
+    public AppStateManagerNode(FakeAppStateManager manager) {
+        super(new AppStateChildren(manager));
+        setName("Loaded App States");
+        manager.setNode(this);
+    }
+
+//    @Override
+//    public Image getIcon(int type) {
+//        return smallImage;
+//    }
+//
+//    @Override
+//    public Image getOpenedIcon(int type) {
+//        return smallImage;
+//    }
+
+    public void refresh() {
+        //TODO: external refresh
+        ((AppStateChildren) getChildren()).doRefresh();
+    }
+
+    public static class AppStateChildren extends Children.Keys<Object> {
+
+        private final FakeAppStateManager manager;
+
+        public AppStateChildren(FakeAppStateManager manager) {
+            this.manager = manager;
+        }
+
+        @Override
+        protected void addNotify() {
+            super.addNotify();
+            setKeys(createKeys());
+        }
+
+        protected void doRefresh() {
+            addNotify();
+            refresh();
+        }
+
+        protected void reorderNotify() {
+            setKeys(createKeys());
+        }
+
+        protected List<Object> createKeys() {
+            List<Object> keys = new LinkedList<Object>();
+            List<AppState> states;
+            states = manager.getAddedStates();
+            keys.addAll(states);
+            return keys;
+        }
+
+        @Override
+        protected Node[] createNodes(Object t) {
+            AppStateNode node = new AppStateNode((AppState) t, manager);
+            return new Node[]{node};
+        }
+    }
+}

+ 191 - 0
jme3-core/src/com/jme3/gde/core/appstates/AppStateNode.java

@@ -0,0 +1,191 @@
+/*
+ * 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.appstates;
+
+import com.jme3.app.state.AppState;
+import com.jme3.app.state.AppStateManager;
+import com.jme3.gde.core.properties.SceneExplorerProperty;
+import com.jme3.gde.core.properties.ScenePropertyChangeListener;
+import com.jme3.gde.core.util.PropertyUtils;
+import java.beans.PropertyDescriptor;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import org.openide.DialogDisplayer;
+import org.openide.NotifyDescriptor;
+import org.openide.nodes.AbstractNode;
+import org.openide.nodes.Children;
+import org.openide.nodes.Node.Property;
+import org.openide.nodes.Sheet;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class AppStateNode extends AbstractNode implements ScenePropertyChangeListener {
+
+    protected AppState appState;
+    protected AppStateManager parent;
+//    private static Image icon;
+//    private static final String ICON_ENABLED = "com/jme3/gde/core/filters/icons/eye.gif";
+//    private static final String ICON_DISABLED = "com/jme3/gde/core/filters/icons/crossedEye.gif";
+
+    public AppStateNode(AppState appState, AppStateManager parent) {
+        super(Children.LEAF);
+        this.parent = parent;
+        this.appState = appState;
+        setName(appState.getClass().getName());
+        setDisplayName(appState.getClass().getSimpleName());
+//        icon = ImageUtilities.loadImage(ICON_ENABLED);
+//        setIconBaseWithExtension(ICON_ENABLED);
+    }
+
+//    @Override
+//    public Image getIcon(int type) {
+//        return icon;
+//
+//    }
+//
+//    @Override
+//    public Image getOpenedIcon(int type) {
+//        return icon;
+//    }
+//
+//    public void toggleIcon(boolean enabled) {
+//        if (enabled) {
+//            icon = ImageUtilities.loadImage(ICON_ENABLED);
+//
+//        } else {
+//            icon = ImageUtilities.loadImage(ICON_DISABLED);
+//
+//        }
+//        fireIconChange();
+//    }
+//    @Override
+//    public Action[] getActions(boolean context) {
+//        return new Action[]{
+//                    //Actions.alwaysEnabled(new EnableFiterAction(this), "Toggle enabled", "", false),
+//                    //                    SystemAction.get(MoveUpAction.class),
+//                    //                    SystemAction.get(MoveDownAction.class),
+//                    null, //                    SystemAction.get(DeleteAction.class),
+//                };
+//    }
+//
+//    @Override
+//    public Action getPreferredAction() {
+////        return Actions.alwaysEnabled(new EnableFiterAction(this), "Toggle enabled", "", false);
+//        return null;
+//    }
+    @Override
+    public boolean canDestroy() {
+        return true;
+    }
+
+    @Override
+    public void destroy() throws IOException {
+        super.destroy();
+        parent.detach(appState);
+        //TODO:hack
+        ((AppStateManagerNode) getParentNode()).refresh();
+    }
+
+    @Override
+    protected Sheet createSheet() {
+        Sheet sheet = super.createSheet();
+        Object obj = appState;
+        if (obj == null) {
+            DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message(
+                    "AppState unexpectedly null",
+                    NotifyDescriptor.WARNING_MESSAGE));
+            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());
+        createFields(appState.getClass(), set, obj);
+        sheet.put(set);
+
+        return sheet;
+    }
+
+    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) {
+            Exceptions.printStackTrace(ex);
+        }
+        return prop;
+    }
+
+    protected Property<?> makeProperty(Object obj, Class returntype, String method, String setter, String name) {
+        Property<?> prop = null;
+        try {
+            prop = new SceneExplorerProperty(appState.getClass().cast(obj), returntype, method, setter, this);
+            prop.setName(name);
+        } catch (NoSuchMethodException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+        return prop;
+    }
+
+    protected void createMethods(Class c, Sheet.Set set, Object obj) throws SecurityException {
+        for (Method method : c.getDeclaredMethods()) {
+            PropertyDescriptor prop = PropertyUtils.getPropertyDescriptor(c, method);
+            if (prop != null) {
+                set.put(makeProperty(obj, prop.getPropertyType(), prop.getReadMethod().getName(), prop.getWriteMethod().getName(), prop.getDisplayName()));
+            }
+        }
+    }
+
+    protected void createFields(Class c, Sheet.Set set, Object obj) throws SecurityException {
+        for (Field field : c.getDeclaredFields()) {
+            PropertyDescriptor prop = PropertyUtils.getPropertyDescriptor(c, field);
+            if (prop != null) {
+                set.put(makeProperty(obj, prop.getPropertyType(), prop.getReadMethod().getName(), prop.getWriteMethod().getName(), prop.getDisplayName()));
+            }
+        }
+    }
+
+    public void propertyChange(String property, Object oldValue, Object newValue) {
+    }
+}

+ 3 - 0
jme3-core/src/com/jme3/gde/core/appstates/Bundle.properties

@@ -0,0 +1,3 @@
+AppStateExplorerTopComponent.jButton1.text=attach..
+NewAppStateVisualPanel1.jLabel1.text=Select or enter AppState classname
+NewAppStateVisualPanel1.jTextField1.text=com.mycompany.MyAppState

+ 425 - 0
jme3-core/src/com/jme3/gde/core/appstates/FakeApplication.java

@@ -0,0 +1,425 @@
+/*
+ * 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.appstates;
+
+import com.jme3.app.Application;
+import com.jme3.app.SimpleApplication;
+import com.jme3.app.state.AppState;
+import com.jme3.app.state.AppStateManager;
+import com.jme3.asset.AssetManager;
+import com.jme3.audio.AudioRenderer;
+import com.jme3.audio.Listener;
+import com.jme3.input.FlyByCamera;
+import com.jme3.input.InputManager;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Node;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeContext;
+import com.jme3.system.JmeContext.Type;
+import com.jme3.system.Timer;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+import org.openide.DialogDisplayer;
+import org.openide.NotifyDescriptor;
+import org.openide.util.Exceptions;
+
+/**
+ * TODO: Temporary implementation before new scene system!
+ *
+ * @author normenhansen
+ */
+public class FakeApplication extends SimpleApplication {
+//    private Lookup lookup;
+
+    private Node rootNode;
+    private Node guiNode;
+    private AssetManager assetManager;
+    private Camera cam;
+    private FakeAppStateManager appStateManager;
+    private FakeRenderManager renderManager;
+
+    public FakeApplication(Node rootNode, Node guiNode, AssetManager assetManager, Camera cam) {
+        this.rootNode = rootNode;
+        this.guiNode = guiNode;
+        this.assetManager = assetManager;
+        this.cam = cam;
+        this.appStateManager = new FakeAppStateManager(this);
+    }
+
+    @Override
+    public void createCanvas() {
+        defaultFakeError(true);
+    }
+
+    @Override
+    public void destroy() {
+        defaultFakeError(true);
+    }
+
+    @Override
+    protected void destroyInput() {
+        defaultFakeError(true);
+    }
+
+    @Override
+    public <V> Future<V> enqueue(Callable<V> callable) {
+        return super.enqueue(callable);
+    }
+
+    @Override
+    public void gainFocus() {
+        defaultFakeError(true);
+    }
+
+    @Override
+    public AssetManager getAssetManager() {
+        return assetManager;
+    }
+
+    @Override
+    public AudioRenderer getAudioRenderer() {
+        defaultFakeError();
+        return null;
+    }
+
+    @Override
+    public Camera getCamera() {
+        return cam;
+    }
+
+    @Override
+    public JmeContext getContext() {
+        defaultFakeError();
+        return null;
+    }
+
+    @Override
+    public ViewPort getGuiViewPort() {
+        //TODO
+        defaultFakeError();
+        return null;
+    }
+
+    @Override
+    public InputManager getInputManager() {
+        defaultFakeError();
+        return null;
+    }
+
+    @Override
+    public Listener getListener() {
+        defaultFakeError();
+        return null;
+    }
+
+    @Override
+    public RenderManager getRenderManager() {
+        defaultFakeError();
+        return null;
+    }
+
+    @Override
+    public Renderer getRenderer() {
+        defaultFakeError();
+        return null;
+    }
+
+    @Override
+    public FakeAppStateManager getStateManager() {
+        return appStateManager;
+    }
+
+    @Override
+    public Timer getTimer() {
+        defaultFakeError();
+        return null;
+    }
+
+    @Override
+    public ViewPort getViewPort() {
+        //TODO
+        defaultFakeError();
+        return null;
+    }
+
+    @Override
+    public boolean isPauseOnLostFocus() {
+        return true;
+    }
+
+    @Override
+    public void loseFocus() {
+        defaultFakeError(true);
+    }
+
+    @Override
+    public void initialize() {
+        defaultFakeError(true);
+    }
+
+    @Override
+    public void handleError(String errMsg, Throwable t) {
+        defaultFakeError(true);
+    }
+
+    @Override
+    public void requestClose(boolean esc) {
+        defaultFakeError();
+    }
+
+    @Override
+    public void reshape(int w, int h) {
+        defaultFakeError();
+    }
+
+    @Override
+    public void restart() {
+        defaultFakeError();
+    }
+
+    @Override
+    public void setAssetManager(AssetManager assetManager) {
+        defaultFakeError(true);
+    }
+
+    @Override
+    public void setSettings(AppSettings settings) {
+        defaultFakeError();
+    }
+
+    @Override
+    public void setPauseOnLostFocus(boolean pauseOnLostFocus) {
+        defaultFakeError();
+    }
+
+    @Override
+    public void start() {
+        defaultFakeError();
+    }
+
+    @Override
+    public void start(Type contextType) {
+        defaultFakeError();
+    }
+
+    @Override
+    public void startCanvas() {
+        defaultFakeError();
+    }
+
+    @Override
+    public void startCanvas(boolean waitFor) {
+        defaultFakeError();
+    }
+
+    @Override
+    public void stop() {
+        defaultFakeError();
+    }
+
+    @Override
+    public void stop(boolean waitFor) {
+        defaultFakeError();
+    }
+
+    @Override
+    public void update() {
+        defaultFakeError(true);
+    }
+
+    /*
+     * SimpleApplication
+     */
+    @Override
+    public void simpleInitApp() {
+        defaultFakeError(true);
+    }
+
+    @Override
+    public FlyByCamera getFlyByCamera() {
+        defaultFakeError();
+        return null;
+    }
+
+    @Override
+    public Node getGuiNode() {
+        return guiNode;
+    }
+
+    @Override
+    public Node getRootNode() {
+        return rootNode;
+    }
+
+    @Override
+    public boolean isShowSettings() {
+        return true;
+    }
+
+    @Override
+    public void setDisplayFps(boolean show) {
+        defaultFakeError();
+    }
+
+    @Override
+    public void setDisplayStatView(boolean show) {
+        defaultFakeError();
+    }
+
+    @Override
+    public void setTimer(Timer timer) {
+        defaultFakeError();
+    }
+
+    @Override
+    public void setShowSettings(boolean showSettings) {
+        defaultFakeError();
+    }
+
+    @Override
+    public void simpleRender(RenderManager rm) {
+        defaultFakeError();
+    }
+
+    @Override
+    public void simpleUpdate(float tpf) {
+        defaultFakeError();
+    }
+
+    private class FakeRenderManager extends RenderManager {
+
+        public FakeRenderManager(Renderer renderer) {
+            super(null);
+        }
+        //TODO: also nice messages
+    }
+
+    /*
+     * Internal
+     */
+    private void defaultFakeError() {
+        defaultFakeError(false);
+    }
+
+    private void defaultFakeError(boolean severe) {
+        ByteArrayOutputStream str = new ByteArrayOutputStream();
+        new Throwable().printStackTrace(new PrintStream(new BufferedOutputStream(str)));
+        String trace = "No stack trace available.";
+        try {
+            trace = str.toString("UTF-8");
+        } catch (UnsupportedEncodingException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+        int idx = trace.indexOf("com.jme3.gde");
+        while (idx != -1) {
+            if (idx < 50) {
+                continue;
+            } else {
+                trace = trace.substring(0, idx);
+                break;
+            }
+        }
+        showError("Fake app is fake!" + (severe ? "And WTF are you trying to do anyway?\n" : "\n") + trace);
+    }
+
+    private void showError(String msg) {
+        DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message(
+                msg,
+                NotifyDescriptor.WARNING_MESSAGE));
+    }
+
+    public void updateFake(float tpf) {
+//        System.out.println("UPDATE");
+        appStateManager.update(tpf);
+    }
+
+    public void renderFake() {
+        appStateManager.render(renderManager);
+    }
+
+    public static class FakeAppStateManager extends AppStateManager {
+
+        private AppStateManagerNode node;
+        ArrayList<AppState> states = new ArrayList<AppState>();
+
+        public FakeAppStateManager(Application app) {
+            super(app);
+        }
+
+        public List<AppState> getAddedStates() {
+            return states;
+        }
+
+        @Override
+        public boolean attach(AppState state) {
+            boolean ret = super.attach(state);
+            try {
+                states.add(state);
+            } catch (Exception e) {
+                Exceptions.printStackTrace(e);
+            }
+//            DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message(
+//                    "attach state",
+//                    NotifyDescriptor.WARNING_MESSAGE));
+            if (node != null) {
+//                DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message(
+//                        "refresh node",
+//                        NotifyDescriptor.WARNING_MESSAGE));
+                node.refresh();
+            }
+            return ret;
+        }
+
+        @Override
+        public boolean detach(AppState state) {
+            try {
+                states.remove(state);
+            } catch (Exception e) {
+                Exceptions.printStackTrace(e);
+            }
+            return super.detach(state);
+        }
+
+        public void setNode(AppStateManagerNode node) {
+            this.node = node;
+        }
+    }
+}

+ 85 - 0
jme3-core/src/com/jme3/gde/core/appstates/NewAppStateVisualPanel1.form

@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Component id="jScrollPane1" alignment="1" pref="351" max="32767" attributes="0"/>
+                  <Component id="jTextField1" max="32767" attributes="0"/>
+                  <Component id="jLabel1" alignment="0" max="32767" attributes="0"/>
+              </Group>
+              <EmptySpace max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="1" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="jScrollPane1" pref="172" max="32767" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="jLabel1" min="-2" max="-2" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="jTextField1" min="-2" max="-2" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Container class="javax.swing.JScrollPane" name="jScrollPane1">
+      <AuxValues>
+        <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+      </AuxValues>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JList" name="jList1">
+          <Properties>
+            <Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
+              <StringArray count="5">
+                <StringItem index="0" value="Item 1"/>
+                <StringItem index="1" value="Item 2"/>
+                <StringItem index="2" value="Item 3"/>
+                <StringItem index="3" value="Item 4"/>
+                <StringItem index="4" value="Item 5"/>
+              </StringArray>
+            </Property>
+          </Properties>
+          <Events>
+            <EventHandler event="valueChanged" listener="javax.swing.event.ListSelectionListener" parameters="javax.swing.event.ListSelectionEvent" handler="updateClassName"/>
+          </Events>
+        </Component>
+      </SubComponents>
+    </Container>
+    <Component class="javax.swing.JLabel" name="jLabel1">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/core/appstates/Bundle.properties" key="NewAppStateVisualPanel1.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JTextField" name="jTextField1">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/core/appstates/Bundle.properties" key="NewAppStateVisualPanel1.jTextField1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+  </SubComponents>
+</Form>

+ 209 - 0
jme3-core/src/com/jme3/gde/core/appstates/NewAppStateVisualPanel1.java

@@ -0,0 +1,209 @@
+/*
+ *  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.appstates;
+
+import com.jme3.gde.core.sceneexplorer.nodes.actions.impl.*;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+import javax.swing.JPanel;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.project.JavaProjectConstants;
+import org.netbeans.api.java.source.ClassIndex;
+import org.netbeans.api.java.source.ClassIndex.NameKind;
+import org.netbeans.api.java.source.ClassIndex.SearchScope;
+import org.netbeans.api.java.source.ClasspathInfo;
+import org.netbeans.api.java.source.CompilationController;
+import org.netbeans.api.java.source.ElementHandle;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.JavaSource.Phase;
+import org.netbeans.api.java.source.Task;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.SourceGroup;
+import org.netbeans.api.project.Sources;
+import org.openide.util.Exceptions;
+
+public final class NewAppStateVisualPanel1 extends JPanel {
+
+    private Project proj;
+
+    /**
+     * Creates new form NewCustomControlVisualPanel1
+     */
+    public NewAppStateVisualPanel1() {
+        initComponents();
+    }
+
+    @Override
+    public String getName() {
+        return "Select AppState";
+    }
+
+    public String getClassName() {
+        return jTextField1.getText();
+    }
+
+    private void scanControls() {
+        List<String> sources = getSources();
+        jList1.setListData(sources.toArray());
+    }
+
+    private List<String> getSources() {
+        Sources sources = proj.getLookup().lookup(Sources.class);
+        final List<String> list = new LinkedList<String>();
+        if (sources != null) {
+            SourceGroup[] groups = sources.getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
+            if (groups != null) {
+                for (SourceGroup sourceGroup : groups) {
+                    ClasspathInfo cpInfo = ClasspathInfo.create(ClassPath.getClassPath(sourceGroup.getRootFolder(), ClassPath.BOOT),
+                            ClassPath.getClassPath(sourceGroup.getRootFolder(), ClassPath.COMPILE),
+                            ClassPath.getClassPath(sourceGroup.getRootFolder(), ClassPath.SOURCE));
+
+                    HashSet<SearchScope> set = new HashSet<SearchScope>();
+                    set.add(ClassIndex.SearchScope.SOURCE);
+                    Set<ElementHandle<TypeElement>> types = cpInfo.getClassIndex().getDeclaredTypes("", NameKind.PREFIX, set);
+                    for (Iterator<ElementHandle<TypeElement>> it = types.iterator(); it.hasNext();) {
+                        final ElementHandle<TypeElement> elementHandle = it.next();
+                        JavaSource js = JavaSource.create(cpInfo);
+                        try {
+                            js.runUserActionTask(new Task<CompilationController>() {
+                                public void run(CompilationController control)
+                                        throws Exception {
+                                    control.toPhase(Phase.RESOLVED);
+                                    //TODO: check with proper casting check.. gotta get TypeMirror of Control interface..
+//                                    TypeUtilities util = control.getTypeUtilities();//.isCastable(Types., null)
+//                                    util.isCastable(null, null);
+                                    TypeElement elem = elementHandle.resolve(control);
+                                    if (elem != null) {
+                                        List<? extends TypeMirror> interfaces = elem.getInterfaces();
+                                        for (TypeMirror typeMirror : interfaces) {
+                                            String interfaceName = typeMirror.toString();
+                                            if ("com.jme3.app.state.AppState".equals(interfaceName)) {
+                                                list.add(elem.getQualifiedName().toString());
+                                            }
+                                        }
+                                        TypeMirror superClass = elem.getSuperclass();
+                                        String superClassName = superClass.toString();
+                                        if ("com.jme3.app.state.AbstractAppState".equals(superClassName)) {
+                                            list.add(elem.getQualifiedName().toString());
+                                        }
+                                    }
+                                }
+                            }, false);
+                        } catch (Exception ioe) {
+                            Exceptions.printStackTrace(ioe);
+                        }
+                    }
+
+                }
+            }
+        }
+        return list;
+    }
+
+    public void load(Project proj) {
+        this.proj = proj;
+        scanControls();
+    }
+
+    /**
+     * This method is called from within the constructor to initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is always
+     * regenerated by the Form Editor.
+     */
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        jScrollPane1 = new javax.swing.JScrollPane();
+        jList1 = new javax.swing.JList();
+        jLabel1 = new javax.swing.JLabel();
+        jTextField1 = new javax.swing.JTextField();
+
+        jList1.setModel(new javax.swing.AbstractListModel() {
+            String[] strings = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" };
+            public int getSize() { return strings.length; }
+            public Object getElementAt(int i) { return strings[i]; }
+        });
+        jList1.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
+            public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
+                updateClassName(evt);
+            }
+        });
+        jScrollPane1.setViewportView(jList1);
+
+        org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(NewAppStateVisualPanel1.class, "NewAppStateVisualPanel1.jLabel1.text")); // NOI18N
+
+        jTextField1.setText(org.openide.util.NbBundle.getMessage(NewAppStateVisualPanel1.class, "NewAppStateVisualPanel1.jTextField1.text")); // NOI18N
+
+        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+        this.setLayout(layout);
+        layout.setHorizontalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 351, Short.MAX_VALUE)
+                    .addComponent(jTextField1)
+                    .addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+                .addContainerGap())
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+                .addContainerGap()
+                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 172, Short.MAX_VALUE)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(jLabel1)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addContainerGap())
+        );
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void updateClassName(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_updateClassName
+        Object obj = jList1.getSelectedValue();
+        if (obj != null) {
+            jTextField1.setText(obj + "");
+        }
+    }//GEN-LAST:event_updateClassName
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JLabel jLabel1;
+    private javax.swing.JList jList1;
+    private javax.swing.JScrollPane jScrollPane1;
+    private javax.swing.JTextField jTextField1;
+    // End of variables declaration//GEN-END:variables
+}

+ 168 - 0
jme3-core/src/com/jme3/gde/core/appstates/NewAppStateWizardAction.java

@@ -0,0 +1,168 @@
+/*
+ *  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.appstates;
+
+import com.jme3.app.Application;
+import com.jme3.app.state.AbstractAppState;
+import com.jme3.app.state.AppState;
+import com.jme3.gde.core.assets.ProjectAssetManager;
+import java.awt.Component;
+import java.awt.Dialog;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.text.MessageFormat;
+import java.util.List;
+import javax.swing.JComponent;
+import org.openide.DialogDisplayer;
+import org.openide.NotifyDescriptor;
+import org.openide.WizardDescriptor;
+import org.openide.util.Exceptions;
+
+// An example action demonstrating how the wizard could be called from within
+// your code. You can move the code below wherever you need, or register an action:
+// @ActionID(category="...", id="com.jme3.gde.core.sceneexplorer.nodes.actions.impl.NewCustomControlWizardAction")
+// @ActionRegistration(displayName="Open NewCustomControl Wizard")
+// @ActionReference(path="Menu/Tools", position=...)
+public final class NewAppStateWizardAction implements ActionListener {
+
+    private WizardDescriptor.Panel[] panels;
+    ProjectAssetManager mgr;
+    Application fakeApp;
+
+    public NewAppStateWizardAction(ProjectAssetManager mgr, Application fakeApp) {
+        this.mgr = mgr;
+        this.fakeApp = fakeApp;
+    }
+
+    public void actionPerformed(ActionEvent e) {
+        //TODO: actual action to open/close via keyboard etc?
+    }
+
+    protected void showWizard() {
+        WizardDescriptor wizardDescriptor = new WizardDescriptor(getPanels());
+        // {0} will be replaced by WizardDesriptor.Panel.getComponent().getName()
+        wizardDescriptor.setTitleFormat(new MessageFormat("{0}"));
+        wizardDescriptor.setTitle("Add New AppState");
+        wizardDescriptor.putProperty("asset_manager", mgr);
+        wizardDescriptor.putProperty("fake_app", fakeApp);
+        wizardDescriptor.putProperty("project", mgr.getProject());
+        Dialog dialog = DialogDisplayer.getDefault().createDialog(wizardDescriptor);
+        dialog.setVisible(true);
+        dialog.toFront();
+        boolean cancelled = wizardDescriptor.getValue() != WizardDescriptor.FINISH_OPTION;
+        if (!cancelled) {
+            AppState state = doCreateAppState(wizardDescriptor);
+            if (state != null) {
+                fakeApp.getStateManager().attach(state);
+            }
+        }
+    }
+
+    protected AppState doCreateAppState(WizardDescriptor configuration) {
+        if (configuration == null) {
+            return null;
+        }
+        WizardDescriptor wizardDescriptor = (WizardDescriptor) configuration;
+        ProjectAssetManager manager = (ProjectAssetManager) wizardDescriptor.getProperty("asset_manager");
+        List<ClassLoader> loaders = manager.getClassLoaders();
+
+        String className = (String) wizardDescriptor.getProperty("class_name");
+        Class clazz = null;
+        try {
+            clazz = getClass().getClassLoader().loadClass(className);
+        } catch (ClassNotFoundException ex) {
+        }
+        for (ClassLoader classLoader : loaders) {
+            if (clazz == null) {
+                try {
+                    clazz = classLoader.loadClass(className);
+                } catch (ClassNotFoundException ex) {
+                }
+            }
+        }
+        if (clazz != null) {
+            try {
+                Object contr = clazz.newInstance();
+                //TODO: remove sillyness-test
+                if (contr instanceof AppState || contr instanceof AbstractAppState) {
+                    return (AppState) contr;
+                } else {
+                    DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message("This is no AppState class!"));
+                }
+            } catch (InstantiationException ex) {
+                Exceptions.printStackTrace(ex);
+                DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message("Error instatiating class!"));
+            } catch (IllegalAccessException ex) {
+                Exceptions.printStackTrace(ex);
+                DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message("Error instatiating class!"));
+            }
+        } else {
+            DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message("Cannot find class: " + className + "\nMake sure the name is correct and the project is compiled,\nbest enable 'Save on Compile' in the project preferences."));
+        }
+        return null;
+    }
+
+    /**
+     * Initialize panels representing individual wizard's steps and sets various
+     * properties for them influencing wizard appearance.
+     */
+    private WizardDescriptor.Panel[] getPanels() {
+        if (panels == null) {
+            panels = new WizardDescriptor.Panel[]{
+                new NewAppStateWizardPanel1()
+            };
+            String[] steps = new String[panels.length];
+            for (int i = 0; i < panels.length; i++) {
+                Component c = panels[i].getComponent();
+                // Default step name to component name of panel. Mainly useful
+                // for getting the name of the target chooser to appear in the
+                // list of steps.
+                steps[i] = c.getName();
+                if (c instanceof JComponent) { // assume Swing components
+                    JComponent jc = (JComponent) c;
+                    // Sets step number of a component
+                    // TODO if using org.openide.dialogs >= 7.8, can use WizardDescriptor.PROP_*:
+                    jc.putClientProperty("WizardPanel_contentSelectedIndex", new Integer(i));
+                    // Sets steps names for a panel
+                    jc.putClientProperty("WizardPanel_contentData", steps);
+                    // Turn on subtitle creation on each step
+                    jc.putClientProperty("WizardPanel_autoWizardStyle", Boolean.TRUE);
+                    // Show steps on the left side with the image on the background
+                    jc.putClientProperty("WizardPanel_contentDisplayed", Boolean.TRUE);
+                    // Turn on numbering of all steps
+                    jc.putClientProperty("WizardPanel_contentNumbered", Boolean.TRUE);
+                }
+            }
+        }
+        return panels;
+    }
+}

+ 117 - 0
jme3-core/src/com/jme3/gde/core/appstates/NewAppStateWizardPanel1.java

@@ -0,0 +1,117 @@
+/*
+ *  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.appstates;
+
+import com.jme3.gde.core.sceneexplorer.nodes.actions.impl.*;
+import java.awt.Component;
+import javax.swing.event.ChangeListener;
+import org.netbeans.api.project.Project;
+import org.openide.WizardDescriptor;
+import org.openide.util.HelpCtx;
+
+public class NewAppStateWizardPanel1 implements WizardDescriptor.Panel {
+
+    /**
+     * The visual component that displays this panel. If you need to access the
+     * component from this class, just use getComponent().
+     */
+    private Component component;
+
+    // Get the visual component for the panel. In this template, the component
+    // is kept separate. This can be more efficient: if the wizard is created
+    // but never displayed, or not all panels are displayed, it is better to
+    // create only those which really need to be visible.
+    public Component getComponent() {
+        if (component == null) {
+            component = new NewAppStateVisualPanel1();
+        }
+        return component;
+    }
+
+    public HelpCtx getHelp() {
+        // Show no Help button for this panel:
+        return HelpCtx.DEFAULT_HELP;
+        // If you have context help:
+        // return new HelpCtx(SampleWizardPanel1.class);
+    }
+
+    public boolean isValid() {
+        // If it is always OK to press Next or Finish, then:
+        return true;
+        // If it depends on some condition (form filled out...), then:
+        // return someCondition();
+        // and when this condition changes (last form field filled in...) then:
+        // fireChangeEvent();
+        // and uncomment the complicated stuff below.
+    }
+
+    public final void addChangeListener(ChangeListener l) {
+    }
+
+    public final void removeChangeListener(ChangeListener l) {
+    }
+    /*
+    private final Set<ChangeListener> listeners = new HashSet<ChangeListener>(1); // or can use ChangeSupport in NB 6.0
+    public final void addChangeListener(ChangeListener l) {
+    synchronized (listeners) {
+    listeners.add(l);
+    }
+    }
+    public final void removeChangeListener(ChangeListener l) {
+    synchronized (listeners) {
+    listeners.remove(l);
+    }
+    }
+    protected final void fireChangeEvent() {
+    Iterator<ChangeListener> it;
+    synchronized (listeners) {
+    it = new HashSet<ChangeListener>(listeners).iterator();
+    }
+    ChangeEvent ev = new ChangeEvent(this);
+    while (it.hasNext()) {
+    it.next().stateChanged(ev);
+    }
+    }
+     */
+
+    // You can use a settings object to keep track of state. Normally the
+    // settings object will be the WizardDescriptor, so you can use
+    // WizardDescriptor.getProperty & putProperty to store information entered
+    // by the user.
+    public void readSettings(Object settings) {
+        ((NewAppStateVisualPanel1) component).load((Project)((WizardDescriptor) settings).getProperty("project"));
+    }
+
+    public void storeSettings(Object settings) {
+        ((WizardDescriptor) settings).putProperty("class_name", ((NewAppStateVisualPanel1) component).getClassName());
+    }
+}

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

@@ -24,6 +24,7 @@
  */
 package com.jme3.gde.core.scene;
 
+import com.jme3.gde.core.appstates.FakeApplication;
 import com.jme3.app.Application;
 import com.jme3.app.StatsView;
 import com.jme3.bullet.BulletAppState;
@@ -116,6 +117,7 @@ public class SceneApplication extends Application implements LookupProvider {
     boolean useCanvas = false;
     private BulletAppState physicsState;
     private Thread thread;
+    private FakeApplication fakeApp;
 
     public SceneApplication() {
         progressHandle.start(7);
@@ -265,6 +267,7 @@ public class SceneApplication extends Application implements LookupProvider {
         }
         try {
             super.update();
+            FakeApplication fakap=fakeApp;
             float tpf = timer.getTimePerFrame();
             camLight.setPosition(cam.getLocation());
             secondCounter += tpf;
@@ -274,12 +277,18 @@ public class SceneApplication extends Application implements LookupProvider {
                 secondCounter = 0.0f;
             }
             getStateManager().update(tpf);
+            if(fakap!=null){
+                fakap.updateFake(tpf);
+            }
             rootNode.updateLogicalState(tpf);
             guiNode.updateLogicalState(tpf);
             toolsNode.updateLogicalState(tpf);
             rootNode.updateGeometricState();
             guiNode.updateGeometricState();
             toolsNode.updateGeometricState();
+            if(fakap!=null){
+                fakap.renderFake();
+            }
             getStateManager().render(renderManager);
             renderManager.render(tpf, context.isRenderable());
             getStateManager().postRender();
@@ -619,4 +628,12 @@ public class SceneApplication extends Application implements LookupProvider {
     public boolean isAwt() {
         return java.awt.EventQueue.isDispatchThread();
     }
+
+    public FakeApplication getFakeApp() {
+        return fakeApp;
+    }
+
+    public void setFakeApp(FakeApplication fakeApp) {
+        this.fakeApp = fakeApp;
+    }
 }

+ 37 - 2
jme3-core/src/com/jme3/gde/core/util/PropertyUtils.java

@@ -34,6 +34,8 @@ package com.jme3.gde.core.util;
 import java.beans.IntrospectionException;
 import java.beans.PropertyDescriptor;
 import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import org.openide.util.Exceptions;
 
 /**
  *
@@ -43,8 +45,18 @@ 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()));
@@ -56,6 +68,29 @@ public class PropertyUtils {
         }
     }
 
+    public static PropertyDescriptor getPropertyDescriptor(Class c, Method meth) {
+        try {
+
+            String name = meth.getName();
+            if (name.startsWith("get") || name.startsWith("set")) {
+                name = name.substring(3);
+                if (name.length() > 0) {
+                    name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
+                }
+            } else {
+                return null;
+            }
+            PropertyDescriptor prop = new PropertyDescriptor(name, c);
+
+            prop.setDisplayName(splitCamelCase(meth.getName()));
+
+            return prop;
+        } catch (IntrospectionException ex) {
+            //System.out.println(ex.getMessage());
+            return null;
+        }
+    }
+
     static String splitCamelCase(String s) {
         s = capitalizeString(s);
         return s.replaceAll(