2
0
Эх сурвалжийг харах

Merge pull request #273 from Dokthar/scenecomposer/master

SDK SceneComposer : new ShortcutTool
normen 10 жил өмнө
parent
commit
f6a2452ad0

+ 45 - 8
sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/SceneComposerToolController.java

@@ -11,6 +11,7 @@ import com.jme3.gde.core.scene.controller.SceneToolController;
 import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
 import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
 import com.jme3.gde.scenecomposer.tools.PickManager;
+import com.jme3.gde.scenecomposer.tools.shortcuts.ShortcutManager;
 import com.jme3.input.event.KeyInputEvent;
 import com.jme3.light.Light;
 import com.jme3.light.PointLight;
@@ -31,6 +32,7 @@ import com.jme3.scene.control.Control;
 import com.jme3.scene.shape.Quad;
 import com.jme3.texture.Texture;
 import java.util.concurrent.Callable;
+import org.openide.util.Lookup;
 
 /**
  *
@@ -184,7 +186,12 @@ public class SceneComposerToolController extends SceneToolController {
      * @param camera
      */
     public void doEditToolActivatedPrimary(Vector2f mouseLoc, boolean pressed, Camera camera) {
-        if (editTool != null) {
+        ShortcutManager scm = Lookup.getDefault().lookup(ShortcutManager.class);
+        
+        if (scm.isActive()) {
+            scm.getActiveShortcut().setCamera(camera);
+            scm.getActiveShortcut().actionPrimary(mouseLoc, pressed, rootNode, editorController.getCurrentDataObject());
+        } else if (editTool != null) {
             editTool.setCamera(camera);
             editTool.actionPrimary(mouseLoc, pressed, rootNode, editorController.getCurrentDataObject());
         }
@@ -198,36 +205,66 @@ public class SceneComposerToolController extends SceneToolController {
      * @param camera
      */
     public void doEditToolActivatedSecondary(Vector2f mouseLoc, boolean pressed, Camera camera) {
-        if (editTool != null) {
+        ShortcutManager scm = Lookup.getDefault().lookup(ShortcutManager.class);
+        
+        if (scm.isActive()) {
+            scm.getActiveShortcut().setCamera(camera);
+            scm.getActiveShortcut().actionSecondary(mouseLoc, pressed, rootNode, editorController.getCurrentDataObject());
+        } else if (editTool != null) {
             editTool.setCamera(camera);
             editTool.actionSecondary(mouseLoc, pressed, rootNode, editorController.getCurrentDataObject());
         }
     }
 
     public void doEditToolMoved(Vector2f mouseLoc, Camera camera) {
-        if (editTool != null) {
+        ShortcutManager scm = Lookup.getDefault().lookup(ShortcutManager.class);
+        
+        if (scm.isActive()) {
+            scm.getActiveShortcut().setCamera(camera);
+            scm.getActiveShortcut().mouseMoved(mouseLoc, rootNode, editorController.getCurrentDataObject(), selectedSpatial);
+        } else if (editTool != null) {
             editTool.setCamera(camera);
             editTool.mouseMoved(mouseLoc, rootNode, editorController.getCurrentDataObject(), selectedSpatial);
         }
     }
 
     public void doEditToolDraggedPrimary(Vector2f mouseLoc, boolean pressed, Camera camera) {
-        if (editTool != null) {
+        ShortcutManager scm = Lookup.getDefault().lookup(ShortcutManager.class);
+        
+        if (scm.isActive()) {
+            scm.getActiveShortcut().setCamera(camera);
+            scm.getActiveShortcut().draggedPrimary(mouseLoc, pressed, rootNode, editorController.getCurrentDataObject());
+        } else if (editTool != null) {
             editTool.setCamera(camera);
             editTool.draggedPrimary(mouseLoc, pressed, rootNode, editorController.getCurrentDataObject());
         }
     }
 
     public void doEditToolDraggedSecondary(Vector2f mouseLoc, boolean pressed, Camera camera) {
-        if (editTool != null) {
+        ShortcutManager scm = Lookup.getDefault().lookup(ShortcutManager.class);
+        
+        if (scm.isActive()) {
+            scm.getActiveShortcut().setCamera(null);
+            scm.getActiveShortcut().draggedSecondary(mouseLoc, pressed, rootNode, editorController.getCurrentDataObject());
+        } else if (editTool != null) {
             editTool.setCamera(camera);
             editTool.draggedSecondary(mouseLoc, pressed, rootNode, editorController.getCurrentDataObject());
         }
     }
     
-    void doKeyPressed(KeyInputEvent kie) {
-        if (editTool != null) {
-            editTool.keyPressed(kie);
+    public void doKeyPressed(KeyInputEvent kie) {
+        ShortcutManager scm = Lookup.getDefault().lookup(ShortcutManager.class);
+
+        if (scm.isActive()) {
+            scm.doKeyPressed(kie);
+        } else {
+            if (scm.activateShortcut(kie)) {
+                scm.getActiveShortcut().activate(manager, toolsNode, onTopToolsNode, selected, this);
+            } else {
+                if (editTool != null) {
+                    editTool.keyPressed(kie);
+                }
+            }
         }
     }
     

+ 0 - 204
sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/MoveManager.java

@@ -1,204 +0,0 @@
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package com.jme3.gde.scenecomposer.tools;
-
-import com.jme3.bullet.control.CharacterControl;
-import com.jme3.bullet.control.RigidBodyControl;
-import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
-import com.jme3.gde.scenecomposer.SceneEditTool;
-import com.jme3.math.FastMath;
-import com.jme3.math.Quaternion;
-import com.jme3.math.Vector2f;
-import com.jme3.math.Vector3f;
-import com.jme3.renderer.Camera;
-import com.jme3.scene.Geometry;
-import com.jme3.scene.Node;
-import com.jme3.scene.Spatial;
-import com.jme3.scene.shape.Quad;
-import org.openide.util.lookup.ServiceProvider;
-
-/**
- *
- * @author Nehon
- */
-@ServiceProvider(service = MoveManager.class)
-public class MoveManager {
-
-    private Vector3f startLoc;
-    private Vector3f startWorldLoc;
-    private Vector3f lastLoc;
-    private Vector3f offset;
-    private Node alternativePickTarget = null;
-    private Node plane;
-    private Spatial spatial;
-    protected static final Quaternion XY = new Quaternion().fromAngleAxis(0, new Vector3f(1, 0, 0));
-    protected static final Quaternion YZ = new Quaternion().fromAngleAxis(-FastMath.PI / 2, new Vector3f(0, 1, 0));
-    protected static final Quaternion XZ = new Quaternion().fromAngleAxis(FastMath.PI / 2, new Vector3f(1, 0, 0));
-    //temp vars 
-    private Quaternion rot = new Quaternion();
-    private Vector3f newPos = new Vector3f();
-
-    public MoveManager() {
-        float size = 1000;
-        Geometry g = new Geometry("plane", new Quad(size, size));
-        g.setLocalTranslation(-size / 2, -size / 2, 0);
-        plane = new Node();
-        plane.attachChild(g);
-    }
-
-    public Vector3f getOffset() {
-        return offset;
-    }
-
-    public void reset() {
-        offset = null;
-        startLoc = null;
-        startWorldLoc = null;
-        lastLoc = null;
-        spatial = null;
-        alternativePickTarget = null;
-    }
-
-    public void initiateMove(Spatial selectedSpatial, Quaternion planeRotation, boolean local) {
-        spatial = selectedSpatial;
-        startLoc = selectedSpatial.getLocalTranslation().clone();
-        startWorldLoc = selectedSpatial.getWorldTranslation().clone();
-        if (local) {
-            rot.set(selectedSpatial.getWorldRotation());
-            plane.setLocalRotation(rot.multLocal(planeRotation));
-        } else {
-            rot.set(planeRotation);
-        }
-
-        plane.setLocalRotation(rot);
-        plane.setLocalTranslation(startWorldLoc);
-
-    }
-
-    public void updatePlaneRotation(Quaternion planeRotation) {
-        plane.setLocalRotation(rot);
-    }
-
-    public boolean move(Camera camera, Vector2f screenCoord) {
-        return move(camera, screenCoord, Vector3f.UNIT_XYZ, false);
-    }
-
-    public boolean move(Camera camera, Vector2f screenCoord, Vector3f constraintAxis, boolean gridSnap) {
-        Node toPick = alternativePickTarget == null ? plane : alternativePickTarget;
-
-        Vector3f planeHit = SceneEditTool.pickWorldLocation(camera, screenCoord, toPick, alternativePickTarget == null ? null : spatial);
-        if (planeHit == null) {
-            return false;
-        }
-
-        Spatial parent = spatial.getParent();
-        //we are moving the root node, there is a slight chance that something went wrong.
-        if (parent == null) {
-            return false;
-        }
-
-        //offset in world space
-        if (offset == null) {
-            offset = planeHit.subtract(spatial.getWorldTranslation()); // get the offset when we start so it doesn't jump
-        }
-
-        newPos.set(planeHit).subtractLocal(offset);
-
-        //constraining the translation with the contraintAxis.
-        Vector3f tmp = startWorldLoc.mult(Vector3f.UNIT_XYZ.subtract(constraintAxis));
-        newPos.multLocal(constraintAxis).addLocal(tmp);
-        worldToLocalMove(gridSnap);
-        return true;
-    }
-
-    private void worldToLocalMove(boolean gridSnap) {
-        //snap to grid (grid is assumed 1 WU per cell)
-        if (gridSnap) {
-            newPos.set(Math.round(newPos.x), Math.round(newPos.y), Math.round(newPos.z));
-        }
-
-        //computing the inverse world transform to get the new localtranslation        
-        newPos.subtractLocal(spatial.getParent().getWorldTranslation());
-        newPos = spatial.getParent().getWorldRotation().inverse().normalizeLocal().multLocal(newPos);
-        newPos.divideLocal(spatial.getParent().getWorldScale());
-       
-        lastLoc = newPos;
-        spatial.setLocalTranslation(newPos);        
-
-        RigidBodyControl control = spatial.getControl(RigidBodyControl.class);
-        if (control != null) {
-            control.setPhysicsLocation(spatial.getWorldTranslation());
-        }
-        CharacterControl character = spatial.getControl(CharacterControl.class);
-        if (character != null) {
-            character.setPhysicsLocation(spatial.getWorldTranslation());
-        }
-    }
-
-    public boolean moveAcross(Vector3f constraintAxis, float value, boolean gridSnap) {
-        newPos.set(startWorldLoc).addLocal(constraintAxis.mult(value));
-        Spatial parent = spatial.getParent();
-        //we are moving the root node, there is a slight chance that something went wrong.
-        if (parent == null) {
-            return false;
-        }
-        worldToLocalMove(gridSnap);
-        return true;
-    }
-
-    public MoveUndo makeUndo() {
-        return new MoveUndo(spatial, startLoc, lastLoc);
-    }
-
-    public void setAlternativePickTarget(Node alternativePickTarget) {
-        this.alternativePickTarget = alternativePickTarget;
-    }
-
-    protected class MoveUndo extends AbstractUndoableSceneEdit {
-
-        private Spatial spatial;
-        private Vector3f before = new Vector3f(), after = new Vector3f();
-
-        MoveUndo(Spatial spatial, Vector3f before, Vector3f after) {
-            this.spatial = spatial;
-            this.before.set(before);
-            if (after != null) {
-                this.after.set(after);
-            }
-        }
-
-        @Override
-        public void sceneUndo() {
-            spatial.setLocalTranslation(before);
-            RigidBodyControl control = spatial.getControl(RigidBodyControl.class);
-            if (control != null) {
-                control.setPhysicsLocation(spatial.getWorldTranslation());
-            }
-            CharacterControl character = spatial.getControl(CharacterControl.class);
-            if (character != null) {
-                character.setPhysicsLocation(spatial.getWorldTranslation());
-            }
-            //     toolController.selectedSpatialTransformed();
-        }
-
-        @Override
-        public void sceneRedo() {
-            spatial.setLocalTranslation(after);
-            RigidBodyControl control = spatial.getControl(RigidBodyControl.class);
-            if (control != null) {
-                control.setPhysicsLocation(spatial.getWorldTranslation());
-            }
-            CharacterControl character = spatial.getControl(CharacterControl.class);
-            if (character != null) {
-                character.setPhysicsLocation(spatial.getWorldTranslation());
-            }
-            //toolController.selectedSpatialTransformed();
-        }
-
-        public void setAfter(Vector3f after) {
-            this.after.set(after);
-        }
-    }
-}

+ 4 - 8
sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/PickManager.java

@@ -33,9 +33,9 @@ public class PickManager {
     private Spatial spatial;
     private SceneComposerToolController.TransformationType transformationType;
 
-    protected static final Quaternion PLANE_XY = new Quaternion().fromAngleAxis(0, new Vector3f(1, 0, 0));
-    protected static final Quaternion PLANE_YZ = new Quaternion().fromAngleAxis(-FastMath.PI / 2, new Vector3f(0, 1, 0));//YAW090
-    protected static final Quaternion PLANE_XZ = new Quaternion().fromAngleAxis(FastMath.PI / 2, new Vector3f(1, 0, 0)); //PITCH090
+    public static final Quaternion PLANE_XY = new Quaternion().fromAngleAxis(0, new Vector3f(1, 0, 0));
+    public static final Quaternion PLANE_YZ = new Quaternion().fromAngleAxis(-FastMath.PI / 2, new Vector3f(0, 1, 0));//YAW090
+    public static final Quaternion PLANE_XZ = new Quaternion().fromAngleAxis(FastMath.PI / 2, new Vector3f(1, 0, 0)); //PITCH090
 
 
     public PickManager() {
@@ -75,7 +75,7 @@ public class PickManager {
             origineRotation = new Quaternion(Quaternion.IDENTITY);
         } else if (transformationType == SceneComposerToolController.TransformationType.camera) {
             rot.set(camera.getRotation());  
-            origineRotation = camera.getRotation().clone();
+            origineRotation = camera.getRotation();
         }
         plane.setLocalRotation(rot);
     }
@@ -87,10 +87,6 @@ public class PickManager {
      * @return true if the the new picked location is set, else return false.
      */
     public boolean updatePick(Camera camera, Vector2f screenCoord) {
-        if(transformationType == SceneComposerToolController.TransformationType.camera){
-            origineRotation = camera.getRotation();
-            plane.setLocalRotation(camera.getRotation());
-        }
         finalPickLoc = SceneEditTool.pickWorldLocation(camera, screenCoord, plane, null);
         return finalPickLoc != null;
     }

+ 4 - 7
sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/RotateTool.java

@@ -25,7 +25,6 @@ import org.openide.util.Lookup;
 public class RotateTool extends SceneEditTool {
 
     private Vector3f pickedMarker;
-    private Vector2f lastScreenCoord;
     private Quaternion startRotate;
     private Quaternion lastRotate;
     private boolean wasDragging = false;
@@ -48,9 +47,8 @@ public class RotateTool extends SceneEditTool {
         if (!pressed) {
             setDefaultAxisMarkerColors();
             pickedMarker = null; // mouse released, reset selection
-            lastScreenCoord = null;
             if (wasDragging) {
-                actionPerformed(new ScaleUndo(toolController.getSelectedSpatial(), startRotate, lastRotate));
+                actionPerformed(new RotateUndo(toolController.getSelectedSpatial(), startRotate, lastRotate));
                 wasDragging = false;
             }
             pickManager.reset();
@@ -100,10 +98,9 @@ public class RotateTool extends SceneEditTool {
         if (!pressed) {
             setDefaultAxisMarkerColors();
             pickedMarker = null; // mouse released, reset selection
-            lastScreenCoord = null;
 
             if (wasDragging) {
-                actionPerformed(new ScaleUndo(toolController.getSelectedSpatial(), startRotate, lastRotate));
+                actionPerformed(new RotateUndo(toolController.getSelectedSpatial(), startRotate, lastRotate));
                 wasDragging = false;
             }
             pickManager.reset();
@@ -138,12 +135,12 @@ public class RotateTool extends SceneEditTool {
         }
     }
 
-    private class ScaleUndo extends AbstractUndoableSceneEdit {
+    private class RotateUndo extends AbstractUndoableSceneEdit {
 
         private Spatial spatial;
         private Quaternion before, after;
 
-        ScaleUndo(Spatial spatial, Quaternion before, Quaternion after) {
+        RotateUndo(Spatial spatial, Quaternion before, Quaternion after) {
             this.spatial = spatial;
             this.before = before;
             this.after = after;

+ 30 - 664
sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/SelectTool.java

@@ -8,425 +8,51 @@ import com.jme3.gde.core.sceneexplorer.SceneExplorerTopComponent;
 import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
 import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
 import com.jme3.gde.core.sceneviewer.SceneViewerTopComponent;
-import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
 import com.jme3.gde.scenecomposer.SceneEditTool;
-import com.jme3.input.KeyInput;
-import com.jme3.input.event.KeyInputEvent;
-import com.jme3.math.FastMath;
-import com.jme3.math.Quaternion;
 import com.jme3.math.Vector2f;
-import com.jme3.math.Vector3f;
 import com.jme3.scene.Node;
 import com.jme3.scene.Spatial;
 import com.jme3.terrain.Terrain;
 import org.openide.loaders.DataObject;
-import org.openide.util.Lookup;
 
 /**
- * This duplicates the Blender manipulate tool.
- * It supports quick access to Grab, Rotate, and Scale operations
- * by typing one of the following keys: 'g', 'r', or 's'
- * Those keys can be followed by an axis key to specify what axis
- * to perform the transformation: x, y, z
- * Then, after the operation and axis are selected, you can type in a
- * number and then hit 'enter' to complete the transformation.
- * 
- * Ctrl+Shift+D will duplicate an object
- * X will delete an object
- * 
- * ITEMS TO FINISH:
- * 1) fixed scale and rotation values by holding Ctrl and dragging mouse
- * BUGS:
- * 1) window always needs focus from primary click when it should focus from secondary and middle mouse
- * 
+ * This duplicates the Blender manipulate tool. It supports quick access to
+ * Grab, Rotate, and Scale operations by typing one of the following keys: 'g',
+ * 'r', or 's' Those keys can be followed by an axis key to specify what axis to
+ * perform the transformation: x, y, z Then, after the operation and axis are
+ * selected, you can type in a number and then hit 'enter' to complete the
+ * transformation.
+ *
+ * Ctrl+Shift+D will duplicate an object X will delete an object
+ *
+ * ITEMS TO FINISH: 1) fixed scale and rotation values by holding Ctrl and
+ * dragging mouse BUGS: 1) window always needs focus from primary click when it
+ * should focus from secondary and middle mouse
+ *
  * @author Brent Owens
  */
 public class SelectTool extends SceneEditTool {
 
-    private enum State {
-
-        translate, rotate, scale
-    };
-    private State currentState = null;
-    private Vector3f currentAxis = Vector3f.UNIT_XYZ;
-    private StringBuilder numberBuilder = new StringBuilder(); // gets appended with numbers
-    private Quaternion startRot;
-    private Vector3f startTrans;
-    private Vector3f startScale;
-    private boolean wasDraggingL = false;
     private boolean wasDraggingR = false;
     private boolean wasDownR = false;
-    private boolean ctrlDown = false;
-    private boolean shiftDown = false;
-    private boolean altDown = false;
-    private MoveManager.MoveUndo moving;
-    private ScaleUndo scaling;
-    private RotateUndo rotating;
-    private Vector2f startMouseCoord; // for scaling and rotation
-    private Vector2f startSelectedCoord; // for scaling and rotation
-    private float lastRotAngle; // used for rotation
 
     /**
-     * This is stateful:
-     * First it checks for a command (rotate, translate, delete, etc..)
-     * Then it checks for an axis (x,y,z)
-     * Then it checks for a number (user typed a number
-     * Then, finally, it checks if Enter was hit.
-     * 
+     * This is stateful: First it checks for a command (rotate, translate,
+     * delete, etc..) Then it checks for an axis (x,y,z) Then it checks for a
+     * number (user typed a number Then, finally, it checks if Enter was hit.
+     *
      * If either of the commands was actioned, the preceeding states/axis/amount
-     * will be reset. For example if the user types: G Y 2 R
-     * Then it will:
-     * 1) Set state as 'Translate' for the G (grab)
-     * 2) Set the axis as 'Y'; it will translate along the Y axis
-     * 3) Distance will be 2, when the 2 key is hit
-     * 4) Distance, Axis, and state are then reset because a new state was set: Rotate
-     * it won't actually translate because 'Enter' was not hit and 'R' reset the state.
-     * 
+     * will be reset. For example if the user types: G Y 2 R Then it will: 1)
+     * Set state as 'Translate' for the G (grab) 2) Set the axis as 'Y'; it will
+     * translate along the Y axis 3) Distance will be 2, when the 2 key is hit
+     * 4) Distance, Axis, and state are then reset because a new state was set:
+     * Rotate it won't actually translate because 'Enter' was not hit and 'R'
+     * reset the state.
+     *
      */
-    @Override
-    public void keyPressed(KeyInputEvent kie) {
-
-        checkModificatorKeys(kie); // alt,shift,ctrl
-        Spatial selected = toolController.getSelectedSpatial();
-
-        if (selected == null) {
-            return; // only do anything if a spatial is selected
-        }
-        // key released
-        if (kie.isPressed()) {
-            boolean commandUsed = checkCommandKey(kie);
-            boolean stateChange = checkStateKey(kie);
-            boolean axisChange = checkAxisKey(kie);
-            boolean numberChange = checkNumberKey(kie);
-            boolean enterHit = checkEnterHit(kie);
-            boolean escHit = checkEscHit(kie);
-
-            if (commandUsed) {
-                return; // commands take priority
-            }
-            if (stateChange) {
-                currentAxis = Vector3f.UNIT_XYZ;
-                numberBuilder = new StringBuilder();
-                recordInitialState(selected);
-            } else if (axisChange) {
-            } else if (numberChange) {
-            } else if (enterHit) {
-                if (currentState != null && numberBuilder.length() > 0) {
-                    applyKeyedChangeState(selected);
-                    clearState(false);
-                }
-            }
-
-
-            // -----------------------
-            // reset conditions below:
-
-            if (escHit) {
-                if (moving != null) {
-                    moving.sceneUndo();
-                }
-
-                moving = null;
-                clearState();
-            }
-
-            if (!stateChange && !axisChange && !numberChange && !enterHit && !escHit) {
-                // nothing valid was hit, reset the state
-                //clearState(); // this will be 
-            }
-        }
-    }
-
-    /**
-     * Abort any manipulations
-     */
-    private void clearState() {
-        clearState(true);
-    }
-
-    private void clearState(boolean resetSelected) {
-        Spatial selected = toolController.getSelectedSpatial();
-        if (resetSelected && selected != null) {
-            // reset the transforms
-            if (startRot != null) {
-                selected.setLocalRotation(startRot);
-            }
-            if (startTrans != null) {
-                selected.setLocalTranslation(startTrans);
-            }
-            if (startScale != null) {
-                selected.setLocalScale(startScale);
-            }
-        }
-        currentState = null;
-        currentAxis = Vector3f.UNIT_XYZ;
-        numberBuilder = new StringBuilder();
-        startRot = null;
-        startTrans = null;
-        startScale = null;
-        startMouseCoord = null;
-        startSelectedCoord = null;
-        lastRotAngle = 0;
-    }
-
-    private void recordInitialState(Spatial selected) {
-        startRot = selected.getLocalRotation().clone();
-        startTrans = selected.getLocalTranslation().clone();
-        startScale = selected.getLocalScale().clone();
-    }
-
-    /**
-     * Applies the changes entered by a number, not by mouse.
-     * Translate: adds the value to the current local translation
-     * Rotate: rotates by X degrees
-     * Scale: scale the current scale by X amount
-     */
-    private void applyKeyedChangeState(Spatial selected) {
-        Float value = null;
-        try {
-            value = new Float(numberBuilder.toString());
-        } catch (NumberFormatException e) {
-            return;
-        }
-
-        if (currentState == State.translate) {
-            MoveManager moveManager = Lookup.getDefault().lookup(MoveManager.class);
-            moveManager.moveAcross(currentAxis, value, toolController.isSnapToGrid());
-            moving.setAfter(selected.getLocalTranslation());
-            actionPerformed(moving);
-            moving = null;
-        } else if (currentState == State.scale) {
-            float x = 1, y = 1, z = 1;
-            if (currentAxis == Vector3f.UNIT_X) {
-                x = value;
-            } else if (currentAxis == Vector3f.UNIT_Y) {
-                y = value;
-            } else if (currentAxis == Vector3f.UNIT_Z) {
-                z = value;
-            } else if (currentAxis == Vector3f.UNIT_XYZ) {
-                x = value;
-                y = value;
-                z = value;
-            }
-            Vector3f before = selected.getLocalScale().clone();
-            Vector3f after = selected.getLocalScale().multLocal(x, y, z);
-            selected.setLocalScale(after);
-            actionPerformed(new ScaleUndo(selected, before, after));
-        } else if (currentState == State.rotate) {
-            float x = 0, y = 0, z = 0;
-            if (currentAxis == Vector3f.UNIT_X) {
-                x = 1;
-            } else if (currentAxis == Vector3f.UNIT_Y) {
-                y = 1;
-            } else if (currentAxis == Vector3f.UNIT_Z) {
-                z = 1;
-            }
-            Vector3f axis = new Vector3f(x, y, z);
-            Quaternion initialRot = selected.getLocalRotation().clone();
-            Quaternion rot = new Quaternion();
-            rot = rot.fromAngleAxis(value * FastMath.DEG_TO_RAD, axis);
-            selected.setLocalRotation(selected.getLocalRotation().mult(rot));
-            RotateUndo undo = new RotateUndo(selected, initialRot, rot);
-            actionPerformed(undo);
-            toolController.updateSelection(null);// force a re-draw of the bbox shape
-            toolController.updateSelection(selected);
-
-        }
-        clearState(false);
-    }
-
-    private void checkModificatorKeys(KeyInputEvent kie) {
-        if (kie.getKeyCode() == KeyInput.KEY_LCONTROL || kie.getKeyCode() == KeyInput.KEY_RCONTROL) {
-            ctrlDown = kie.isPressed();
-        }
-
-        if (kie.getKeyCode() == KeyInput.KEY_LSHIFT || kie.getKeyCode() == KeyInput.KEY_RSHIFT) {
-            shiftDown = kie.isPressed();
-        }
-
-        if (kie.getKeyCode() == KeyInput.KEY_LMENU || kie.getKeyCode() == KeyInput.KEY_RMENU) {
-            altDown = kie.isPressed();
-        }
-    }
-
-    private boolean checkCommandKey(KeyInputEvent kie) {
-        if (kie.getKeyCode() == KeyInput.KEY_D) {
-            if (shiftDown) {
-                duplicateSelected();
-                return true;
-            }
-        }
-        // X will only delete if the user isn't already transforming
-        if (currentState == null && kie.getKeyCode() == KeyInput.KEY_X) {
-            if (!ctrlDown && !shiftDown) {
-                deleteSelected();
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private boolean checkStateKey(KeyInputEvent kie) {
-        Spatial selected = toolController.getSelectedSpatial();
-        if (kie.getKeyCode() == KeyInput.KEY_G && !ctrlDown) {
-            currentState = State.translate;
-            MoveManager moveManager = Lookup.getDefault().lookup(MoveManager.class);
-            moveManager.reset();
-            Quaternion rot = camera.getRotation().mult(new Quaternion().fromAngleAxis(FastMath.PI, Vector3f.UNIT_Y));
-            moveManager.initiateMove(selected, rot, false);
-            moving = moveManager.makeUndo();
-            return true;
-        } else if (kie.getKeyCode() == KeyInput.KEY_R && !ctrlDown) {
-            currentState = State.rotate;
-            return true;
-        } else if (kie.getKeyCode() == KeyInput.KEY_S && !ctrlDown) {
-            currentState = State.scale;
-            return true;
-        }
-        return false;
-    }
-
-    private boolean checkAxisKey(KeyInputEvent kie) {
-        if (kie.getKeyCode() == KeyInput.KEY_X) {
-            currentAxis = Vector3f.UNIT_X;
-            checkMovePlane(MoveManager.XY, MoveManager.XZ);
-            return true;
-        } else if (kie.getKeyCode() == KeyInput.KEY_Y) {
-            currentAxis = Vector3f.UNIT_Y;
-            checkMovePlane(MoveManager.XY, MoveManager.YZ);
-            return true;
-        } else if (kie.getKeyCode() == KeyInput.KEY_Z) {
-            currentAxis = Vector3f.UNIT_Z;
-            checkMovePlane(MoveManager.XZ, MoveManager.YZ);
-            return true;
-        }
-        return false;
-    }
-
-    private void checkMovePlane(Quaternion rot1, Quaternion rot2) {
-        if (currentState == State.translate) {
-            MoveManager moveManager = Lookup.getDefault().lookup(MoveManager.class);
-            Quaternion rot = camera.getRotation().mult(new Quaternion().fromAngleAxis(FastMath.PI, Vector3f.UNIT_Y));
-            Quaternion planRot = null;
-            if (rot.dot(rot1) < rot.dot(rot2)) {
-                planRot = rot1;
-            } else {
-                planRot = rot2;
-            }
-            moveManager.updatePlaneRotation(planRot);
-        }
-    }
-
-    private boolean checkNumberKey(KeyInputEvent kie) {
-        if (kie.getKeyCode() == KeyInput.KEY_MINUS) {
-            if (numberBuilder.length() > 0) {
-                if (numberBuilder.charAt(0) == '-') {
-                    numberBuilder.replace(0, 1, "");
-                } else {
-                    numberBuilder.insert(0, '-');
-                }
-            } else {
-                numberBuilder.append('-');
-            }
-            return true;
-        } else if (kie.getKeyCode() == KeyInput.KEY_0 || kie.getKeyCode() == KeyInput.KEY_NUMPAD0) {
-            numberBuilder.append('0');
-            return true;
-        } else if (kie.getKeyCode() == KeyInput.KEY_1 || kie.getKeyCode() == KeyInput.KEY_NUMPAD1) {
-            numberBuilder.append('1');
-            return true;
-        } else if (kie.getKeyCode() == KeyInput.KEY_2 || kie.getKeyCode() == KeyInput.KEY_NUMPAD2) {
-            numberBuilder.append('2');
-            return true;
-        } else if (kie.getKeyCode() == KeyInput.KEY_3 || kie.getKeyCode() == KeyInput.KEY_NUMPAD3) {
-            numberBuilder.append('3');
-            return true;
-        } else if (kie.getKeyCode() == KeyInput.KEY_4 || kie.getKeyCode() == KeyInput.KEY_NUMPAD4) {
-            numberBuilder.append('4');
-            return true;
-        } else if (kie.getKeyCode() == KeyInput.KEY_5 || kie.getKeyCode() == KeyInput.KEY_NUMPAD5) {
-            numberBuilder.append('5');
-            return true;
-        } else if (kie.getKeyCode() == KeyInput.KEY_6 || kie.getKeyCode() == KeyInput.KEY_NUMPAD6) {
-            numberBuilder.append('6');
-            return true;
-        } else if (kie.getKeyCode() == KeyInput.KEY_7 || kie.getKeyCode() == KeyInput.KEY_NUMPAD7) {
-            numberBuilder.append('7');
-            return true;
-        } else if (kie.getKeyCode() == KeyInput.KEY_8 || kie.getKeyCode() == KeyInput.KEY_NUMPAD8) {
-            numberBuilder.append('8');
-            return true;
-        } else if (kie.getKeyCode() == KeyInput.KEY_9 || kie.getKeyCode() == KeyInput.KEY_NUMPAD9) {
-            numberBuilder.append('9');
-            return true;
-        } else if (kie.getKeyCode() == KeyInput.KEY_PERIOD) {
-            if (numberBuilder.indexOf(".") == -1) { // if it doesn't exist yet
-                if (numberBuilder.length() == 0
-                        || (numberBuilder.length() == 1 && numberBuilder.charAt(0) == '-')) {
-                    numberBuilder.append("0.");
-                } else {
-                    numberBuilder.append(".");
-                }
-            }
-            return true;
-        }
-
-        return false;
-    }
-
-    private boolean checkEnterHit(KeyInputEvent kie) {
-        if (kie.getKeyCode() == KeyInput.KEY_RETURN) {
-            return true;
-        }
-        return false;
-    }
-
-    private boolean checkEscHit(KeyInputEvent kie) {
-        if (kie.getKeyCode() == KeyInput.KEY_ESCAPE) {
-            return true;
-        }
-        return false;
-    }
-
     @Override
     public void actionPrimary(Vector2f screenCoord, boolean pressed, final JmeNode rootNode, DataObject dataObject) {
-        if (!pressed) {
-            Spatial selected = toolController.getSelectedSpatial();
-            // left mouse released
-            if (!wasDraggingL) {
-                // left mouse pressed
-                if (currentState != null) {
-                    // finish manipulating the spatial
-                    if (moving != null) {
-                        moving.setAfter(selected.getLocalTranslation());
-                        actionPerformed(moving);
-                        moving = null;
-                        clearState(false);
-                    } else if (scaling != null) {
-                        scaling.after = selected.getLocalScale().clone();
-                        actionPerformed(scaling);
-                        scaling = null;
-                        clearState(false);
-                        toolController.rebuildSelectionBox();
-                    } else if (rotating != null) {
-                        rotating.after = selected.getLocalRotation().clone();
-                        actionPerformed(rotating);
-                        rotating = null;
-                        clearState(false);
-                    }
-                } else {
-                    // mouse released and wasn't dragging, place cursor
-                    final Vector3f result = pickWorldLocation(getCamera(), screenCoord, rootNode);
-                    if (result != null) {
-                        if (toolController.isSnapToGrid()) {
-                            result.set(Math.round(result.x), result.y, Math.round(result.z));
-                        }
-                        toolController.setCursorLocation(result);
-                    }
-                }
-            }
-            wasDraggingL = false;
-        }
+
     }
 
     @Override
@@ -435,19 +61,7 @@ public class SelectTool extends SceneEditTool {
             Spatial selected = toolController.getSelectedSpatial();
             // mouse down
 
-            if (moving != null) {
-                moving.sceneUndo();
-                moving = null;
-                clearState();
-            } else if (scaling != null) {
-                scaling.sceneUndo();
-                scaling = null;
-                clearState();
-            } else if (rotating != null) {
-                rotating.sceneUndo();
-                rotating = null;
-                clearState();
-            } else if (!wasDraggingR && !wasDownR) { // wasn't dragging and was not down already
+            if (!wasDraggingR && !wasDownR) { // wasn't dragging and was not down already
                 // pick on the spot
                 Spatial s = pickWorldSpatial(camera, screenCoord, rootNode);
                 if (!toolController.selectTerrain() && isTerrain(s)) {
@@ -498,8 +112,8 @@ public class SelectTool extends SceneEditTool {
     }
 
     /**
-     * Climb up the spatial until we find the first node parent.
-     * TODO: use userData to determine the actual model's parent.
+     * Climb up the spatial until we find the first node parent. TODO: use
+     * userData to determine the actual model's parent.
      */
     private Spatial findModelNodeParent(Spatial child) {
         if (child == null) {
@@ -519,14 +133,10 @@ public class SelectTool extends SceneEditTool {
 
     @Override
     public void mouseMoved(Vector2f screenCoord, JmeNode rootNode, DataObject currentDataObject, JmeSpatial selectedSpatial) {
-        if (currentState != null) {
-            handleMouseManipulate(screenCoord, currentState, currentAxis, rootNode, currentDataObject, selectedSpatial);
-        }
     }
 
     @Override
     public void draggedPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
-        wasDraggingL = pressed;
     }
 
     @Override
@@ -535,252 +145,8 @@ public class SelectTool extends SceneEditTool {
     }
 
     /**
-     * Manipulate the spatial
-     */
-    private void handleMouseManipulate(Vector2f screenCoord,
-            State state,
-            Vector3f axis,
-            JmeNode rootNode,
-            DataObject currentDataObject,
-            JmeSpatial selectedSpatial) {
-        if (state == State.translate) {
-            doMouseTranslate(axis, screenCoord, rootNode, selectedSpatial);
-        } else if (state == State.scale) {
-            doMouseScale(axis, screenCoord, rootNode, selectedSpatial);
-        } else if (state == State.rotate) {
-            doMouseRotate(axis, screenCoord, rootNode, selectedSpatial);
-        }
-
-    }
-
-    private void doMouseTranslate(Vector3f axis, Vector2f screenCoord, JmeNode rootNode, JmeSpatial selectedSpatial) {
-        MoveManager moveManager = Lookup.getDefault().lookup(MoveManager.class);
-        if (toolController.isSnapToScene()) {
-            moveManager.setAlternativePickTarget(rootNode.getLookup().lookup(Node.class));
-        }
-        // free form translation
-        moveManager.move(camera, screenCoord, axis, toolController.isSnapToGrid());
-    }
-
-    private void doMouseScale(Vector3f axis, Vector2f screenCoord, JmeNode rootNode, JmeSpatial selectedSpatial) {
-        Spatial selected = toolController.getSelectedSpatial();
-        // scale based on the original mouse position and original model-to-screen position
-        // and compare that to the distance from the new mouse position and the original distance
-        if (startMouseCoord == null) {
-            startMouseCoord = screenCoord.clone();
-        }
-        if (startSelectedCoord == null) {
-            Vector3f screen = getCamera().getScreenCoordinates(selected.getWorldTranslation());
-            startSelectedCoord = new Vector2f(screen.x, screen.y);
-        }
-
-        if (scaling == null) {
-            scaling = new ScaleUndo(selected, selected.getLocalScale().clone(), null);
-        }
-
-        float origDist = startMouseCoord.distanceSquared(startSelectedCoord);
-        float newDist = screenCoord.distanceSquared(startSelectedCoord);
-        if (origDist == 0) {
-            origDist = 1;
-        }
-        float ratio = newDist / origDist;
-        Vector3f prev = selected.getLocalScale();
-        if (axis == Vector3f.UNIT_X) {
-            selected.setLocalScale(ratio, prev.y, prev.z);
-        } else if (axis == Vector3f.UNIT_Y) {
-            selected.setLocalScale(prev.x, ratio, prev.z);
-        } else if (axis == Vector3f.UNIT_Z) {
-            selected.setLocalScale(prev.x, prev.y, ratio);
-        } else {
-            selected.setLocalScale(ratio, ratio, ratio);
-        }
-    }
-
-    private void doMouseRotate(Vector3f axis, Vector2f screenCoord, JmeNode rootNode, JmeSpatial selectedSpatial) {
-        Spatial selected = toolController.getSelectedSpatial();
-        if (startMouseCoord == null) {
-            startMouseCoord = screenCoord.clone();
-        }
-        if (startSelectedCoord == null) {
-            Vector3f screen = getCamera().getScreenCoordinates(selected.getWorldTranslation());
-            startSelectedCoord = new Vector2f(screen.x, screen.y);
-        }
-
-        if (rotating == null) {
-            rotating = new RotateUndo(selected, selected.getLocalRotation().clone(), null);
-        }
-
-        Vector2f origRot = startMouseCoord.subtract(startSelectedCoord);
-        Vector2f newRot = screenCoord.subtract(startSelectedCoord);
-        float newRotAngle = origRot.angleBetween(newRot);
-        float temp = newRotAngle;
-
-        if (lastRotAngle != 0) {
-            newRotAngle -= lastRotAngle;
-        }
-
-        lastRotAngle = temp;
-
-        Quaternion rotate = new Quaternion();
-        if (axis != Vector3f.UNIT_XYZ) {
-            rotate = rotate.fromAngleAxis(newRotAngle, selected.getWorldRotation().inverse().mult(axis));
-        } else {
-            rotate = rotate.fromAngleAxis(newRotAngle, selected.getWorldRotation().inverse().mult(getCamera().getDirection().mult(-1).normalizeLocal()));
-        }
-        selected.setLocalRotation(selected.getLocalRotation().mult(rotate));
-
-
-    }
-
-    private void duplicateSelected() {
-        Spatial selected = toolController.getSelectedSpatial();
-        if (selected == null) {
-            return;
-        }
-        Spatial clone = selected.clone();
-        clone.move(1, 0, 1);
-
-        selected.getParent().attachChild(clone);
-        actionPerformed(new DuplicateUndo(clone, selected.getParent()));
-        selected = clone;
-        final Spatial cloned = clone;
-        final JmeNode rootNode = toolController.getRootNode();
-        refreshSelected(rootNode, selected.getParent());
-
-        java.awt.EventQueue.invokeLater(new Runnable() {
-
-            @Override
-            public void run() {
-                if (cloned != null) {
-                    SceneViewerTopComponent.findInstance().setActivatedNodes(new org.openide.nodes.Node[]{rootNode.getChild(cloned)});
-                    SceneExplorerTopComponent.findInstance().setSelectedNode(rootNode.getChild(cloned));
-                }
-            }
-        });
-
-        // set to automatically 'grab'/'translate' the new cloned model
-        toolController.updateSelection(selected);
-        currentState = State.translate;
-        currentAxis = Vector3f.UNIT_XYZ;
-    }
-
-    private void deleteSelected() {
-        Spatial selected = toolController.getSelectedSpatial();
-        if (selected == null) {
-            return;
-        }
-        Node parent = selected.getParent();
-        selected.removeFromParent();
-        actionPerformed(new DeleteUndo(selected, parent));
-
-        selected = null;
-        toolController.updateSelection(selected);
-
-        final JmeNode rootNode = toolController.getRootNode();
-        refreshSelected(rootNode, parent);
-    }
-
-    private void refreshSelected(final JmeNode jmeRootNode, final Node parent) {
-        java.awt.EventQueue.invokeLater(new Runnable() {
-
-            @Override
-            public void run() {
-                jmeRootNode.getChild(parent).refresh(false);
-            }
-        });
-    }
-
-    private class ScaleUndo extends AbstractUndoableSceneEdit {
-
-        private Spatial spatial;
-        private Vector3f before, after;
-
-        ScaleUndo(Spatial spatial, Vector3f before, Vector3f after) {
-            this.spatial = spatial;
-            this.before = before;
-            this.after = after;
-        }
-
-        @Override
-        public void sceneUndo() {
-            spatial.setLocalScale(before);
-        }
-
-        @Override
-        public void sceneRedo() {
-            spatial.setLocalScale(after);
-        }
-    }
-
-    private class RotateUndo extends AbstractUndoableSceneEdit {
-
-        private Spatial spatial;
-        private Quaternion before, after;
-
-        RotateUndo(Spatial spatial, Quaternion before, Quaternion after) {
-            this.spatial = spatial;
-            this.before = before;
-            this.after = after;
-        }
-
-        @Override
-        public void sceneUndo() {
-            spatial.setLocalRotation(before);
-        }
-
-        @Override
-        public void sceneRedo() {
-            spatial.setLocalRotation(after);
-        }
-    }
-
-    private class DeleteUndo extends AbstractUndoableSceneEdit {
-
-        private Spatial spatial;
-        private Node parent;
-
-        DeleteUndo(Spatial spatial, Node parent) {
-            this.spatial = spatial;
-            this.parent = parent;
-        }
-
-        @Override
-        public void sceneUndo() {
-            parent.attachChild(spatial);
-        }
-
-        @Override
-        public void sceneRedo() {
-            spatial.removeFromParent();
-        }
-    }
-
-    private class DuplicateUndo extends AbstractUndoableSceneEdit {
-
-        private Spatial spatial;
-        private Node parent;
-
-        DuplicateUndo(Spatial spatial, Node parent) {
-            this.spatial = spatial;
-            this.parent = parent;
-        }
-
-        @Override
-        public void sceneUndo() {
-            spatial.removeFromParent();
-        }
-
-        @Override
-        public void sceneRedo() {
-            parent.attachChild(spatial);
-        }
-    }
-
-    /**
-     * Check if the selected item is a Terrain
-     * It will climb up the parent tree to see if
-     * a parent is terrain too.
-     * Recursive call.
+     * Check if the selected item is a Terrain It will climb up the parent tree
+     * to see if a parent is terrain too. Recursive call.
      */
     protected boolean isTerrain(Spatial s) {
         if (s == null) {

+ 123 - 0
sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/shortcuts/DeleteShortcut.java

@@ -0,0 +1,123 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.scenecomposer.tools.shortcuts;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.gde.core.sceneexplorer.SceneExplorerTopComponent;
+import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
+import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
+import com.jme3.gde.core.sceneviewer.SceneViewerTopComponent;
+import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
+import com.jme3.gde.scenecomposer.SceneComposerToolController;
+import com.jme3.input.KeyInput;
+import com.jme3.input.event.KeyInputEvent;
+import com.jme3.math.Vector2f;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import org.openide.loaders.DataObject;
+import org.openide.util.Lookup;
+
+/**
+ *
+ * @author dokthar
+ */
+public class DeleteShortcut extends ShortcutTool {
+
+    @Override
+    public boolean isActivableBy(KeyInputEvent kie) {
+        if (kie.getKeyCode() == KeyInput.KEY_X && kie.isPressed()) {
+            if (Lookup.getDefault().lookup(ShortcutManager.class).isShiftDown()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void cancel() {
+        terminate();
+    }
+
+    @Override
+    public void activate(AssetManager manager, Node toolNode, Node onTopToolNode, Spatial selectedSpatial, SceneComposerToolController toolController) {
+        super.activate(manager, toolNode, onTopToolNode, selectedSpatial, toolController); //To change body of generated methods, choose Tools | Templates.
+        hideMarker();
+        if (selectedSpatial != null) {
+            delete();
+        }
+        terminate();
+    }
+
+    private void delete() {
+        Spatial selected = toolController.getSelectedSpatial();
+
+        Node parent = selected.getParent();
+        selected.removeFromParent();
+        actionPerformed(new DeleteUndo(selected, parent));
+
+        selected = null;
+        toolController.updateSelection(selected);
+
+        final JmeNode rootNode = toolController.getRootNode();
+        refreshSelected(rootNode, parent);
+    }
+
+    private void refreshSelected(final JmeNode jmeRootNode, final Node parent) {
+        java.awt.EventQueue.invokeLater(new Runnable() {
+
+            @Override
+            public void run() {
+                jmeRootNode.getChild(parent).refresh(false);
+            }
+        });
+    }
+
+    @Override
+    public void keyPressed(KeyInputEvent kie) {
+
+    }
+
+    @Override
+    public void actionPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
+    }
+
+    @Override
+    public void actionSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
+    }
+
+    @Override
+    public void mouseMoved(Vector2f screenCoord, JmeNode rootNode, DataObject dataObject, JmeSpatial selectedSpatial) {
+    }
+
+    @Override
+    public void draggedPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
+    }
+
+    @Override
+    public void draggedSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
+    }
+    
+    private class DeleteUndo extends AbstractUndoableSceneEdit {
+
+        private Spatial spatial;
+        private Node parent;
+
+        DeleteUndo(Spatial spatial, Node parent) {
+            this.spatial = spatial;
+            this.parent = parent;
+        }
+
+        @Override
+        public void sceneUndo() {
+            parent.attachChild(spatial);
+        }
+
+        @Override
+        public void sceneRedo() {
+            spatial.removeFromParent();
+        }
+    }
+}

+ 141 - 0
sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/shortcuts/DuplicateShortcut.java

@@ -0,0 +1,141 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.scenecomposer.tools.shortcuts;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.gde.core.sceneexplorer.SceneExplorerTopComponent;
+import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
+import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
+import com.jme3.gde.core.sceneviewer.SceneViewerTopComponent;
+import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
+import com.jme3.gde.scenecomposer.SceneComposerToolController;
+import com.jme3.input.KeyInput;
+import com.jme3.input.event.KeyInputEvent;
+import com.jme3.math.Vector2f;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import org.openide.loaders.DataObject;
+import org.openide.util.Lookup;
+
+/**
+ *
+ * @author dokthar
+ */
+public class DuplicateShortcut extends ShortcutTool {
+
+    @Override
+    public boolean isActivableBy(KeyInputEvent kie) {
+        if (kie.getKeyCode() == KeyInput.KEY_D && kie.isPressed()) {
+            if (Lookup.getDefault().lookup(ShortcutManager.class).isShiftDown()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void cancel() {
+        terminate();
+    }
+
+    @Override
+    public void activate(AssetManager manager, Node toolNode, Node onTopToolNode, Spatial selectedSpatial, SceneComposerToolController toolController) {
+        super.activate(manager, toolNode, onTopToolNode, selectedSpatial, toolController); //To change body of generated methods, choose Tools | Templates.
+        hideMarker();
+        if (selectedSpatial != null) {
+            duplicate();
+            terminate();
+            
+            //then enable move shortcut
+            toolController.doKeyPressed(new KeyInputEvent(KeyInput.KEY_G, 'g', true, false));
+        } else {
+            terminate();
+        }
+    }
+
+    private void duplicate() {
+        Spatial selected = toolController.getSelectedSpatial();
+
+        Spatial clone = selected.clone();
+        clone.move(1, 0, 1);
+
+        selected.getParent().attachChild(clone);
+        actionPerformed(new DuplicateUndo(clone, selected.getParent()));
+        selected = clone;
+        final Spatial cloned = clone;
+        final JmeNode rootNode = toolController.getRootNode();
+        refreshSelected(rootNode, selected.getParent());
+
+        java.awt.EventQueue.invokeLater(new Runnable() {
+
+            @Override
+            public void run() {
+                if (cloned != null) {
+                    SceneViewerTopComponent.findInstance().setActivatedNodes(new org.openide.nodes.Node[]{rootNode.getChild(cloned)});
+                    SceneExplorerTopComponent.findInstance().setSelectedNode(rootNode.getChild(cloned));
+                }
+            }
+        });
+
+        toolController.updateSelection(selected);
+    }
+
+    private void refreshSelected(final JmeNode jmeRootNode, final Node parent) {
+        java.awt.EventQueue.invokeLater(new Runnable() {
+
+            @Override
+            public void run() {
+                jmeRootNode.getChild(parent).refresh(false);
+            }
+        });
+    }
+
+    @Override
+    public void keyPressed(KeyInputEvent kie) {
+
+    }
+
+    @Override
+    public void actionPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
+    }
+
+    @Override
+    public void actionSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
+    }
+
+    @Override
+    public void mouseMoved(Vector2f screenCoord, JmeNode rootNode, DataObject dataObject, JmeSpatial selectedSpatial) {
+    }
+
+    @Override
+    public void draggedPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
+    }
+
+    @Override
+    public void draggedSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
+    }
+
+    private class DuplicateUndo extends AbstractUndoableSceneEdit {
+
+        private Spatial spatial;
+        private Node parent;
+
+        DuplicateUndo(Spatial spatial, Node parent) {
+            this.spatial = spatial;
+            this.parent = parent;
+        }
+
+        @Override
+        public void sceneUndo() {
+            spatial.removeFromParent();
+        }
+
+        @Override
+        public void sceneRedo() {
+            parent.attachChild(spatial);
+        }
+    }
+}

+ 227 - 0
sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/shortcuts/MoveShortcut.java

@@ -0,0 +1,227 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.scenecomposer.tools.shortcuts;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.bullet.control.CharacterControl;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
+import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
+import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
+import com.jme3.gde.scenecomposer.SceneComposerToolController;
+import com.jme3.gde.scenecomposer.tools.PickManager;
+import com.jme3.input.KeyInput;
+import com.jme3.input.event.KeyInputEvent;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import org.openide.loaders.DataObject;
+import org.openide.util.Lookup;
+
+/**
+ *
+ * @author dokthar
+ */
+public class MoveShortcut extends ShortcutTool {
+
+    private Vector3f currentAxis;
+    private StringBuilder numberBuilder;
+    private Spatial spatial;
+    private PickManager pickManager;
+    private boolean pickEnabled;
+    private Vector3f startPosition;
+    private Vector3f finalPosition;
+
+    @Override
+
+    public boolean isActivableBy(KeyInputEvent kie) {
+        return kie.getKeyCode() == KeyInput.KEY_G;
+    }
+
+    @Override
+    public void cancel() {
+        spatial.setLocalTranslation(startPosition);
+        terminate();
+    }
+
+    private void apply() {
+        actionPerformed(new MoveUndo(toolController.getSelectedSpatial(), startPosition, finalPosition));
+        terminate();
+    }
+
+    private void init(Spatial selectedSpatial) {
+        spatial = selectedSpatial;
+        startPosition = spatial.getLocalTranslation().clone();
+        currentAxis = Vector3f.UNIT_XYZ;
+        pickManager = Lookup.getDefault().lookup(PickManager.class);
+        pickEnabled = false;
+    }
+
+    @Override
+    public void activate(AssetManager manager, Node toolNode, Node onTopToolNode, Spatial selectedSpatial, SceneComposerToolController toolController) {
+        super.activate(manager, toolNode, onTopToolNode, selectedSpatial, toolController); //To change body of generated methods, choose Tools | Templates.
+        hideMarker();
+        numberBuilder = new StringBuilder();
+        if (selectedSpatial == null) {
+            terminate();
+        } else {
+            init(selectedSpatial);
+        }
+    }
+
+    @Override
+    public void keyPressed(KeyInputEvent kie) {
+        if (kie.isPressed()) {
+            Lookup.getDefault().lookup(ShortcutManager.class).activateShortcut(kie);
+
+            Vector3f axis = new Vector3f();
+            boolean axisChanged = ShortcutManager.checkAxisKey(kie, axis);
+            if (axisChanged) {
+                currentAxis = axis;
+            }
+            boolean numberChanged = ShortcutManager.checkNumberKey(kie, numberBuilder);
+            boolean enterHit = ShortcutManager.checkEnterHit(kie);
+            boolean escHit = ShortcutManager.checkEscHit(kie);
+
+            if (escHit) {
+                cancel();
+            } else if (enterHit) {
+                apply();
+            } else if (axisChanged && pickEnabled) {
+                //update pick manager
+
+                if (currentAxis.equals(Vector3f.UNIT_X)) {
+                    pickManager.setTransformation(PickManager.PLANE_XY, getTransformType(), camera);
+                } else if (currentAxis.equals(Vector3f.UNIT_Y)) {
+                    pickManager.setTransformation(PickManager.PLANE_YZ, getTransformType(), camera);
+                } else if (currentAxis.equals(Vector3f.UNIT_Z)) {
+                    pickManager.setTransformation(PickManager.PLANE_XZ, getTransformType(), camera);
+                }
+            } else if (axisChanged || numberChanged) {
+                //update transformation
+                float number = ShortcutManager.getNumberKey(numberBuilder);
+                Vector3f translation = currentAxis.mult(number);
+                finalPosition = startPosition.add(translation);
+                spatial.setLocalTranslation(finalPosition);
+
+            }
+
+        }
+    }
+
+    @Override
+    public void actionPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
+        if (pressed) {
+            apply();
+        }
+    }
+
+    @Override
+    public void actionSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
+        if (pressed) {
+            cancel();
+        }
+    }
+
+    @Override
+    public void mouseMoved(Vector2f screenCoord, JmeNode rootNode, DataObject dataObject, JmeSpatial selectedSpatial) {
+
+        if (!pickEnabled) {
+            if (currentAxis.equals(Vector3f.UNIT_XYZ)) {
+                pickManager.initiatePick(toolController.getSelectedSpatial(), camera.getRotation(), SceneComposerToolController.TransformationType.camera, camera, screenCoord);
+                pickEnabled = true;
+            } else if (currentAxis.equals(Vector3f.UNIT_X)) {
+                pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_XY, getTransformType(), camera, screenCoord);
+                pickEnabled = true;
+            } else if (currentAxis.equals(Vector3f.UNIT_Y)) {
+                pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_YZ, getTransformType(), camera, screenCoord);
+                pickEnabled = true;
+            } else if (currentAxis.equals(Vector3f.UNIT_Z)) {
+                pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_XZ, getTransformType(), camera, screenCoord);
+                pickEnabled = true;
+            } else {
+                return;
+            }
+        }
+
+        if (pickManager.updatePick(camera, screenCoord)) {
+            //pick update success
+            Vector3f diff;
+
+            if (currentAxis.equals(Vector3f.UNIT_XYZ)) {
+                diff = pickManager.getTranslation();
+            } else {
+                diff = pickManager.getTranslation(currentAxis);
+            }
+            Vector3f position = startPosition.add(diff);
+            finalPosition = position;
+            toolController.getSelectedSpatial().setLocalTranslation(position);
+            updateToolsTransformation();
+        }
+    }
+
+    @Override
+    public void draggedPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
+        if (pressed) {
+            apply();
+        }
+    }
+
+    @Override
+    public void draggedSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
+        if (pressed) {
+            cancel();
+        }
+    }
+    
+    private class MoveUndo extends AbstractUndoableSceneEdit {
+
+        private Spatial spatial;
+        private Vector3f before = new Vector3f(), after = new Vector3f();
+
+        MoveUndo(Spatial spatial, Vector3f before, Vector3f after) {
+            this.spatial = spatial;
+            this.before.set(before);
+            if (after != null) {
+                this.after.set(after);
+            }
+        }
+
+        @Override
+        public void sceneUndo() {
+            spatial.setLocalTranslation(before);
+            RigidBodyControl control = spatial.getControl(RigidBodyControl.class);
+            if (control != null) {
+                control.setPhysicsLocation(spatial.getWorldTranslation());
+            }
+            CharacterControl character = spatial.getControl(CharacterControl.class);
+            if (character != null) {
+                character.setPhysicsLocation(spatial.getWorldTranslation());
+            }
+            //     toolController.selectedSpatialTransformed();
+        }
+
+        @Override
+        public void sceneRedo() {
+            spatial.setLocalTranslation(after);
+            RigidBodyControl control = spatial.getControl(RigidBodyControl.class);
+            if (control != null) {
+                control.setPhysicsLocation(spatial.getWorldTranslation());
+            }
+            CharacterControl character = spatial.getControl(CharacterControl.class);
+            if (character != null) {
+                character.setPhysicsLocation(spatial.getWorldTranslation());
+            }
+            //toolController.selectedSpatialTransformed();
+        }
+
+        public void setAfter(Vector3f after) {
+            this.after.set(after);
+        }
+    }
+
+}

+ 189 - 0
sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/shortcuts/RotateShortcut.java

@@ -0,0 +1,189 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.scenecomposer.tools.shortcuts;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
+import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
+import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
+import com.jme3.gde.scenecomposer.SceneComposerToolController;
+import com.jme3.gde.scenecomposer.tools.PickManager;
+import com.jme3.input.KeyInput;
+import com.jme3.input.event.KeyInputEvent;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import org.openide.loaders.DataObject;
+import org.openide.util.Lookup;
+
+/**
+ *
+ * @author dokthar
+ */
+public class RotateShortcut extends ShortcutTool {
+
+    private Vector3f currentAxis;
+    private StringBuilder numberBuilder;
+    private Spatial spatial;
+    private PickManager pickManager;
+    private boolean pickEnabled;
+    private Quaternion startRotation;
+    private Quaternion finalRotation;
+
+    @Override
+
+    public boolean isActivableBy(KeyInputEvent kie) {
+        return kie.getKeyCode() == KeyInput.KEY_R;
+    }
+
+    @Override
+    public void cancel() {
+        spatial.setLocalRotation(startRotation);
+        terminate();
+    }
+
+    private void apply() {
+        actionPerformed(new RotateUndo(toolController.getSelectedSpatial(), startRotation, finalRotation));
+        terminate();
+    }
+
+    private void init(Spatial selectedSpatial) {
+        spatial = selectedSpatial;
+        startRotation = spatial.getLocalRotation().clone();
+        currentAxis = Vector3f.UNIT_XYZ;
+        pickManager = Lookup.getDefault().lookup(PickManager.class);
+        pickEnabled = false;
+    }
+
+    @Override
+    public void activate(AssetManager manager, Node toolNode, Node onTopToolNode, Spatial selectedSpatial, SceneComposerToolController toolController) {
+        super.activate(manager, toolNode, onTopToolNode, selectedSpatial, toolController); //To change body of generated methods, choose Tools | Templates.
+        hideMarker();
+        numberBuilder = new StringBuilder();
+        if (selectedSpatial == null) {
+            terminate();
+        } else {
+            init(selectedSpatial);
+        }
+    }
+
+    @Override
+    public void keyPressed(KeyInputEvent kie) {
+        if (kie.isPressed()) {
+            Lookup.getDefault().lookup(ShortcutManager.class).activateShortcut(kie);
+
+            Vector3f axis = new Vector3f();
+            boolean axisChanged = ShortcutManager.checkAxisKey(kie, axis);
+            if (axisChanged) {
+                currentAxis = axis;
+            }
+            boolean numberChanged = ShortcutManager.checkNumberKey(kie, numberBuilder);
+            boolean enterHit = ShortcutManager.checkEnterHit(kie);
+            boolean escHit = ShortcutManager.checkEscHit(kie);
+
+            if (escHit) {
+                cancel();
+            } else if (enterHit) {
+                apply();
+            } else if (axisChanged && pickEnabled) {
+                pickEnabled = false;
+                spatial.setLocalRotation(startRotation.clone());
+            } else if (axisChanged || numberChanged) {
+                //update transformation
+       /*         float number = ShortcutManager.getNumberKey(numberBuilder);
+                 Vector3f translation = currentAxis.mult(number);
+                 finalPosition = startPosition.add(translation);
+                 spatial.setLocalTranslation(finalPosition);
+                 */
+            }
+
+        }
+    }
+
+    @Override
+    public void actionPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
+        if (pressed) {
+            apply();
+        }
+    }
+
+    @Override
+    public void actionSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
+        if (pressed) {
+            cancel();
+        }
+    }
+
+    @Override
+    public void mouseMoved(Vector2f screenCoord, JmeNode rootNode, DataObject dataObject, JmeSpatial selectedSpatial) {
+
+        if (!pickEnabled) {
+            if (currentAxis.equals(Vector3f.UNIT_XYZ)) {
+                pickManager.initiatePick(toolController.getSelectedSpatial(), camera.getRotation(), SceneComposerToolController.TransformationType.camera, camera, screenCoord);
+                pickEnabled = true;
+            } else if (currentAxis.equals(Vector3f.UNIT_X)) {
+                pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_YZ, getTransformType(), camera, screenCoord);
+                pickEnabled = true;
+            } else if (currentAxis.equals(Vector3f.UNIT_Y)) {
+                pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_XZ, getTransformType(), camera, screenCoord);
+                pickEnabled = true;
+            } else if (currentAxis.equals(Vector3f.UNIT_Z)) {
+                pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_XY, getTransformType(), camera, screenCoord);
+                pickEnabled = true;
+            } else {
+                return;
+            }
+        }
+
+        if (pickManager.updatePick(camera, screenCoord)) {
+
+            Quaternion rotation = startRotation.mult(pickManager.getRotation(startRotation.inverse()));
+            toolController.getSelectedSpatial().setLocalRotation(rotation);
+            finalRotation = rotation;
+            updateToolsTransformation();
+        }
+    }
+
+    @Override
+    public void draggedPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
+        if (pressed) {
+            apply();
+        }
+    }
+
+    @Override
+    public void draggedSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
+        if (pressed) {
+            cancel();
+        }
+    }
+
+    private class RotateUndo extends AbstractUndoableSceneEdit {
+
+        private Spatial spatial;
+        private Quaternion before, after;
+
+        RotateUndo(Spatial spatial, Quaternion before, Quaternion after) {
+            this.spatial = spatial;
+            this.before = before;
+            this.after = after;
+        }
+
+        @Override
+        public void sceneUndo() {
+            spatial.setLocalRotation(before);
+            toolController.selectedSpatialTransformed();
+        }
+
+        @Override
+        public void sceneRedo() {
+            spatial.setLocalRotation(after);
+            toolController.selectedSpatialTransformed();
+        }
+    }
+}

+ 199 - 0
sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/shortcuts/ScaleShortcut.java

@@ -0,0 +1,199 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.scenecomposer.tools.shortcuts;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
+import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
+import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
+import com.jme3.gde.scenecomposer.SceneComposerToolController;
+import com.jme3.gde.scenecomposer.tools.PickManager;
+import com.jme3.input.KeyInput;
+import com.jme3.input.event.KeyInputEvent;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import org.openide.loaders.DataObject;
+import org.openide.util.Lookup;
+
+/**
+ *
+ * @author dokthar
+ */
+public class ScaleShortcut extends ShortcutTool {
+
+    private Vector3f currentAxis;
+    private StringBuilder numberBuilder;
+    private Spatial spatial;
+    private PickManager pickManager;
+    private boolean pickEnabled;
+    private Vector3f startScale;
+    private Vector3f finalScale;
+
+    @Override
+
+    public boolean isActivableBy(KeyInputEvent kie) {
+        return kie.getKeyCode() == KeyInput.KEY_S;
+    }
+
+    @Override
+    public void cancel() {
+        spatial.setLocalScale(startScale);
+        terminate();
+    }
+
+    private void apply() {
+        actionPerformed(new ScaleUndo(toolController.getSelectedSpatial(), startScale, finalScale));
+        terminate();
+    }
+
+    private void init(Spatial selectedSpatial) {
+        spatial = selectedSpatial;
+        startScale = spatial.getLocalScale().clone();
+        currentAxis = Vector3f.UNIT_XYZ;
+        pickManager = Lookup.getDefault().lookup(PickManager.class);
+        pickEnabled = false;
+    }
+
+    @Override
+    public void activate(AssetManager manager, Node toolNode, Node onTopToolNode, Spatial selectedSpatial, SceneComposerToolController toolController) {
+        super.activate(manager, toolNode, onTopToolNode, selectedSpatial, toolController); //To change body of generated methods, choose Tools | Templates.
+        hideMarker();
+        numberBuilder = new StringBuilder();
+        if (selectedSpatial == null) {
+            terminate();
+        } else {
+            init(selectedSpatial);
+        }
+    }
+
+    @Override
+    public void keyPressed(KeyInputEvent kie) {
+        if (kie.isPressed()) {
+            Lookup.getDefault().lookup(ShortcutManager.class).activateShortcut(kie);
+
+            Vector3f axis = new Vector3f();
+            boolean axisChanged = ShortcutManager.checkAxisKey(kie, axis);
+            if (axisChanged) {
+                currentAxis = axis;
+            }
+            boolean numberChanged = ShortcutManager.checkNumberKey(kie, numberBuilder);
+            boolean enterHit = ShortcutManager.checkEnterHit(kie);
+            boolean escHit = ShortcutManager.checkEscHit(kie);
+
+            if (escHit) {
+                cancel();
+            } else if (enterHit) {
+                apply();
+            } else if (axisChanged && pickEnabled) {
+                pickEnabled = false;
+            } else if (axisChanged || numberChanged) {
+                //update transformation
+       /*         float number = ShortcutManager.getNumberKey(numberBuilder);
+                 Vector3f translation = currentAxis.mult(number);
+                 finalPosition = startPosition.add(translation);
+                 spatial.setLocalTranslation(finalPosition);
+                 */
+            }
+
+        }
+    }
+
+    @Override
+    public void actionPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
+        if (pressed) {
+            apply();
+        }
+    }
+
+    @Override
+    public void actionSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
+        if (pressed) {
+            cancel();
+        }
+    }
+
+    @Override
+    public void mouseMoved(Vector2f screenCoord, JmeNode rootNode, DataObject dataObject, JmeSpatial selectedSpatial) {
+
+        if (!pickEnabled) {
+            if (currentAxis.equals(Vector3f.UNIT_XYZ)) {
+                pickManager.initiatePick(toolController.getSelectedSpatial(), camera.getRotation(), SceneComposerToolController.TransformationType.camera, camera, screenCoord);
+                pickEnabled = true;
+            } else if (currentAxis.equals(Vector3f.UNIT_X)) {
+                pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_XY, getTransformType(), camera, screenCoord);
+                pickEnabled = true;
+            } else if (currentAxis.equals(Vector3f.UNIT_Y)) {
+                pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_YZ, getTransformType(), camera, screenCoord);
+                pickEnabled = true;
+            } else if (currentAxis.equals(Vector3f.UNIT_Z)) {
+                pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_XZ, getTransformType(), camera, screenCoord);
+                pickEnabled = true;
+            } else {
+                return;
+            }
+        }
+
+        if (pickManager.updatePick(camera, screenCoord)) {
+            Vector3f scale = startScale;
+            if (currentAxis.equals(Vector3f.UNIT_XYZ)) {
+                Vector3f constraintAxis = pickManager.getStartOffset().normalize();
+                float diff = pickManager.getTranslation(constraintAxis).dot(constraintAxis);
+                diff *= 0.5f;
+                scale = startScale.add(new Vector3f(diff, diff, diff));
+            } else {
+                // Get the translation in the spatial Space
+                Quaternion worldToSpatial = toolController.getSelectedSpatial().getWorldRotation().inverse();
+                Vector3f diff = pickManager.getTranslation(worldToSpatial.mult(currentAxis));
+                diff.multLocal(0.5f);
+                scale = startScale.add(diff);
+            }
+            finalScale = scale;
+            toolController.getSelectedSpatial().setLocalScale(scale);
+            updateToolsTransformation();
+        }
+    }
+
+    @Override
+    public void draggedPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
+        if (pressed) {
+            apply();
+        }
+    }
+
+    @Override
+    public void draggedSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
+        if (pressed) {
+            cancel();
+        }
+    }
+
+    private class ScaleUndo extends AbstractUndoableSceneEdit {
+
+        private Spatial spatial;
+        private Vector3f before, after;
+
+        ScaleUndo(Spatial spatial, Vector3f before, Vector3f after) {
+            this.spatial = spatial;
+            this.before = before;
+            this.after = after;
+        }
+
+        @Override
+        public void sceneUndo() {
+            spatial.setLocalScale(before);
+            toolController.selectedSpatialTransformed();
+        }
+
+        @Override
+        public void sceneRedo() {
+            spatial.setLocalScale(after);
+            toolController.selectedSpatialTransformed();
+        }
+    }
+}

+ 335 - 0
sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/shortcuts/ShortcutManager.java

@@ -0,0 +1,335 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.scenecomposer.tools.shortcuts;
+
+import com.jme3.gde.scenecomposer.SceneEditTool;
+import com.jme3.input.KeyInput;
+import com.jme3.input.event.KeyInputEvent;
+import com.jme3.math.Vector3f;
+import java.util.ArrayList;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author dokthar
+ */
+@ServiceProvider(service = ShortcutManager.class)
+public class ShortcutManager {
+
+    private ShortcutTool currentShortcut;
+    private ArrayList<ShortcutTool> shortcutList;
+    private boolean ctrlDown = false;
+    private boolean shiftDown = false;
+    private boolean altDown = false;
+
+    public ShortcutManager() {
+        shortcutList = new ArrayList<ShortcutTool>();
+        shortcutList.add(new MoveShortcut());
+        shortcutList.add(new RotateShortcut());
+        shortcutList.add(new ScaleShortcut());
+        shortcutList.add(new DuplicateShortcut());
+        shortcutList.add(new DeleteShortcut());
+    }
+
+    /*
+     Methodes 
+     */
+    /**
+     * This MUST be called by the shortcut tool once the modifications are done.
+     */
+    public void terminate() {
+        currentShortcut = null;
+    }
+
+    /**
+     *
+     * @return true if a shortCutTool is active, else return false.
+     */
+    public boolean isActive() {
+        return currentShortcut != null;
+    }
+
+    /**
+     * @return the ctrlDown
+     */
+    public boolean isCtrlDown() {
+        return ctrlDown;
+    }
+
+    /**
+     * @return the shiftDown
+     */
+    public boolean isShiftDown() {
+        return shiftDown;
+    }
+
+    /**
+     * @return the altDown
+     */
+    public boolean isAltDown() {
+        return altDown;
+    }
+
+    /**
+     * Set the current shortcut to <code>shortcut</code>. cancel the current
+     * shortcut if it was still active
+     *
+     * @param shortcut the ShortCutTool to set
+     */
+    public void setShortCut(ShortcutTool shortcut) {
+        if (isActive()) {
+            currentShortcut.cancel();
+        }
+        currentShortcut = shortcut;
+    }
+
+    /**
+     * Get the shortcut that can be enable with the given kei, the current
+     * shortcut cannot be enable twice. This also check for command key used to
+     * provide isCtrlDown(), isShiftDown() and isAltDown().
+     *
+     * @param kie the KeyInputEvent
+     * @return the activable shortcut else return null
+     */
+    public ShortcutTool getActivableShortcut(KeyInputEvent kie) {
+        if (checkCommandeKey(kie)) {
+            return null;
+        }
+        for (ShortcutTool s : shortcutList) {
+            if (s != currentShortcut) {
+                if (s.isActivableBy(kie)) {
+                    return s;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     *
+     * @return the current active shortcut
+     */
+    public ShortcutTool getActiveShortcut() {
+        return currentShortcut;
+    }
+
+    /**
+     *
+     * @param kie the KeyInputEvent
+     * @return true if the given Kei can enable a sortcut, else false
+     */
+    public boolean canActivateShortcut(KeyInputEvent kie) {
+        return getActivableShortcut(kie) != null;
+    }
+
+    /**
+     * Set the current shortcut with the shortcut one that can be enable with
+     * the given key
+     *
+     * @param kie the KeyInputEvent
+     * @return true is the shortcut changed, else false
+     */
+    public boolean activateShortcut(KeyInputEvent kie) {
+        ShortcutTool newShortcut = getActivableShortcut(kie);
+        if (newShortcut != null) {
+            currentShortcut = newShortcut;
+        }
+        return newShortcut != null;
+    }
+
+    /**
+     * This should be called to trigger the currentShortcut.keyPressed() method.
+     * This also check for command key used to provide isCtrlDown(),
+     * isShiftDown() and isAltDown().
+     *
+     * @param kie
+     */
+    public void doKeyPressed(KeyInputEvent kie) {
+        if (checkCommandeKey(kie)) {
+            //return;
+        } else if (isActive()) {
+            currentShortcut.keyPressed(kie);
+        }
+    }
+
+    private boolean checkCommandeKey(KeyInputEvent kie) {
+        if (checkCtrlHit(kie)) {
+            ctrlDown = kie.isPressed();
+            return true;
+        } else if (checkAltHit(kie)) {
+            altDown = kie.isPressed();
+            return true;
+        } else if (checkShiftHit(kie)) {
+            shiftDown = kie.isPressed();
+            return true;
+        }
+        return false;
+    }
+
+    /*
+     STATIC
+     */
+    /**
+     *
+     * @param kie
+     * @return true if the given kie is KEY_RETURN
+     */
+    public static boolean checkEnterHit(KeyInputEvent kie) {
+        if (kie.getKeyCode() == KeyInput.KEY_RETURN) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     *
+     * @param kie
+     * @return true if the given kie is KEY_ESCAPE
+     */
+    public static boolean checkEscHit(KeyInputEvent kie) {
+        if (kie.getKeyCode() == KeyInput.KEY_ESCAPE) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     *
+     * @param kie
+     * @return true if the given kie is KEY_LCONTROL || KEY_RCONTROL
+     */
+    public static boolean checkCtrlHit(KeyInputEvent kie) {
+        if (kie.getKeyCode() == KeyInput.KEY_LCONTROL || kie.getKeyCode() == KeyInput.KEY_RCONTROL) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     *
+     * @param kie
+     * @return true if the given kie is KEY_LSHIFT || KEY_RSHIFT
+     */
+    public static boolean checkShiftHit(KeyInputEvent kie) {
+        if (kie.getKeyCode() == KeyInput.KEY_LSHIFT || kie.getKeyCode() == KeyInput.KEY_RSHIFT) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     *
+     * @param kie
+     * @return true if the given kie is KEY_LMENU || KEY_RMENU
+     */
+    public static boolean checkAltHit(KeyInputEvent kie) {
+        if (kie.getKeyCode() == KeyInput.KEY_LMENU || kie.getKeyCode() == KeyInput.KEY_RMENU) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * store the number kie into the numberBuilder
+     *
+     * @param kie
+     * @param numberBuilder
+     * @return true if the given kie is handled as a number key event
+     */
+    public static boolean checkNumberKey(KeyInputEvent kie, StringBuilder numberBuilder) {
+        if (kie.getKeyCode() == KeyInput.KEY_MINUS) {
+            if (numberBuilder.length() > 0) {
+                if (numberBuilder.charAt(0) == '-') {
+                    numberBuilder.replace(0, 1, "");
+                } else {
+                    numberBuilder.insert(0, '-');
+                }
+            } else {
+                numberBuilder.append('-');
+            }
+            return true;
+        } else if (kie.getKeyCode() == KeyInput.KEY_0 || kie.getKeyCode() == KeyInput.KEY_NUMPAD0) {
+            numberBuilder.append('0');
+            return true;
+        } else if (kie.getKeyCode() == KeyInput.KEY_1 || kie.getKeyCode() == KeyInput.KEY_NUMPAD1) {
+            numberBuilder.append('1');
+            return true;
+        } else if (kie.getKeyCode() == KeyInput.KEY_2 || kie.getKeyCode() == KeyInput.KEY_NUMPAD2) {
+            numberBuilder.append('2');
+            return true;
+        } else if (kie.getKeyCode() == KeyInput.KEY_3 || kie.getKeyCode() == KeyInput.KEY_NUMPAD3) {
+            numberBuilder.append('3');
+            return true;
+        } else if (kie.getKeyCode() == KeyInput.KEY_4 || kie.getKeyCode() == KeyInput.KEY_NUMPAD4) {
+            numberBuilder.append('4');
+            return true;
+        } else if (kie.getKeyCode() == KeyInput.KEY_5 || kie.getKeyCode() == KeyInput.KEY_NUMPAD5) {
+            numberBuilder.append('5');
+            return true;
+        } else if (kie.getKeyCode() == KeyInput.KEY_6 || kie.getKeyCode() == KeyInput.KEY_NUMPAD6) {
+            numberBuilder.append('6');
+            return true;
+        } else if (kie.getKeyCode() == KeyInput.KEY_7 || kie.getKeyCode() == KeyInput.KEY_NUMPAD7) {
+            numberBuilder.append('7');
+            return true;
+        } else if (kie.getKeyCode() == KeyInput.KEY_8 || kie.getKeyCode() == KeyInput.KEY_NUMPAD8) {
+            numberBuilder.append('8');
+            return true;
+        } else if (kie.getKeyCode() == KeyInput.KEY_9 || kie.getKeyCode() == KeyInput.KEY_NUMPAD9) {
+            numberBuilder.append('9');
+            return true;
+        } else if (kie.getKeyCode() == KeyInput.KEY_PERIOD) {
+            if (numberBuilder.indexOf(".") == -1) { // if it doesn't exist yet
+                if (numberBuilder.length() == 0
+                        || (numberBuilder.length() == 1 && numberBuilder.charAt(0) == '-')) {
+                    numberBuilder.append("0.");
+                } else {
+                    numberBuilder.append(".");
+                }
+            }
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     *
+     * @param numberBuilder the StringBuilder storing the float number
+     * @return the float value created from the given StringBuilder
+     */
+    public static float getNumberKey(StringBuilder numberBuilder) {
+        if (numberBuilder.length() == 0) {
+            return 0;
+        } else {
+            return new Float(numberBuilder.toString());
+        }
+    }
+
+    /**
+     * Check for axis input for key X,Y,Z and store the corresponding UNIT_ into
+     * the axisStore
+     *
+     * @param kie
+     * @param axisStore
+     * @return true if the given kie is handled as a Axis input
+     */
+    public static boolean checkAxisKey(KeyInputEvent kie, Vector3f axisStore) {
+        if (kie.getKeyCode() == KeyInput.KEY_X) {
+            axisStore.set(Vector3f.UNIT_X);
+            return true;
+        } else if (kie.getKeyCode() == KeyInput.KEY_Y) {
+            axisStore.set(Vector3f.UNIT_Y);
+            return true;
+        } else if (kie.getKeyCode() == KeyInput.KEY_Z) {
+            axisStore.set(Vector3f.UNIT_Z);
+            return true;
+        }
+        return false;
+    }
+
+}

+ 29 - 0
sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/shortcuts/ShortcutTool.java

@@ -0,0 +1,29 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.scenecomposer.tools.shortcuts;
+
+import com.jme3.gde.scenecomposer.SceneEditTool;
+import com.jme3.input.event.KeyInputEvent;
+import org.openide.util.Lookup;
+
+/**
+ *
+ * @author dokthar
+ */
+public abstract class ShortcutTool extends SceneEditTool {
+
+    public abstract boolean isActivableBy(KeyInputEvent kie);
+
+    public abstract void cancel();
+
+    protected final void terminate() {
+        Lookup.getDefault().lookup(ShortcutManager.class).terminate();
+    }
+
+    @Override
+    public abstract void keyPressed(KeyInputEvent kie);
+   
+}