PageRenderTime 77ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/src/com/wagerfield/events/ModMouseEvent.as

https://github.com/wagerfield/girder
ActionScript | 591 lines | 369 code | 87 blank | 135 comment | 65 complexity | 0fdf1d7eb4c5c9271ffc2e0bd1acda9e MD5 | raw file
  1. /**
  2. * Copyright (C) 2011 by Matthew Wagerfield
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. * THE SOFTWARE.
  21. */
  22. package com.wagerfield.events
  23. {
  24. import com.wagerfield.utils.ExternalMouse;
  25. import flash.display.InteractiveObject;
  26. import flash.display.Sprite;
  27. import flash.display.Stage;
  28. import flash.events.Event;
  29. import flash.events.MouseEvent;
  30. import flash.geom.Point;
  31. import flash.utils.Dictionary;
  32. /**
  33. * @author Matthew Wagerfield
  34. */
  35. public class ModMouseEvent extends MouseEvent
  36. {
  37. /** Event thrown when an InteractiveObject is clicked on. */
  38. public static const CLICK:String = "ModMouseClick";
  39. /** Event thrown when an InteractiveObject is double clicked on. */
  40. public static const DOUBLE_CLICK:String = "ModMouseDoubleClick";
  41. /** Event thrown when the mouse button is down on an InteractiveObject. */
  42. public static const MOUSE_DOWN:String = "ModMouseMouseDown";
  43. /** Event thrown when the mouse button is released on an InteractiveObject. */
  44. public static const MOUSE_UP:String = "ModMouseMouseUp";
  45. /** Event thrown when the mouse is rolled over an InteractiveObject. */
  46. public static const MOUSE_OVER:String = "ModMouseMouseOver";
  47. /** Event thrown when the mouse is rolled out of an InteractiveObject. */
  48. public static const MOUSE_OUT:String = "ModMouseMouseOut";
  49. /** Event thrown when the mouse moves. */
  50. public static const MOUSE_MOVE:String = "ModMouseMouseMove";
  51. /** Event thrown when the mouse wheel is rotated. */
  52. public static const MOUSE_WHEEL:String = "ModMouseMouseWheel";
  53. /** Array containing all ModMouseEvent Events. */
  54. public static const ALL_EVENTS:Array = [];
  55. private static var _init:Boolean;
  56. private static var _external:Boolean;
  57. private static var _stage:Stage;
  58. private static var _mouse:Point;
  59. private static var _cursor:Sprite;
  60. private static var _intObjArr:Array;
  61. private static var _eventDict:Dictionary;
  62. private static var _blockDict:Dictionary;
  63. private static var _buttonDict:Dictionary;
  64. private static var _ghostDict:Dictionary;
  65. private static var _overDict:Dictionary;
  66. private static var _downDict:Dictionary;
  67. private static var _skeleton:MouseEvent;
  68. /**
  69. * Modified MouseEvent that allows for complex layering of InteractiveObjects listening on the same ModMouseEvent types.
  70. *
  71. * @param type ModMouseEvent type.
  72. * @param bubbles Indicates whether an event is a bubbling event.
  73. * @param cancelable Indicates whether the behavior associated with the event can be prevented.
  74. * @param localX The horizontal coordinate at which the event occurred relative to the containing sprite.
  75. * @param localY The vertical coordinate at which the event occurred relative to the containing sprite.
  76. * @param relatedObject A reference to a display list object that is related to the event.
  77. * @param ctrlKey On Windows, indicates whether the Ctrl key is active (true) or inactive (false).
  78. * @param altKey Indicates whether the Alt key is active (true) or inactive (false).
  79. * @param shiftKey Indicates whether the Shift key is active (true) or inactive (false).
  80. * @param buttonDown Indicates whether the primary mouse button is pressed (true) or not (false).
  81. * @param delta Indicates how many lines should be scrolled for each unit the user rotates the mouse wheel.
  82. */
  83. public function ModMouseEvent(type:String = null, bubbles:Boolean = true, cancelable:Boolean = false, localX:Number = 0, localY:Number = 0, relatedObject:InteractiveObject = null, ctrlKey:Boolean = false, altKey:Boolean = false, shiftKey:Boolean = false, buttonDown:Boolean = false, delta:int = 0):void
  84. {
  85. super(type, bubbles, cancelable, localX, localY, relatedObject, ctrlKey, altKey, shiftKey, buttonDown, delta);
  86. }
  87. /**
  88. * Initialises the ModMouseEvent to interact with the stage. This only needs to be done once within an application.
  89. *
  90. * @param stage The application Stage instance.
  91. * @param external Specifies whether or not to use the ExternalMouse Class that uses JS.
  92. */
  93. public static function init(stage:Stage, external:Boolean = false):Boolean
  94. {
  95. if (!_init)
  96. {
  97. _init = true;
  98. _stage = stage;
  99. _external = external;
  100. createClasses();
  101. configClasses();
  102. mapEvents();
  103. drawGraphics();
  104. addEvents();
  105. addChildren();
  106. if (_external) ExternalMouse.init();
  107. }
  108. return _init;
  109. }
  110. private static function createClasses():void
  111. {
  112. _cursor = new Sprite();
  113. _mouse = new Point();
  114. _intObjArr = new Array();
  115. _eventDict = new Dictionary();
  116. _blockDict = new Dictionary();
  117. _buttonDict = new Dictionary();
  118. _ghostDict = new Dictionary();
  119. _overDict = new Dictionary();
  120. _downDict = new Dictionary();
  121. _skeleton = new MouseEvent("SKELETON");
  122. }
  123. private static function configClasses():void
  124. {
  125. _cursor.name = "CURSOR";
  126. ModMouseEvent.ALL_EVENTS.push(ModMouseEvent.CLICK);
  127. ModMouseEvent.ALL_EVENTS.push(ModMouseEvent.DOUBLE_CLICK);
  128. ModMouseEvent.ALL_EVENTS.push(ModMouseEvent.MOUSE_DOWN);
  129. ModMouseEvent.ALL_EVENTS.push(ModMouseEvent.MOUSE_UP);
  130. ModMouseEvent.ALL_EVENTS.push(ModMouseEvent.MOUSE_MOVE);
  131. ModMouseEvent.ALL_EVENTS.push(ModMouseEvent.MOUSE_WHEEL);
  132. ModMouseEvent.ALL_EVENTS.push(ModMouseEvent.MOUSE_OVER);
  133. ModMouseEvent.ALL_EVENTS.push(ModMouseEvent.MOUSE_OUT);
  134. }
  135. private static function mapEvents():void
  136. {
  137. addDefinition(MouseEvent.CLICK, ModMouseEvent.CLICK);
  138. addDefinition(MouseEvent.DOUBLE_CLICK, ModMouseEvent.DOUBLE_CLICK);
  139. addDefinition(MouseEvent.MOUSE_DOWN, ModMouseEvent.MOUSE_DOWN);
  140. addDefinition(MouseEvent.MOUSE_UP, ModMouseEvent.MOUSE_UP);
  141. addDefinition(MouseEvent.MOUSE_MOVE, ModMouseEvent.MOUSE_MOVE);
  142. addDefinition(MouseEvent.MOUSE_WHEEL, ModMouseEvent.MOUSE_WHEEL);
  143. addDefinition(ExternalMouseEvent.MOUSE_WHEEL, ModMouseEvent.MOUSE_WHEEL);
  144. }
  145. private static function addDefinition(mouseEvent:String, modMouseEvent:String):void
  146. {
  147. _eventDict[mouseEvent] = modMouseEvent;
  148. }
  149. private static function drawGraphics():void
  150. {
  151. with (_cursor.graphics)
  152. {
  153. clear();
  154. beginFill(0, 0);
  155. drawCircle(0, 0, 20);
  156. endFill();
  157. }
  158. }
  159. private static function addEvents():void
  160. {
  161. if (_external)
  162. {
  163. ExternalMouse.addEventListener(ExternalMouseEvent.INIT, onInit);
  164. ExternalMouse.addEventListener(ExternalMouseEvent.MOUSE_WHEEL, onMouse);
  165. }
  166. _stage.addEventListener(MouseEvent.CLICK, onMouse);
  167. _stage.addEventListener(MouseEvent.DOUBLE_CLICK, onMouse);
  168. _stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouse);
  169. _stage.addEventListener(MouseEvent.MOUSE_UP, onMouse);
  170. _stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouse);
  171. _stage.addEventListener(MouseEvent.MOUSE_WHEEL, onMouse);
  172. _stage.addEventListener(Event.ENTER_FRAME, onPaint);
  173. }
  174. private static function addChildren():void
  175. {
  176. _stage.addChild(_cursor);
  177. }
  178. private static function onInit(e:ExternalMouseEvent):void
  179. {
  180. _stage.removeEventListener(MouseEvent.MOUSE_WHEEL, onMouse);
  181. }
  182. private static function onPaint(e:Event):void
  183. {
  184. _stage.setChildIndex(_cursor, _stage.numChildren-1);
  185. _mouse.x = _stage.mouseX;
  186. _mouse.y = _stage.mouseY;
  187. _intObjArr = interactiveObjects;
  188. _cursor.x = _mouse.x;
  189. _cursor.y = _mouse.y;
  190. _cursor.buttonMode = buttonMode;
  191. checkEventListener(ModMouseEvent.MOUSE_OVER, _skeleton);
  192. checkEventListener(ModMouseEvent.MOUSE_OUT, _skeleton);
  193. }
  194. private static function onMouse(e:MouseEvent):void
  195. {
  196. if (e is ModMouseEvent) return;
  197. // Dispatches a ModMouseEvent from the stage with all MouseEvent properties included.
  198. dispatchEvent(_stage, _eventDict[e.type], e);
  199. // Check all InteractiveObjects under the mouse for the specified EventListener type.
  200. checkEventListener(_eventDict[e.type], e);
  201. }
  202. private static function get interactiveObjects():Array
  203. {
  204. var object:Object = {};
  205. var objectArr:Array = _stage.getObjectsUnderPoint(_mouse);
  206. var intObjArr:Array = new Array();
  207. var intObjDict:Dictionary = new Dictionary();
  208. // Iterate through all Objects under the mouse.
  209. for (var i:int = objectArr.length-1; i >= 0; i--)
  210. {
  211. // Set object to the current Object and check that it is not the Cursor.
  212. if ((object = objectArr[i]) != _cursor)
  213. {
  214. // Create a new Array to hold all the parents of the current Object.
  215. var parentArr:Array = new Array();
  216. do // Loop up through all the current Objects parents.
  217. {
  218. // Check to see if the Object is an InteractiveObject.
  219. if (object is InteractiveObject)
  220. {
  221. // Check to see if the InteractiveObject is unique.
  222. if (intObjDict[object] == null)
  223. {
  224. // Add the InteractiveObject to the InteractiveObject Dictionary, so that it cannot be referenced again.
  225. intObjDict[object] = "UNDER MOUSE";
  226. // Unshift the InteractiveObject onto the beginning of the Parent Array.
  227. parentArr.unshift(object);
  228. }
  229. else // If the InteractiveObject has already been referenced, break out of the DoWhile loop.
  230. {
  231. break;
  232. }
  233. }
  234. }
  235. while ((object = object.parent) != null); // Set the current Object to its Parent and check that it is not null.
  236. // Iterate through all Object's Parents and push them onto the Interactive Array.
  237. for (var j:uint = 0; j < parentArr.length; j++)
  238. {
  239. intObjArr.push(parentArr[j]);
  240. }
  241. }
  242. }
  243. return intObjArr;
  244. }
  245. private static function get buttonMode():Boolean
  246. {
  247. var buttonMode:Boolean = false;
  248. // Iterate through all InteractiveObjects under the mouse.
  249. for (var i:uint = 0; i < _intObjArr.length; i++)
  250. {
  251. // Check to see if the InteractiveObject has the buttonMode property.
  252. if (_intObjArr[i].hasOwnProperty("buttonMode"))
  253. {
  254. // Check the buttonMode Dictionary to see if the InteractiveObject is set to define the cursor buttonMode.
  255. if (_buttonDict[_intObjArr[i]])
  256. {
  257. buttonMode = _intObjArr[i].buttonMode;
  258. break;
  259. }
  260. // ...else check that the InteractiveObject's buttonMode is set to true.
  261. else if (_intObjArr[i].buttonMode)
  262. {
  263. buttonMode = _intObjArr[i].buttonMode;
  264. break;
  265. }
  266. }
  267. }
  268. return buttonMode;
  269. }
  270. private static function checkEventListener(type:String, e:MouseEvent):void
  271. {
  272. // Filter out MOUSE_DOWN ModMouseEvents.
  273. if (type == ModMouseEvent.MOUSE_DOWN)
  274. {
  275. // Delete all InteractiveObjects from the 'down' Dictionary.
  276. for (var key:Object in _downDict)
  277. {
  278. delete _downDict[key];
  279. }
  280. }
  281. // Check all ModMouseEvent listener types par MOUSE_OUT.
  282. if (type != ModMouseEvent.MOUSE_OUT)
  283. {
  284. // Iterate through all InteractiveObjects under the mouse.
  285. for (var i:uint = 0; i < _intObjArr.length; i++)
  286. {
  287. // Filter out MOUSE_DOWN ModMouseEvents.
  288. if (type == ModMouseEvent.MOUSE_DOWN)
  289. {
  290. // Check to see if the InteractiveObject is listening on the CLICK ModMouseEvent type.
  291. if (_intObjArr[i].hasEventListener(ModMouseEvent.CLICK))
  292. {
  293. // Add the InteractiveObject to the 'down' Dictionary with reference to the CLICK ModMouseEvent.
  294. _downDict[_intObjArr[i]] = [ModMouseEvent.CLICK];
  295. // Check to see if the InteractiveObject is a ghost to the mapped ModMouseEvent.
  296. if (!inDict(_ghostDict, _intObjArr[i], ModMouseEvent.CLICK)) return;
  297. }
  298. }
  299. // Check to see if the InteractiveObject is listening on the dispatched ModMouseEvent type.
  300. if (_intObjArr[i].hasEventListener(type))
  301. {
  302. // Boolean that specifies whether or not to proceed and potentially Dispatch Events.
  303. var proceed:Boolean;
  304. // If a Block has been set, check the current InteractiveObject to see if it is in the Child display chain of the current blockObj.
  305. if (blockObj)
  306. {
  307. // Create a temporary reference to the current InteractiveObject.
  308. var intObj:InteractiveObject = _intObjArr[i];
  309. do
  310. {
  311. // If the current intObj is equal to the blockObj proceed within the InteractiveObject Array loop.
  312. if (intObj == blockObj)
  313. {
  314. proceed = true;
  315. break;
  316. }
  317. }
  318. while ((intObj = intObj.parent) != null); // Set the intObj to its Parent and check that it is not null.
  319. }
  320. else
  321. {
  322. proceed = true;
  323. }
  324. if (proceed)
  325. {
  326. // Filter out MOUSE_OVER ModMouseEvents.
  327. if (type == ModMouseEvent.MOUSE_OVER)
  328. {
  329. // Check to see if the current InteractiveObject is not in the 'over' Dictionary.
  330. if (_overDict[_intObjArr[i]] == null)
  331. {
  332. // Add the InteractiveObject to the 'over' Dictionary.
  333. addOver(_intObjArr[i]);
  334. // Dispatch a MOUSE_OVER ModMouseEvent from the current InteractiveObject.
  335. dispatchEvent(_intObjArr[i], type, e);
  336. }
  337. }
  338. // Filter out CLICK ModMouseEvents.
  339. else if (type == ModMouseEvent.CLICK)
  340. {
  341. // Only react to InteractiveObjects that are in the 'down' Dictionary.
  342. if (inDict(_downDict, _intObjArr[i], type))
  343. {
  344. // Dispatch a CLICK ModMouseEvent from the current InteractiveObject.
  345. dispatchEvent(_intObjArr[i], type, e);
  346. }
  347. }
  348. else
  349. {
  350. // Dispatch a ModMouseEvent from the current InteractiveObject.
  351. dispatchEvent(_intObjArr[i], type, e);
  352. }
  353. // Check to see if the InteractiveObject is a ghost to the mapped ModMouseEvent.
  354. if (!inDict(_ghostDict, _intObjArr[i], type)) return;
  355. }
  356. else
  357. {
  358. return;
  359. }
  360. }
  361. // Check to see if the InteractiveObject is blocking the mapped ModMouseEvent.
  362. if (inDict(_blockDict, _intObjArr[i], type))
  363. {
  364. var blockObj:InteractiveObject = _intObjArr[i];
  365. }
  366. }
  367. }
  368. else // ...else if the ModMouseEvent type is MOUSE_OUT
  369. {
  370. var outDict:Dictionary = new Dictionary();
  371. // Iterate through all InteractiveObjects in the 'over' Dictionary.
  372. for (key in _overDict)
  373. {
  374. var consume:uint = _intObjArr.length;
  375. var consumed:Boolean = false;
  376. var over:Boolean = false;
  377. // Iterate through all InteractiveObjects under the mouse.
  378. for (i = 0; i < _intObjArr.length; i++)
  379. {
  380. // Checks to see if the current InteractiveObject under the mouse is in the 'over' Dictionary.
  381. if (key == _intObjArr[i])
  382. {
  383. over = true;
  384. }
  385. // Checks to see if:
  386. // A) The InteractiveObject is listening on a MOUSE_OVER ModMouseEvent.
  387. // B) AND the InteractiveObject is NOT a Ghost for a MOUSE_OVER ModMouseEvent.
  388. // C) OR the InteractiveObject is a Block for a MOUSE_OVER ModMouseEvent.
  389. // D) AND the MOUSE_OVER ModMouseEvent has not yet been cosumed.
  390. if ((( _intObjArr[i].hasEventListener(ModMouseEvent.MOUSE_OVER)
  391. && !inDict(_ghostDict, _intObjArr[i], ModMouseEvent.MOUSE_OVER))
  392. || inDict(_blockDict, _intObjArr[i], ModMouseEvent.MOUSE_OVER))
  393. && !consumed)
  394. {
  395. // Specifies that the MOUSE_OVER ModMouseEvent has been cosumed.
  396. consumed = true;
  397. // Sets the index of the consuming InteractiveObject.
  398. consume = i;
  399. }
  400. // Checks to see if:
  401. // A) The MOUSE_OVER ModMouseEvent has been cosumed.
  402. // B) AND the current InteractiveObject's index is GREATER THAN the cosume index.
  403. // C) AND the current InteractiveObject is in the 'over' Dictionary.
  404. if (consumed && (i > consume) && _overDict[_intObjArr[i]])
  405. {
  406. // Adds the current InteractiveObject to the 'out' Dictionary.
  407. outDict[_intObjArr[i]] = true;
  408. }
  409. }
  410. // If the current InteractiveObject in the 'over' Dictionary is no longer under the mouse; add it to the 'out' Dictionary.
  411. if (!over) outDict[key] = true;
  412. }
  413. // Iterate through all InteractiveObjects in the 'out' Dictionary.
  414. for (key in outDict)
  415. {
  416. // Removes the InteractiveObject to the 'over' Dictionary.
  417. removeOver(InteractiveObject(key));
  418. // Dispatch a MOUSE_OUT ModMouseEvent from the current InteractiveObject.
  419. dispatchEvent(InteractiveObject(key), type, e);
  420. }
  421. }
  422. }
  423. private static function addOver(interactiveObject:InteractiveObject):void
  424. {
  425. _overDict[interactiveObject] = true;
  426. }
  427. private static function removeOver(interactiveObject:InteractiveObject):void
  428. {
  429. delete _overDict[interactiveObject];
  430. }
  431. private static function inDict(dictionary:Dictionary, interactiveObject:InteractiveObject, type:String):Boolean
  432. {
  433. var inDict:Boolean;
  434. if (dictionary[interactiveObject] != null)
  435. {
  436. for (var i:uint = 0; i < dictionary[interactiveObject].length; i++)
  437. {
  438. if (dictionary[interactiveObject][i] == type)
  439. {
  440. inDict = true;
  441. break;
  442. }
  443. }
  444. }
  445. return inDict;
  446. }
  447. private static function dispatchEvent(interactiveObject:InteractiveObject, type:String, e:MouseEvent):void
  448. {
  449. var modMouseEvent:ModMouseEvent = new ModMouseEvent(type, false, e.cancelable, interactiveObject.x, interactiveObject.y, null, e.ctrlKey, e.altKey, e.shiftKey, e.buttonDown, e.delta); // delta
  450. interactiveObject.dispatchEvent(modMouseEvent);
  451. }
  452. /**
  453. * Adds an InteractiveObject to the ModMouseEvent 'block' Dictionary. Block InteractiveObjects block ModMouseEvents.
  454. *
  455. * @param interactiveObject The InteractiveObject that is to act as a block.
  456. * @param events An array of events that are to be blocked by the InteractiveObject.
  457. * @param buttonMode Specifies whether to use the buttonMode of the blocking InteractiveObject.
  458. */
  459. public static function addBlock(interactiveObject:InteractiveObject, events:Array = null, buttonMode:Boolean = true):void
  460. {
  461. _blockDict[interactiveObject] = events;
  462. _buttonDict[interactiveObject] = buttonMode;
  463. }
  464. /**
  465. * Removes an InteractiveObject from the ModMouseEvent 'block' Dictionary.
  466. *
  467. * @param interactiveObject The InteractiveObject that is to be removed from the ModMouseEvent 'block' Dictionary.
  468. */
  469. public static function removeBlock(interactiveObject:InteractiveObject):void
  470. {
  471. delete _buttonDict[interactiveObject];
  472. delete _blockDict[interactiveObject];
  473. }
  474. /**
  475. * Adds an InteractiveObject to the ModMouseEvent 'ghost' Dictionary. Ghost InteractiveObjects do not consume specified ModMouseEvents.
  476. *
  477. * @param interactiveObject The InteractiveObject that is to act as a ghost.
  478. * @param events An array of events that are NOT to be consumed by the InteractiveObject.
  479. */
  480. public static function addGhost(interactiveObject:InteractiveObject, events:Array):void
  481. {
  482. _ghostDict[interactiveObject] = events;
  483. }
  484. /**
  485. * Removes an InteractiveObject from the ModMouseEvent 'ghost' Dictionary.
  486. *
  487. * @param interactiveObject The InteractiveObject that is to be removed from the ModMouseEvent 'ghost' Dictionary.
  488. */
  489. public static function removeGhost(interactiveObject:InteractiveObject):void
  490. {
  491. delete _ghostDict[interactiveObject];
  492. }
  493. /** @param value Sets the visibility of the cursor Sprite that is used to set the buttonMode of the Mouse cursor. */
  494. public static function set cursorVisible(value:Boolean):void
  495. {
  496. _cursor.visible = value;
  497. }
  498. /** Returns the visibility of the cursor Sprite that is used to set the buttonMode of the Mouse cursor. */
  499. public static function get cursorVisible():Boolean
  500. {
  501. return _cursor.visible;
  502. }
  503. }
  504. }