package com.drawbridge.dm.animation; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import com.drawbridge.dm.DMImageFactory.DMImage; import com.drawbridge.dm.DMObject; import com.drawbridge.dm.DMSimpleObject; import com.drawbridge.jsengine.JsEngine; import com.drawbridge.jsengine.jsobjects.JSNativeFunction; import com.drawbridge.text.TextPanel; import com.drawbridge.utils.AnalyticUtils; import com.drawbridge.utils.JSUtils; import com.drawbridge.utils.Utils; import com.drawbridge.vl.VLPanel; /** * Animation object that describes an animation with a given set of objects. * * @author Alistair Stead * */ public class AnimationManager { private List<DMObject> mAnimationObjects; private AnimationListener mListener; private Long mStartTimestamp = null; public final static int TYPE_PROVISIONAL = 1; public final static int TYPE_CURRENT = 2; public enum AnimationState { STATE_PLAYING, STATE_STOPPED, STATE_RECORDING, } private AnimationState mState; private NativeAnimationItem mPreviousAnimationItem = null; private String mCurrentAnimationCode = ""; private String mProvisionalCode = ""; public AnimationManager(AnimationListener parent, LinkedList<DMObject> simpleObjects) { mListener = parent; mAnimationObjects = simpleObjects; reset(); mState = AnimationState.STATE_STOPPED; } private void addAction(DMObject object, int x, int y, int w, int h, long timeDelay) { if (mStartTimestamp == null) { mStartTimestamp = System.currentTimeMillis(); } mCurrentAnimationCode += object.getName() + ".setTweenPosition(" + x + ", " + y + ");\n" + object.getName() + ".setTweenSize(" + w + ", " + h + ");\n" + "setTimeToDestination(" + Utils.toDecimalPlace((double)timeDelay / 1000, 1) + ");\n\n"; mPreviousAnimationItem = new NativeAnimationItem(object, x, y, w, h, ((double)timeDelay / 1000)); Utils.out.println(this.getClass(), "Add action set text"); if (TextPanel.hasInstance()) { TextPanel.getInstance().setText(this.getClass(), mCurrentAnimationCode); TextPanel.getInstance().moveToBottom(); } else if(VLPanel.hasInstance()){ JsEngine.getInstance().executeCode(null, mCurrentAnimationCode , false); } if (VLPanel.hasInstance()) VLPanel.getInstance().moveToBottom(); } private void setProvisionalAction(DMObject object, int x, int y, int w, int h, long timeDelay) { mProvisionalCode = mCurrentAnimationCode; mProvisionalCode += object.getName() + ".setTweenPosition(" + x + ", " + y + ");\n" + object.getName() + ".setTweenSize(" + w + ", " + h + ");\n" + "setTimeToDestination(" + Utils.toDecimalPlace((double)timeDelay / 1000, 1) + ");\n\n"; if (TextPanel.hasInstance()) { TextPanel.getInstance().setText(this.getClass(), mProvisionalCode); TextPanel.getInstance().moveToBottom(); TextPanel.getInstance().getDocument().updateUIOnChange(true); } else if(VLPanel.hasInstance()){ JsEngine.getInstance().executeCode(null, mProvisionalCode , false); } if (VLPanel.hasInstance()) VLPanel.getInstance().moveToBottom(); } public void reset() { mStartTimestamp = null; mPreviousAnimationItem = null; } public AnimationState moveToNextState() { mState = peekAtNextState(); AnalyticUtils.recordAnimationStateChange(mState); switch (mState) { case STATE_PLAYING: play(); break; case STATE_RECORDING: if(TextPanel.hasInstance()) mCurrentAnimationCode = TextPanel.getInstance().getDocument().getText() + "\n\n"; else if(VLPanel.hasInstance()) mCurrentAnimationCode = VLPanel.getInstance().mCanvas.getModel().generateCodeFromBlocks(VLPanel.getInstance().mCanvas.mViewGrid) + "\n\n"; else throw new RuntimeException("No language panels available to execute animation"); for (DMObject obj : mAnimationObjects) { obj.addMouseListener(mAnimationMA); obj.addMouseMotionListener(mAnimationMA); } reset(); break; case STATE_STOPPED: for (DMObject obj : mAnimationObjects) { obj.removeMouseListener(mAnimationMA); obj.removeMouseMotionListener(mAnimationMA); } break; } mListener.onAnimationStateChange(); return mState; } public AnimationState peekAtNextState() { AnimationState nextState = mState; switch (mState) { case STATE_PLAYING: nextState = AnimationState.STATE_STOPPED; break; case STATE_STOPPED: if (mPreviousAnimationItem != null) nextState = AnimationState.STATE_PLAYING; else nextState = AnimationState.STATE_RECORDING; break; case STATE_RECORDING: nextState = AnimationState.STATE_STOPPED; break; } return nextState; } public AnimationState getState() { return mState; } @Override public String toString() { String result = "Animation: \n"; if (mPreviousAnimationItem != null) result += mPreviousAnimationItem.toString(); return result; } // Mouse listener that is bound to each object on record, // and unbound on stop. MouseAdapter mAnimationMA = new MouseAdapter() { private boolean dragged = false; @Override public void mousePressed(MouseEvent me) { dragged = false; Utils.out.println(this.getClass(), "MouseListener Animation MousePressed"); if (mStartTimestamp == null) { mStartTimestamp = System.currentTimeMillis(); } } @Override public void mouseDragged(MouseEvent me) { dragged = true; if (getState() == AnimationState.STATE_RECORDING) { long currentTime = System.currentTimeMillis(); long timeDelay = currentTime - mStartTimestamp; DMObject source = (DMObject) me.getSource(); if (source instanceof DMImage) setProvisionalAction(source, source.getX(), source.getY(), source.getWidth(), source.getHeight(), timeDelay); else { DMSimpleObject dmso = (DMSimpleObject) source; setProvisionalAction(source, dmso.getCounterpart().getX(), dmso.getCounterpart().getY(), dmso.getCounterpart().getWidth(), dmso.getCounterpart().getHeight(), timeDelay); } } } @Override public void mouseReleased(MouseEvent me) { if (getState() == AnimationState.STATE_RECORDING && dragged) { long currentTime = System.currentTimeMillis(); long timeDelay = currentTime - mStartTimestamp; mStartTimestamp = currentTime; DMObject source = (DMObject) me.getSource(); if (source instanceof DMImage) addAction(source, source.getX(), source.getY(), source.getWidth(), source.getHeight(), timeDelay); else { DMSimpleObject dmso = (DMSimpleObject) source; addAction(source, dmso.getCounterpart().getX(), dmso.getCounterpart().getY(), dmso.getCounterpart().getWidth(), dmso.getCounterpart().getHeight(), timeDelay); } } dragged = false; } }; private void play() { // Reset object locations & execute the code here Iterator<DMImage> it = getImages().iterator(); while (it.hasNext()) { DMObject obj = it.next(); obj.setLocation(obj.getPreferredLocation()); obj.setSize(obj.getPreferredSize()); } // Reset all the animation tracking variables JSNativeFunction.animationItems = new LinkedList<NativeAnimationItem>(); if (JSNativeFunction.processor.isAlive()) JSNativeFunction.processor.stopAnimation(); JSNativeFunction.processor = new NativeAnimationProcessor(); JSNativeFunction.tweensCalled = 0; JSNativeFunction.tweensProcessed = -1; Utils.out.println(this.getClass(), "Executing/Playing from Play() in AnimationCodeGenerator.java"); if(TextPanel.hasInstance()) JsEngine.getInstance().executeCode(TextPanel.getInstance(), TextPanel.getInstance().getDocument().getText(), true); else if(VLPanel.hasInstance()) JsEngine.getInstance().executeCode(VLPanel.getInstance(), VLPanel.getInstance().mCanvas.getModel().generateCodeFromBlocks(VLPanel.getInstance().mCanvas.mViewGrid), true); else throw new RuntimeException("Could not execute animation without code representation!"); } public interface AnimationListener { public void onAnimationStateChange(); } // This method is dangerous public List<DMImage> getImages() { LinkedList<DMImage> images = new LinkedList<DMImage>(); for (DMObject obj : mAnimationObjects) { if (obj instanceof DMSimpleObject) images.add(((DMSimpleObject) obj).getCounterpart()); } return images; } /** * Generates code based on a given animation sequence. * * @param mAnimation * @return */ public String generateAnimationCode(int type) { String boilerplateCode = JSUtils.generateCanvasJS(); String result = boilerplateCode + "\n" + JSUtils.generateDMImageJS(getImages()) + "\n\n// Do Animation\n"; if (type == TYPE_CURRENT) result += mCurrentAnimationCode; else result += mProvisionalCode; return result; } public String getRawCode() { return mCurrentAnimationCode; } }