PageRenderTime 57ms CodeModel.GetById 14ms app.highlight 37ms RepoModel.GetById 2ms app.codeStats 0ms

/src/org/mt4j/input/inputProcessors/componentProcessors/tapAndHoldProcessor/TapAndHoldProcessor.java

http://mt4j.googlecode.com/
Java | 345 lines | 189 code | 56 blank | 100 comment | 18 complexity | 00e3595ae717e74b44ee4886f9a55f2b MD5 | raw file
  1/***********************************************************************
  2 * mt4j Copyright (c) 2008 - 2009 Christopher Ruff, Fraunhofer-Gesellschaft All rights reserved.
  3 *  
  4 *   This program is free software: you can redistribute it and/or modify
  5 *   it under the terms of the GNU General Public License as published by
  6 *   the Free Software Foundation, either version 3 of the License, or
  7 *   (at your option) any later version.
  8 *
  9 *   This program is distributed in the hope that it will be useful,
 10 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 11 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12 *   GNU General Public License for more details.
 13 *
 14 *   You should have received a copy of the GNU General Public License
 15 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 16 *
 17 ***********************************************************************/
 18package org.mt4j.input.inputProcessors.componentProcessors.tapAndHoldProcessor;
 19
 20import java.util.List;
 21
 22import org.mt4j.MTApplication;
 23import org.mt4j.components.MTCanvas;
 24import org.mt4j.components.interfaces.IMTComponent3D;
 25import org.mt4j.input.inputData.AbstractCursorInputEvt;
 26import org.mt4j.input.inputData.InputCursor;
 27import org.mt4j.input.inputProcessors.IInputProcessor;
 28import org.mt4j.input.inputProcessors.MTGestureEvent;
 29import org.mt4j.input.inputProcessors.componentProcessors.AbstractComponentProcessor;
 30import org.mt4j.input.inputProcessors.componentProcessors.AbstractCursorProcessor;
 31import org.mt4j.sceneManagement.IPreDrawAction;
 32import org.mt4j.util.math.Vector3D;
 33
 34/**
 35 * The Class TapAndHoldProcessor. Multi-Touch gesture which is triggered
 36 * after touching and resting the finger on the same spot for some time.
 37 * Only works with the first cursor entering the component. Cancels if the finger is moved beyond
 38 * a specified threshold distance.
 39 * Fires TapAndHoldEvent gesture events.
 40 * 
 41 * @author Christopher Ruff
 42 */
 43public class TapAndHoldProcessor extends AbstractCursorProcessor implements IPreDrawAction{
 44	
 45	/** The applet. */
 46	private MTApplication app;
 47	
 48	/** The max finger up dist. */
 49	private float maxFingerUpDist;
 50	
 51	/** The button down screen pos. */
 52	private Vector3D buttonDownScreenPos;
 53
 54	/** The tap start time. */
 55	private long tapStartTime;
 56	
 57	/** The tap time. */
 58	private int holdTime;
 59	
 60	private IMTComponent3D lastCurrentTarget;
 61	
 62	//TODO atm this only allows 1 tap on 1 component
 63	//if we want more we have to do different (save each cursor to each start time etc, dont relock other cursors)
 64	
 65	
 66	/**
 67	 * Instantiates a new tap processor.
 68	 * @param pa the pa
 69	 */
 70	public TapAndHoldProcessor(MTApplication pa) {
 71		this(pa, 1800, false);
 72	}
 73	
 74	public TapAndHoldProcessor(MTApplication pa, int duration){
 75		this(pa, duration, false);
 76	}
 77	
 78	/**
 79	 * Instantiates a new tap and hold processor.
 80	 * @param pa the pa
 81	 * @param duration the duration
 82	 * @param stopPropatation 
 83	 */
 84	public TapAndHoldProcessor(MTApplication pa, int duration, boolean stopPropatation) {
 85		super(stopPropatation);
 86		this.app = pa;
 87		
 88		this.maxFingerUpDist = 17.0f;
 89		this.holdTime = duration;
 90		
 91		this.setLockPriority(1);
 92		this.setDebug(false);
 93//		logger.setLevel(Level.DEBUG);
 94	}
 95	
 96
 97	/* (non-Javadoc)
 98	 * @see org.mt4j.input.inputProcessors.componentProcessors.AbstractCursorProcessor#cursorStarted(org.mt4j.input.inputData.InputCursor, org.mt4j.input.inputData.AbstractCursorInputEvt)
 99	 */
100	@Override
101	public void cursorStarted(InputCursor c, AbstractCursorInputEvt positionEvent) {
102		List<InputCursor> locked = getLockedCursors();
103		
104		if (locked.size() >=1){
105			//already in progress
106		}else{
107			if (getFreeComponentCursors().size() == 1){ //Only start if this is the first cursor on the component
108				if (this.getLock(c)){//See if we can obtain a lock on this cursor (depends on the priority)
109					logger.debug(this.getName() + " successfully locked cursor (id:" + c.getId() + ")");
110					buttonDownScreenPos = c.getPosition();
111					tapStartTime = System.currentTimeMillis();
112					//Save the last used currenttarget so we can use that during the updates in pre() 
113					this.lastCurrentTarget = positionEvent.getCurrentTarget();
114					this.fireGestureEvent(new TapAndHoldEvent(this, MTGestureEvent.GESTURE_STARTED, positionEvent.getCurrentTarget(), c, false, c.getPosition(), this.holdTime, 0, 0));
115					try {
116//						applet.registerPre(this);
117						app.registerPreDrawAction(this);
118					} catch (Exception e) {
119						System.err.println(e.getMessage());
120					}
121				}
122			}
123		}
124	}
125
126	//problem: mouse does only send update evt if mouse is actually dragged
127	//idea: registerPre and check if we have  alocked cursor and the times is up
128	//then do the checking
129	
130	/**
131	 * Pre.
132	 */
133	public void pre(){
134		List<InputCursor> locked = getLockedCursors();
135		if (locked.size() == 1){
136			InputCursor c = locked.get(0);
137			IMTComponent3D comp = c.getTarget();
138//			IMTComponent3D currentTarget = c.getCurrentEvent().getCurrentTarget(); //FIXME this will often return the wrong target since we are not in a processInputEvent() method!
139			IMTComponent3D currentTarget = lastCurrentTarget;
140			
141			long nowTime = System.currentTimeMillis();
142			long elapsedTime = nowTime - this.tapStartTime;
143			Vector3D screenPos = c.getPosition();
144			float normalized = (float)elapsedTime / (float)this.holdTime;
145			
146			if (elapsedTime >= holdTime){
147				normalized = 1;
148				logger.debug("TIME PASSED!");
149				Vector3D intersection = getIntersection(app, comp, c);
150				//logger.debug("Distance between buttondownScreenPos: " + buttonDownScreenPos + " and upScrPos: " + buttonUpScreenPos +  " is: " + Vector3D.distance(buttonDownScreenPos, buttonUpScreenPos));
151				if ( (intersection != null || comp instanceof MTCanvas) //hack - at canvas no intersection..
152						&& 
153					Vector3D.distance2D(buttonDownScreenPos, screenPos) <= this.maxFingerUpDist
154				){
155					this.fireGestureEvent(new TapAndHoldEvent(this, MTGestureEvent.GESTURE_ENDED, currentTarget, c, true, screenPos, this.holdTime, elapsedTime, normalized));
156				}else{
157					logger.debug("DISTANCE TOO FAR OR NO INTERSECTION");
158					this.fireGestureEvent(new TapAndHoldEvent(this, MTGestureEvent.GESTURE_ENDED, currentTarget, c, false, screenPos, this.holdTime, elapsedTime, normalized));
159				}
160				this.unLock(c); 
161				try {
162//					app.unregisterPre(this);
163					app.unregisterPreDrawAction(this);
164				} catch (Exception e) {
165					System.err.println(e.getMessage());
166				}
167			}else{
168				this.fireGestureEvent(new TapAndHoldEvent(this, MTGestureEvent.GESTURE_UPDATED, currentTarget, c, false, screenPos, this.holdTime, elapsedTime, normalized));
169			}
170		}
171	}
172
173	/* (non-Javadoc)
174	 * @see org.mt4j.input.inputProcessors.componentProcessors.AbstractCursorProcessor#cursorUpdated(org.mt4j.input.inputData.InputCursor, org.mt4j.input.inputData.AbstractCursorInputEvt)
175	 */
176	@Override
177	public void cursorUpdated(InputCursor c, AbstractCursorInputEvt positionEvent) {
178		List<InputCursor> locked = getLockedCursors();
179		if (locked.contains(c)){
180			long nowTime = System.currentTimeMillis();
181			long elapsedTime = nowTime - this.tapStartTime;
182			Vector3D screenPos = c.getPosition();
183			float normalized = (float)elapsedTime / (float)this.holdTime;
184
185			//logger.debug("Distance between buttondownScreenPos: " + buttonDownScreenPos + " and upScrPos: " + buttonUpScreenPos +  " is: " + Vector3D.distance(buttonDownScreenPos, buttonUpScreenPos));
186			if (Vector3D.distance2D(buttonDownScreenPos, screenPos) > this.maxFingerUpDist){
187				logger.debug("DISTANCE TOO FAR OR NO INTERSECTION");
188				this.unLock(c); 
189				try {
190//					app.unregisterPre(this);
191					app.unregisterPreDrawAction(this);
192				} catch (Exception e) {
193					System.err.println(e.getMessage());
194				}
195				this.fireGestureEvent(new TapAndHoldEvent(this, MTGestureEvent.GESTURE_ENDED, positionEvent.getCurrentTarget(), c, false, screenPos, this.holdTime, elapsedTime, normalized));
196			}
197		}
198	}
199	
200	/* (non-Javadoc)
201	 * @see org.mt4j.input.inputProcessors.componentProcessors.AbstractCursorProcessor#cursorEnded(org.mt4j.input.inputData.InputCursor, org.mt4j.input.inputData.AbstractCursorInputEvt)
202	 */
203	@Override
204	public void cursorEnded(InputCursor c, AbstractCursorInputEvt positionEvent) {
205		logger.debug(this.getName() + " INPUT_ENDED RECIEVED - MOTION: " + c.getId());
206		
207		List<InputCursor> locked = getLockedCursors();
208		if (locked.contains(c)){
209			long nowTime = System.currentTimeMillis();
210			long elapsedTime = nowTime - this.tapStartTime;
211			float normalized = (float)elapsedTime / (float)this.holdTime;
212			
213			List<InputCursor> free = getFreeComponentCursors();
214			if (free.size() > 0){ 			//check if there are other cursors on the component, we could use 
215				InputCursor otherCursor = free.get(0); 
216				if (this.canLock(otherCursor) 
217						&& 
218					Vector3D.distance2D(buttonDownScreenPos, otherCursor.getPosition()) <= this.maxFingerUpDist)
219				{ 	//Check if we have the priority to use this other cursor and if cursor is in range
220					this.getLock(otherCursor);
221					buttonDownScreenPos = otherCursor.getPosition();
222				}else{
223					//Other cursor has higher prior -> end this gesture
224					this.fireGestureEvent(new TapAndHoldEvent(this, MTGestureEvent.GESTURE_ENDED, positionEvent.getCurrentTarget(), c, false,  c.getPosition(), this.holdTime, elapsedTime, normalized));
225					try {
226//						app.unregisterPre(this);
227						app.unregisterPreDrawAction(this);
228					} catch (Exception e) {
229						System.err.println(e.getMessage());
230					}
231				}
232			}else{
233				//We have no other cursor to continue gesture -> end
234				this.fireGestureEvent(new TapAndHoldEvent(this, MTGestureEvent.GESTURE_ENDED, positionEvent.getCurrentTarget(), c, false,  c.getPosition(), this.holdTime, elapsedTime, normalized));
235				try {
236//					app.unregisterPre(this);
237					app.unregisterPreDrawAction(this);
238				} catch (Exception e) {
239					System.err.println(e.getMessage());
240				}
241			}
242			this.unLock(c); 
243		}	
244	}
245
246
247
248	@Override
249	public void cursorLocked(InputCursor c, IInputProcessor lockingAnalyzer) {
250		if (lockingAnalyzer instanceof AbstractComponentProcessor){
251			logger.debug(this.getName() + " Recieved MOTION LOCKED by (" + ((AbstractComponentProcessor)lockingAnalyzer).getName()  + ") - cursor ID: " + c.getId());
252		}else{
253			logger.debug(this.getName() + " Recieved MOTION LOCKED by higher priority signal - cursor ID: " + c.getId());
254		}
255
256		long nowTime = System.currentTimeMillis();
257		long elapsedTime = nowTime - this.tapStartTime;
258		float normalized = (float)elapsedTime / (float)this.holdTime;
259
260		this.fireGestureEvent(new TapAndHoldEvent(this, MTGestureEvent.GESTURE_ENDED, c.getCurrentEvent().getCurrentTarget(), c, false, c.getPosition(), this.holdTime, elapsedTime, normalized));
261
262		try {
263//			app.unregisterPre(this);
264			app.unregisterPreDrawAction(this);
265		} catch (Exception e) {
266			System.err.println(e.getMessage());
267		}
268
269		logger.debug(this.getName() + " cursor:" + c.getId() + " MOTION LOCKED. Was an active cursor in this gesture!");
270	}
271
272
273
274	@Override
275	public void cursorUnlocked(InputCursor c) {
276		//TAP AND HOLD IS NOT RESUMABLE 
277	}
278
279	
280	
281	/**
282	 * Gets the max finger up dist.
283	 * 
284	 * @return the max finger up dist
285	 */
286	public float getMaxFingerUpDist() {
287		return maxFingerUpDist;
288	}
289
290
291	/**
292	 * Sets the maximum allowed distance of the position
293	 * of the finger_down event and the finger_up event
294	 * that fires a click event
295	 * <br>This ensures that a click event is only raised
296	 * if the finger didnt move that far during the click.
297	 * 
298	 * @param maxFingerUpDist the max finger up dist
299	 */
300	public void setMaxFingerUpDist(float maxFingerUpDist) {
301		this.maxFingerUpDist = maxFingerUpDist;
302	}
303	
304	
305	
306	
307	/**
308	 * Gets the time (in ms.) needed to hold to successfully tap&hold.
309	 * 
310	 * @return the Hold time
311	 */
312	public long getHoldTime() {
313		return this.holdTime;
314	}
315
316
317
318	/**
319	 * Sets the holding time for the gesture.
320	 *
321	 * @param tapTime the new hold time
322	 */
323	public void setHoldTime(int tapTime) {
324		this.holdTime = tapTime;
325	}
326
327
328
329	/* (non-Javadoc)
330	 * @see org.mt4j.input.inputProcessors.componentProcessors.AbstractComponentProcessor#getName()
331	 */
332	@Override
333	public String getName() {
334		return "tap and hold processor";
335	}
336
337	public boolean isLoop() {
338		return true;
339	}
340
341	public void processAction() {
342		pre();
343	}
344
345}