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