PageRenderTime 140ms CodeModel.GetById 60ms RepoModel.GetById 14ms app.codeStats 0ms

/Sample/mx/managers/FocusManager.as

https://github.com/ingydotnet/yaml-oscon2009-talk
ActionScript | 2257 lines | 1255 code | 303 blank | 699 comment | 285 complexity | 7ecbf0c83d17c85905c9c9898cb515da MD5 | raw file
  1. ////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // ADOBE SYSTEMS INCORPORATED
  4. // Copyright 2003-2007 Adobe Systems Incorporated
  5. // All Rights Reserved.
  6. //
  7. // NOTICE: Adobe permits you to use, modify, and distribute this file
  8. // in accordance with the terms of the license agreement accompanying it.
  9. //
  10. ////////////////////////////////////////////////////////////////////////////////
  11. package mx.managers
  12. {
  13. import flash.display.DisplayObject;
  14. import flash.display.DisplayObjectContainer;
  15. import flash.display.InteractiveObject;
  16. import flash.display.LoaderInfo;
  17. import flash.display.Sprite;
  18. import flash.events.Event;
  19. import flash.events.FocusEvent;
  20. import flash.events.IEventDispatcher;
  21. import flash.events.KeyboardEvent;
  22. import flash.events.MouseEvent;
  23. import flash.system.Capabilities;
  24. import flash.text.TextField;
  25. import flash.ui.Keyboard;
  26. import flash.utils.Dictionary;
  27. import mx.core.Application;
  28. import mx.core.FlexSprite;
  29. import mx.core.ISWFLoader;
  30. import mx.core.IButton;
  31. import mx.core.IChildList;
  32. import mx.core.IRawChildrenContainer;
  33. import mx.core.ISWFBridgeProvider;
  34. import mx.core.IUIComponent;
  35. import mx.core.mx_internal;
  36. import mx.core.SWFBridgeGroup;
  37. import mx.events.FlexEvent;
  38. import mx.events.FocusRequestDirection;
  39. import mx.events.SWFBridgeEvent;
  40. import mx.events.SWFBridgeRequest;
  41. import mx.utils.DisplayUtil;
  42. use namespace mx_internal;
  43. /**
  44. * The FocusManager class manages the focus on components in response to mouse
  45. * activity or keyboard activity (Tab key). There can be several FocusManager
  46. * instances in an application. Each FocusManager instance
  47. * is responsible for a set of components that comprise a "tab loop". If you
  48. * hit Tab enough times, focus traverses through a set of components and
  49. * eventually get back to the first component that had focus. That is a "tab loop"
  50. * and a FocusManager instance manages that loop. If there are popup windows
  51. * with their own set of components in a "tab loop" those popup windows will have
  52. * their own FocusManager instances. The main application always has a
  53. * FocusManager instance.
  54. *
  55. * <p>The FocusManager manages focus from the "component level".
  56. * In Flex, a UITextField in a component is the only way to allow keyboard entry
  57. * of text. To the Flash Player or AIR, that UITextField has focus. However, from the
  58. * FocusManager's perspective the component that parents the UITextField has focus.
  59. * Thus there is a distinction between component-level focus and player-level focus.
  60. * Application developers generally only have to deal with component-level focus while
  61. * component developers must understand player-level focus.</p>
  62. *
  63. * <p>All components that can be managed by the FocusManager must implement
  64. * mx.managers.IFocusManagerComponent, whereas objects managed by player-level focus do not.</p>
  65. *
  66. * <p>The FocusManager also managers the concept of a defaultButton, which is
  67. * the Button on a form that dispatches a click event when the Enter key is pressed
  68. * depending on where focus is at that time.</p>
  69. */
  70. public class FocusManager implements IFocusManager
  71. {
  72. include "../core/Version.as";
  73. //--------------------------------------------------------------------------
  74. //
  75. // Class constants
  76. //
  77. //--------------------------------------------------------------------------
  78. /**
  79. * @private
  80. *
  81. * Default value of parameter, ignore.
  82. */
  83. private static const FROM_INDEX_UNSPECIFIED:int = -2;
  84. //--------------------------------------------------------------------------
  85. //
  86. // Constructor
  87. //
  88. //--------------------------------------------------------------------------
  89. /**
  90. * Constructor.
  91. *
  92. * <p>A FocusManager manages the focus within the children of an IFocusManagerContainer.
  93. * It installs itself in the IFocusManagerContainer during execution
  94. * of the constructor.</p>
  95. *
  96. * @param container An IFocusManagerContainer that hosts the FocusManager.
  97. *
  98. * @param popup If <code>true</code>, indicates that the container
  99. * is a popup component and not the main application.
  100. */
  101. public function FocusManager(container:IFocusManagerContainer, popup:Boolean = false)
  102. {
  103. super();
  104. this.popup = popup;
  105. browserMode = Capabilities.playerType == "ActiveX" && !popup;
  106. container.focusManager = this; // this property name is reserved in the parent
  107. // trace("FocusManager constructor " + container + ".focusManager");
  108. _form = container;
  109. focusableObjects = [];
  110. focusPane = new FlexSprite();
  111. focusPane.name = "focusPane";
  112. addFocusables(DisplayObject(container));
  113. // Listen to the stage so we know when the root application is loaded.
  114. container.addEventListener(Event.ADDED, addedHandler);
  115. container.addEventListener(Event.REMOVED, removedHandler);
  116. container.addEventListener(FlexEvent.SHOW, showHandler);
  117. container.addEventListener(FlexEvent.HIDE, hideHandler);
  118. //special case application and window
  119. if (container.systemManager is SystemManager)
  120. {
  121. // special case application. It shouldn't need to be made
  122. // active and because we defer appCreationComplete, this
  123. // would steal focus back from any popups created during
  124. // instantiation
  125. if (container != SystemManager(container.systemManager).application)
  126. container.addEventListener(FlexEvent.CREATION_COMPLETE,
  127. creationCompleteHandler);
  128. }
  129. // Make sure the SystemManager is running so it can tell us about
  130. // mouse clicks and stage size changes.
  131. try
  132. {
  133. container.systemManager.addFocusManager(container); // build a message that does the equal
  134. var sm:ISystemManager = form.systemManager;
  135. // Set up our swfBridgeGroup. If this is a pop up then the parent
  136. // bridge is empty, otherwise its the form's system manager's bridge.
  137. swfBridgeGroup = new SWFBridgeGroup(sm);
  138. if (!popup)
  139. swfBridgeGroup.parentBridge = sm.swfBridgeGroup.parentBridge;
  140. // add ourselves to our parent focus manager if this is a bridged
  141. // application not a dialog or other popup.
  142. if (sm.useSWFBridge())
  143. {
  144. sm.addEventListener(SWFBridgeEvent.BRIDGE_APPLICATION_UNLOADING, removeFromParentBridge);
  145. // have the child listen to move requests from the parent.
  146. var bridge:IEventDispatcher = swfBridgeGroup.parentBridge;
  147. if (bridge)
  148. {
  149. bridge.addEventListener(SWFBridgeRequest.MOVE_FOCUS_REQUEST, focusRequestMoveHandler);
  150. bridge.addEventListener(SWFBridgeRequest.SET_SHOW_FOCUS_INDICATOR_REQUEST,
  151. setShowFocusIndicatorRequestHandler);
  152. }
  153. // add listener activate/deactivate requests
  154. if (bridge && !(form.systemManager is SystemManagerProxy))
  155. {
  156. bridge.addEventListener(SWFBridgeRequest.ACTIVATE_FOCUS_REQUEST, focusRequestActivateHandler);
  157. bridge.addEventListener(SWFBridgeRequest.DEACTIVATE_FOCUS_REQUEST, focusRequestDeactivateHandler);
  158. bridge.addEventListener(SWFBridgeEvent.BRIDGE_FOCUS_MANAGER_ACTIVATE,
  159. bridgeEventActivateHandler);
  160. }
  161. // listen when the container has been added to the stage so we can add the focusable
  162. // children
  163. container.addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
  164. }
  165. }
  166. catch (e:Error)
  167. {
  168. // ignore null pointer errors caused by container using a
  169. // systemManager from another sandbox.
  170. }
  171. }
  172. //--------------------------------------------------------------------------
  173. //
  174. // Variables
  175. //
  176. //--------------------------------------------------------------------------
  177. private var LARGE_TAB_INDEX:int = 99999;
  178. private var calculateCandidates:Boolean = true;
  179. /**
  180. * @private
  181. * We track whether we've been last activated or saw a TAB
  182. * This is used in browser tab management
  183. */
  184. private var lastAction:String;
  185. /**
  186. * @private
  187. * Tab management changes based on whether were in a browser or not
  188. * This value is also affected by whether you are a modal dialog or not
  189. */
  190. public var browserMode:Boolean;
  191. /**
  192. * @private
  193. * Tab management changes based on whether were in a browser or not
  194. * If non-null, this is the object that will
  195. * lose focus to the browser
  196. */
  197. private var browserFocusComponent:InteractiveObject;
  198. /**
  199. * @private
  200. * Total set of all objects that can receive focus
  201. * but might be disabled or invisible.
  202. */
  203. private var focusableObjects:Array;
  204. /**
  205. * @private
  206. * Filtered set of objects that can receive focus right now.
  207. */
  208. private var focusableCandidates:Array;
  209. /**
  210. * @private
  211. */
  212. private var activated:Boolean = false;
  213. /**
  214. * @private
  215. *
  216. * true if focus was changed to one of focusable objects. False if focus passed to
  217. * the browser.
  218. */
  219. private var focusChanged:Boolean;
  220. /**
  221. * @private
  222. *
  223. * if non-null, the location to move focus from instead of the object
  224. * that has focus in the stage.
  225. */
  226. private var fauxFocus:DisplayObject;
  227. /**
  228. * @private
  229. *
  230. * The focus manager maintains its own bridges so a focus manager in a pop
  231. * up can move focus to another focus manager in the same pop up. That is,
  232. * A pop ups can be a collection of focus managers working together just
  233. * as is done in the System Manager's document.
  234. */
  235. private var swfBridgeGroup:SWFBridgeGroup;
  236. /**
  237. * @private
  238. *
  239. * bridge handle of the last active focus manager.
  240. */
  241. private var lastActiveFocusManager:FocusManager;
  242. /**
  243. * @private
  244. *
  245. * Test if the focus was set locally in this focus manager (true) or
  246. * if focus was transfer to another focus manager (false)
  247. */
  248. private var focusSetLocally:Boolean;
  249. /**
  250. * @private
  251. *
  252. * True if this focus manager is a popup, false if it is a main application.
  253. *
  254. */
  255. private var popup:Boolean;
  256. /**
  257. * @private
  258. *
  259. * Used when a the skip parameter can't be passed into
  260. * dispatchEventFromSWFBridges() because the caller doesn't take
  261. * a skip parameter.
  262. */
  263. private var skipBridge:IEventDispatcher;
  264. //--------------------------------------------------------------------------
  265. //
  266. // Properties
  267. //
  268. //--------------------------------------------------------------------------
  269. //----------------------------------
  270. // showFocusIndicator
  271. //----------------------------------
  272. /**
  273. * @private
  274. * Storage for the showFocusIndicator property.
  275. */
  276. private var _showFocusIndicator:Boolean = false;
  277. /**
  278. * @inheritDoc
  279. */
  280. public function get showFocusIndicator():Boolean
  281. {
  282. return _showFocusIndicator;
  283. }
  284. /**
  285. * @private
  286. */
  287. public function set showFocusIndicator(value:Boolean):void
  288. {
  289. var changed:Boolean = _showFocusIndicator != value;
  290. _showFocusIndicator = value;
  291. if (changed && !popup && form.systemManager.swfBridgeGroup)
  292. dispatchSetShowFocusIndicatorRequest(value, null);
  293. }
  294. //----------------------------------
  295. // defaultButton
  296. //----------------------------------
  297. /**
  298. * @private
  299. * The current default button.
  300. */
  301. private var defButton:IButton;
  302. /**
  303. * @private
  304. */
  305. private var _defaultButton:IButton;
  306. /**
  307. * @inheritDoc
  308. */
  309. public function get defaultButton():IButton
  310. {
  311. return _defaultButton;
  312. }
  313. /**
  314. * @private
  315. * We don't type the value as Button for dependency reasons
  316. */
  317. public function set defaultButton(value:IButton):void
  318. {
  319. var button:IButton = value ? IButton(value) : null;
  320. if (button != _defaultButton)
  321. {
  322. if (_defaultButton)
  323. _defaultButton.emphasized = false;
  324. if (defButton)
  325. defButton.emphasized = false;
  326. _defaultButton = button;
  327. defButton = button;
  328. if (button)
  329. button.emphasized = true;
  330. }
  331. }
  332. //----------------------------------
  333. // defaultButtonEnabled
  334. //----------------------------------
  335. /**
  336. * @private
  337. * Storage for the defaultButtonEnabled property.
  338. */
  339. private var _defaultButtonEnabled:Boolean = true;
  340. /**
  341. * @inheritDoc
  342. */
  343. public function get defaultButtonEnabled():Boolean
  344. {
  345. return _defaultButtonEnabled;
  346. }
  347. /**
  348. * @private
  349. */
  350. public function set defaultButtonEnabled(value:Boolean):void
  351. {
  352. _defaultButtonEnabled = value;
  353. }
  354. //----------------------------------
  355. // focusPane
  356. //----------------------------------
  357. /**
  358. * @private
  359. * Storage for the focusPane property.
  360. */
  361. private var _focusPane:Sprite;
  362. /**
  363. * @inheritDoc
  364. */
  365. public function get focusPane():Sprite
  366. {
  367. return _focusPane;
  368. }
  369. /**
  370. * @private
  371. */
  372. public function set focusPane(value:Sprite):void
  373. {
  374. _focusPane = value;
  375. }
  376. //----------------------------------
  377. // form
  378. //----------------------------------
  379. /**
  380. * @private
  381. * Storage for the form property.
  382. */
  383. private var _form:IFocusManagerContainer;
  384. /**
  385. * @private
  386. * The form is the property where we store the IFocusManagerContainer
  387. * that hosts this FocusManager.
  388. */
  389. mx_internal function get form():IFocusManagerContainer
  390. {
  391. return _form;
  392. }
  393. /**
  394. * @private
  395. */
  396. mx_internal function set form (value:IFocusManagerContainer):void
  397. {
  398. _form = value;
  399. }
  400. //----------------------------------
  401. // _lastFocus
  402. //----------------------------------
  403. /**
  404. * @private
  405. * the object that last had focus
  406. */
  407. private var _lastFocus:IFocusManagerComponent;
  408. /**
  409. * @private
  410. */
  411. mx_internal function get lastFocus():IFocusManagerComponent
  412. {
  413. return _lastFocus;
  414. }
  415. //----------------------------------
  416. // nextTabIndex
  417. //----------------------------------
  418. /**
  419. * @inheritDoc
  420. */
  421. public function get nextTabIndex():int
  422. {
  423. return getMaxTabIndex() + 1;
  424. }
  425. /**
  426. * Gets the highest tab index currently used in this Focus Manager's form or subform.
  427. *
  428. * @return Highest tab index currently used.
  429. */
  430. private function getMaxTabIndex():int
  431. {
  432. var z:Number = 0;
  433. var n:int = focusableObjects.length;
  434. for (var i:int = 0; i < n; i++)
  435. {
  436. var t:Number = focusableObjects[i].tabIndex;
  437. if (!isNaN(t))
  438. z = Math.max(z, t);
  439. }
  440. return z;
  441. }
  442. //--------------------------------------------------------------------------
  443. //
  444. // Methods
  445. //
  446. //--------------------------------------------------------------------------
  447. /**
  448. * @inheritDoc
  449. */
  450. public function getFocus():IFocusManagerComponent
  451. {
  452. var o:InteractiveObject = form.systemManager.stage.focus;
  453. return findFocusManagerComponent(o);
  454. }
  455. /**
  456. * @inheritDoc
  457. */
  458. public function setFocus(o:IFocusManagerComponent):void
  459. {
  460. // trace("FM " + this + " setting focus to " + o);
  461. o.setFocus();
  462. focusSetLocally = true;
  463. // trace("FM set focus");
  464. }
  465. /**
  466. * @private
  467. */
  468. private function focusInHandler(event:FocusEvent):void
  469. {
  470. var target:InteractiveObject = InteractiveObject(event.target);
  471. // trace("FocusManager focusInHandler in = " + this._form.systemManager.loaderInfo.url);
  472. // trace("FM " + this + " focusInHandler " + target);
  473. // if the target is in a bridged application, let it handle the click.
  474. var sm:ISystemManager = form.systemManager;
  475. if (sm.isDisplayObjectInABridgedApplication(DisplayObject(event.target)))
  476. return;
  477. if (isParent(DisplayObjectContainer(form), target))
  478. {
  479. // trace("FM " + this + " setting last focus " + target);
  480. _lastFocus = findFocusManagerComponent(InteractiveObject(target));
  481. // handle default button here
  482. // we can't check for Button because of cross-versioning so
  483. // for now we just check for an emphasized property
  484. if (_lastFocus is IButton)
  485. {
  486. var x:IButton = _lastFocus as IButton;
  487. // if we have marked some other button as a default button
  488. if (defButton)
  489. {
  490. // change it to be this button
  491. defButton.emphasized = false;
  492. defButton = x;
  493. x.emphasized = true;
  494. }
  495. }
  496. else
  497. {
  498. // restore the default button to be the original one
  499. if (defButton && defButton != _defaultButton)
  500. {
  501. defButton.emphasized = false;
  502. defButton = _defaultButton;
  503. _defaultButton.emphasized = true;
  504. }
  505. }
  506. }
  507. }
  508. /**
  509. * @private Useful for debugging
  510. */
  511. private function focusOutHandler(event:FocusEvent):void
  512. {
  513. var target:InteractiveObject = InteractiveObject(event.target);
  514. // trace("FocusManager focusOutHandler in = " + this._form.systemManager.loaderInfo.url);
  515. // trace("FM " + this + " focusOutHandler " + target);
  516. }
  517. /**
  518. * @private
  519. * restore focus to whoever had it last
  520. */
  521. private function activateHandler(event:Event):void
  522. {
  523. // var target:InteractiveObject = InteractiveObject(event.target);
  524. // trace("FM " + this + " activateHandler ", _lastFocus);
  525. // restore focus if this focus manager had last focus
  526. if (_lastFocus && !browserMode)
  527. _lastFocus.setFocus();
  528. lastAction = "ACTIVATE";
  529. }
  530. /**
  531. * @private Useful for debugging
  532. */
  533. private function deactivateHandler(event:Event):void
  534. {
  535. // var target:InteractiveObject = InteractiveObject(event.target);
  536. // trace("FM " + this + " deactivateHandler ", _lastFocus);
  537. }
  538. /**
  539. * @inheritDoc
  540. */
  541. public function showFocus():void
  542. {
  543. if (!showFocusIndicator)
  544. {
  545. showFocusIndicator = true;
  546. if (_lastFocus)
  547. _lastFocus.drawFocus(true);
  548. }
  549. }
  550. /**
  551. * @inheritDoc
  552. */
  553. public function hideFocus():void
  554. {
  555. // trace("FOcusManger " + this + " Hide Focus");
  556. if (showFocusIndicator)
  557. {
  558. showFocusIndicator = false;
  559. if (_lastFocus)
  560. _lastFocus.drawFocus(false);
  561. }
  562. // trace("END FOcusManger Hide Focus");
  563. }
  564. /**
  565. * The SystemManager activates and deactivates a FocusManager
  566. * if more than one IFocusManagerContainer is visible at the same time.
  567. * If the mouse is clicked in an IFocusManagerContainer with a deactivated
  568. * FocusManager, the SystemManager will call
  569. * the <code>activate()</code> method on that FocusManager.
  570. * The FocusManager that was activated will have its <code>deactivate()</code> method
  571. * called prior to the activation of another FocusManager.
  572. *
  573. * <p>The FocusManager adds event handlers that allow it to monitor
  574. * focus related keyboard and mouse activity.</p>
  575. */
  576. public function activate():void
  577. {
  578. // we can get a double activation if we're popping up and becoming visible
  579. // like the second time a menu appears
  580. if (activated)
  581. {
  582. // trace("FocusManager is already active " + this);
  583. return;
  584. }
  585. //trace("FocusManager activating = " + this._form.systemManager.loaderInfo.url);
  586. //trace("FocusManager activating " + this);
  587. // listen for focus changes, use weak references for the stage
  588. // form.systemManager can be null if the form is created in a sandbox and
  589. // added as a child to the root system manager.
  590. var sm:ISystemManager = form.systemManager;
  591. if (sm)
  592. {
  593. if (sm.isTopLevelRoot())
  594. {
  595. sm.stage.addEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, mouseFocusChangeHandler, false, 0, true);
  596. sm.stage.addEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler, false, 0, true);
  597. sm.stage.addEventListener(Event.ACTIVATE, activateHandler, false, 0, true);
  598. sm.stage.addEventListener(Event.DEACTIVATE, deactivateHandler, false, 0, true);
  599. }
  600. else
  601. {
  602. sm.addEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, mouseFocusChangeHandler, false, 0, true);
  603. sm.addEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler, false, 0, true);
  604. sm.addEventListener(Event.ACTIVATE, activateHandler, false, 0, true);
  605. sm.addEventListener(Event.DEACTIVATE, deactivateHandler, false, 0, true);
  606. }
  607. }
  608. form.addEventListener(FocusEvent.FOCUS_IN, focusInHandler, true);
  609. form.addEventListener(FocusEvent.FOCUS_OUT, focusOutHandler, true);
  610. form.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
  611. form.addEventListener(KeyboardEvent.KEY_DOWN, defaultButtonKeyHandler);
  612. // listen for default button in Capture phase. Some components like TextInput
  613. // and Accordion stop the Enter key from propagating in the Bubble phase.
  614. form.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler, true);
  615. activated = true;
  616. // Restore focus to the last control that had it if there was one.
  617. if (_lastFocus)
  618. setFocus(_lastFocus);
  619. // activate children in compatibility mode or in sandboxes.
  620. dispatchEventFromSWFBridges(new SWFBridgeRequest(SWFBridgeRequest.ACTIVATE_FOCUS_REQUEST), skipBridge);
  621. }
  622. /**
  623. * The SystemManager activates and deactivates a FocusManager
  624. * if more than one IFocusManagerContainer is visible at the same time.
  625. * If the mouse is clicked in an IFocusManagerContainer with a deactivated
  626. * FocusManager, the SystemManager will call
  627. * the <code>activate()</code> method on that FocusManager.
  628. * The FocusManager that was activated will have its <code>deactivate()</code> method
  629. * called prior to the activation of another FocusManager.
  630. *
  631. * <p>The FocusManager removes event handlers that allow it to monitor
  632. * focus related keyboard and mouse activity.</p>
  633. */
  634. public function deactivate():void
  635. {
  636. // trace("FocusManager deactivating " + this);
  637. //trace("FocusManager deactivating = " + this._form.systemManager.loaderInfo.url);
  638. // listen for focus changes
  639. var sm:ISystemManager = form.systemManager;
  640. if (sm)
  641. {
  642. if (sm.isTopLevelRoot())
  643. {
  644. sm.stage.removeEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, mouseFocusChangeHandler);
  645. sm.stage.removeEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler);
  646. sm.stage.removeEventListener(Event.ACTIVATE, activateHandler);
  647. sm.stage.removeEventListener(Event.DEACTIVATE, deactivateHandler);
  648. }
  649. else
  650. {
  651. sm.removeEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, mouseFocusChangeHandler);
  652. sm.removeEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler);
  653. sm.removeEventListener(Event.ACTIVATE, activateHandler);
  654. sm.removeEventListener(Event.DEACTIVATE, deactivateHandler);
  655. }
  656. }
  657. form.removeEventListener(FocusEvent.FOCUS_IN, focusInHandler, true);
  658. form.removeEventListener(FocusEvent.FOCUS_OUT, focusOutHandler, true);
  659. form.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
  660. form.removeEventListener(KeyboardEvent.KEY_DOWN, defaultButtonKeyHandler);
  661. // stop listening for default button in Capture phase
  662. form.removeEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler, true);
  663. activated = false;
  664. // deactivate children in compatibility mode or in sandboxes.
  665. dispatchEventFromSWFBridges(new SWFBridgeRequest(SWFBridgeRequest.DEACTIVATE_FOCUS_REQUEST), skipBridge);
  666. }
  667. /**
  668. * @inheritDoc
  669. */
  670. public function findFocusManagerComponent(
  671. o:InteractiveObject):IFocusManagerComponent
  672. {
  673. return findFocusManagerComponent2(o) as IFocusManagerComponent;
  674. }
  675. /**
  676. * @private
  677. *
  678. * This version of the method differs from the old one to support SWFLoader
  679. * being in the focusableObjects list but not being a component that
  680. * gets focus. SWFLoader is in the list of focusable objects so
  681. * focus may be passed over a bridge to the components on the other
  682. * side of the bridge.
  683. */
  684. private function findFocusManagerComponent2(
  685. o:InteractiveObject):DisplayObject
  686. {
  687. try
  688. {
  689. while (o)
  690. {
  691. if ((o is IFocusManagerComponent && IFocusManagerComponent(o).focusEnabled) ||
  692. o is ISWFLoader)
  693. return o;
  694. o = o.parent;
  695. }
  696. }
  697. catch (error:SecurityError)
  698. {
  699. // can happen in a loaded child swf
  700. // trace("findFocusManagerComponent: handling security error");
  701. }
  702. // tab was set somewhere else
  703. return null;
  704. }
  705. /**
  706. * @inheritDoc
  707. */
  708. public function moveFocus(direction:String, fromDisplayObject:DisplayObject = null):void
  709. {
  710. if (direction == FocusRequestDirection.TOP)
  711. {
  712. setFocusToTop();
  713. return;
  714. }
  715. if (direction == FocusRequestDirection.BOTTOM)
  716. {
  717. setFocusToBottom();
  718. return;
  719. }
  720. var keyboardEvent:KeyboardEvent = new KeyboardEvent(KeyboardEvent.KEY_DOWN);
  721. keyboardEvent.keyCode = Keyboard.TAB;
  722. keyboardEvent.shiftKey = (direction == FocusRequestDirection.FORWARD) ? false : true;
  723. fauxFocus = fromDisplayObject;
  724. keyDownHandler(keyboardEvent);
  725. var focusEvent:FocusEvent = new FocusEvent(FocusEvent.KEY_FOCUS_CHANGE);
  726. focusEvent.keyCode = Keyboard.TAB;
  727. focusEvent.shiftKey = (direction == FocusRequestDirection.FORWARD) ? false : true;
  728. keyFocusChangeHandler(focusEvent);
  729. fauxFocus = null;
  730. }
  731. /**
  732. * @private
  733. * Returns true if p is a parent of o.
  734. */
  735. private function isParent(p:DisplayObjectContainer, o:DisplayObject):Boolean
  736. {
  737. if (p is IRawChildrenContainer)
  738. return IRawChildrenContainer(p).rawChildren.contains(o);
  739. return p.contains(o);
  740. }
  741. private function isEnabledAndVisible(o:DisplayObject):Boolean
  742. {
  743. var formParent:DisplayObjectContainer = DisplayObject(form).parent;
  744. while (o != formParent)
  745. {
  746. if (o is IUIComponent)
  747. if (!IUIComponent(o).enabled)
  748. return false;
  749. if (!o.visible)
  750. return false;
  751. o = o.parent;
  752. }
  753. return true;
  754. }
  755. /**
  756. * @private
  757. */
  758. private function sortByTabIndex(a:InteractiveObject, b:InteractiveObject):int
  759. {
  760. var aa:int = a.tabIndex;
  761. var bb:int = b.tabIndex;
  762. if (aa == -1)
  763. aa = int.MAX_VALUE;
  764. if (bb == -1)
  765. bb = int.MAX_VALUE;
  766. return (aa > bb ? 1 :
  767. aa < bb ? -1 : sortByDepth(DisplayObject(a), DisplayObject(b)));
  768. }
  769. /**
  770. * @private
  771. */
  772. private function sortFocusableObjectsTabIndex():void
  773. {
  774. //trace("FocusableObjectsTabIndex");
  775. focusableCandidates = [];
  776. var n:int = focusableObjects.length;
  777. for (var i:int = 0; i < n; i++)
  778. {
  779. var c:IFocusManagerComponent = focusableObjects[i] as IFocusManagerComponent;
  780. if ((c && c.tabIndex && !isNaN(Number(c.tabIndex))) ||
  781. focusableObjects[i] is ISWFLoader)
  782. {
  783. // if we get here, it is a candidate
  784. focusableCandidates.push(focusableObjects[i]);
  785. }
  786. }
  787. focusableCandidates.sort(sortByTabIndex);
  788. }
  789. /**
  790. * @private
  791. */
  792. private function sortByDepth(aa:DisplayObject, bb:DisplayObject):Number
  793. {
  794. var val1:String = "";
  795. var val2:String = "";
  796. var index:int;
  797. var tmp:String;
  798. var tmp2:String;
  799. var zeros:String = "0000";
  800. var a:DisplayObject = DisplayObject(aa);
  801. var b:DisplayObject = DisplayObject(bb);
  802. while (a != DisplayObject(form) && a.parent)
  803. {
  804. index = getChildIndex(a.parent, a);
  805. tmp = index.toString(16);
  806. if (tmp.length < 4)
  807. {
  808. tmp2 = zeros.substring(0, 4 - tmp.length) + tmp;
  809. }
  810. val1 = tmp2 + val1;
  811. a = a.parent;
  812. }
  813. while (b != DisplayObject(form) && b.parent)
  814. {
  815. index = getChildIndex(b.parent, b);
  816. tmp = index.toString(16);
  817. if (tmp.length < 4)
  818. {
  819. tmp2 = zeros.substring(0, 4 - tmp.length) + tmp;
  820. }
  821. val2 = tmp2 + val2;
  822. b = b.parent;
  823. }
  824. return val1 > val2 ? 1 : val1 < val2 ? -1 : 0;
  825. }
  826. private function getChildIndex(parent:DisplayObjectContainer, child:DisplayObject):int
  827. {
  828. try
  829. {
  830. return parent.getChildIndex(child);
  831. }
  832. catch(e:Error)
  833. {
  834. if (parent is IRawChildrenContainer)
  835. return IRawChildrenContainer(parent).rawChildren.getChildIndex(child);
  836. throw e;
  837. }
  838. throw new Error("FocusManager.getChildIndex failed"); // shouldn't ever get here
  839. }
  840. /**
  841. * @private
  842. * Calculate what focusableObjects are valid tab candidates.
  843. */
  844. private function sortFocusableObjects():void
  845. {
  846. // trace("FocusableObjects " + focusableObjects.length.toString());
  847. focusableCandidates = [];
  848. var n:int = focusableObjects.length;
  849. for (var i:int = 0; i < n; i++)
  850. {
  851. var c:InteractiveObject = focusableObjects[i];
  852. // trace(" " + c);
  853. if (c.tabIndex && !isNaN(Number(c.tabIndex)) && c.tabIndex > 0)
  854. {
  855. sortFocusableObjectsTabIndex();
  856. return;
  857. }
  858. focusableCandidates.push(c);
  859. }
  860. focusableCandidates.sort(sortByDepth);
  861. }
  862. /**
  863. * Call this method to make the system
  864. * think the Enter key was pressed and the defaultButton was clicked
  865. */
  866. mx_internal function sendDefaultButtonEvent():void
  867. {
  868. // trace("FocusManager.sendDefaultButtonEvent " + defButton);
  869. defButton.dispatchEvent(new MouseEvent("click"));
  870. }
  871. /**
  872. * @private
  873. * Do a tree walk and add all children you can find.
  874. */
  875. private function addFocusables(o:DisplayObject, skipTopLevel:Boolean = false):void
  876. {
  877. // trace(">>addFocusables " + o);
  878. if ((o is IFocusManagerComponent) && !skipTopLevel)
  879. {
  880. var addToFocusables:Boolean = false;
  881. if (o is IFocusManagerComponent)
  882. {
  883. var focusable:IFocusManagerComponent = IFocusManagerComponent(o);
  884. if (focusable.focusEnabled)
  885. {
  886. if (focusable.tabEnabled && isTabVisible(o))
  887. {
  888. addToFocusables = true;
  889. }
  890. }
  891. }
  892. if (addToFocusables)
  893. {
  894. if (focusableObjects.indexOf(o) == -1)
  895. {
  896. focusableObjects.push(o);
  897. calculateCandidates = true;
  898. // trace("FM added " + o);
  899. }
  900. o.addEventListener("tabEnabledChange", tabEnabledChangeHandler);
  901. o.addEventListener("tabIndexChange", tabIndexChangeHandler);
  902. }
  903. }
  904. if (o is DisplayObjectContainer)
  905. {
  906. var doc:DisplayObjectContainer = DisplayObjectContainer(o);
  907. // Even if they aren't focusable now,
  908. // listen in case they become later.
  909. o.addEventListener("tabChildrenChange", tabChildrenChangeHandler);
  910. if (doc.tabChildren)
  911. {
  912. if (o is IRawChildrenContainer)
  913. {
  914. // trace("using view rawChildren");
  915. var rawChildren:IChildList = IRawChildrenContainer(o).rawChildren;
  916. // recursively visit and add children of components
  917. // we don't do this for containers because we get individual
  918. // adds for the individual children
  919. var i:int;
  920. for (i = 0; i < rawChildren.numChildren; i++)
  921. {
  922. try
  923. {
  924. addFocusables(rawChildren.getChildAt(i));
  925. }
  926. catch(error:SecurityError)
  927. {
  928. // Ignore this child if we can't access it
  929. // trace("addFocusables: ignoring security error getting child from rawChildren: " + error);
  930. }
  931. }
  932. }
  933. else
  934. {
  935. // trace("using container's children");
  936. // recursively visit and add children of components
  937. // we don't do this for containers because we get individual
  938. // adds for the individual children
  939. for (i = 0; i < doc.numChildren; i++)
  940. {
  941. try
  942. {
  943. addFocusables(doc.getChildAt(i));
  944. }
  945. catch(error:SecurityError)
  946. {
  947. // Ignore this child if we can't access it
  948. // trace("addFocusables: ignoring security error getting child at document." + error);
  949. }
  950. }
  951. }
  952. }
  953. }
  954. // trace("<<addFocusables " + o);
  955. }
  956. /**
  957. * @private
  958. * is it really tabbable?
  959. */
  960. private function isTabVisible(o:DisplayObject):Boolean
  961. {
  962. var s:DisplayObject = DisplayObject(form.systemManager);
  963. if (!s) return false;
  964. var p:DisplayObjectContainer = o.parent;
  965. while (p && p != s)
  966. {
  967. if (!p.tabChildren)
  968. return false;
  969. p = p.parent;
  970. }
  971. return true;
  972. }
  973. private function isValidFocusCandidate(o:DisplayObject, g:String):Boolean
  974. {
  975. if (!isEnabledAndVisible(o))
  976. return false;
  977. if (o is IFocusManagerGroup)
  978. {
  979. // reject if it is in the same tabgroup
  980. var tg:IFocusManagerGroup = IFocusManagerGroup(o);
  981. if (g == tg.groupName) return false;
  982. }
  983. return true;
  984. }
  985. private function getIndexOfFocusedObject(o:DisplayObject):int
  986. {
  987. if (!o)
  988. return -1;
  989. var n:int = focusableCandidates.length;
  990. // trace(" focusableCandidates " + n);
  991. var i:int = 0;
  992. for (i = 0; i < n; i++)
  993. {
  994. // trace(" comparing " + focusableCandidates[i]);
  995. if (focusableCandidates[i] == o)
  996. return i;
  997. }
  998. // no match? try again with a slower match for certain
  999. // cases like DG editors
  1000. for (i = 0; i < n; i++)
  1001. {
  1002. var iui:IUIComponent = focusableCandidates[i] as IUIComponent;
  1003. if (iui && iui.owns(o))
  1004. return i;
  1005. }
  1006. return -1;
  1007. }
  1008. private function getIndexOfNextObject(i:int, shiftKey:Boolean, bSearchAll:Boolean, groupName:String):int
  1009. {
  1010. var n:int = focusableCandidates.length;
  1011. var start:int = i;
  1012. while (true)
  1013. {
  1014. if (shiftKey)
  1015. i--;
  1016. else
  1017. i++;
  1018. if (bSearchAll)
  1019. {
  1020. if (shiftKey && i < 0)
  1021. break;
  1022. if (!shiftKey && i == n)
  1023. break;
  1024. }
  1025. else
  1026. {
  1027. i = (i + n) % n;
  1028. // came around and found the original
  1029. if (start == i)
  1030. break;
  1031. }
  1032. // trace("testing " + focusableCandidates[i]);
  1033. if (isValidFocusCandidate(focusableCandidates[i], groupName))
  1034. {
  1035. // trace(" stopped at " + i);
  1036. var o:DisplayObject = DisplayObject(findFocusManagerComponent2(focusableCandidates[i]));
  1037. if (o is IFocusManagerGroup)
  1038. {
  1039. // look around to see if there's a selected member in the tabgroup
  1040. // otherwise use the first one we found.
  1041. var tg1:IFocusManagerGroup = IFocusManagerGroup(o);
  1042. for (var j:int = 0; j < focusableCandidates.length; j++)
  1043. {
  1044. var obj:DisplayObject = focusableCandidates[j];
  1045. if (obj is IFocusManagerGroup)
  1046. {
  1047. var tg2:IFocusManagerGroup = IFocusManagerGroup(obj);
  1048. if (tg2.groupName == tg1.groupName && tg2.selected)
  1049. {
  1050. // if objects of same group have different tab index
  1051. // skip you aren't selected.
  1052. if (InteractiveObject(obj).tabIndex != InteractiveObject(o).tabIndex && !tg1.selected)
  1053. return getIndexOfNextObject(i, shiftKey, bSearchAll, groupName);
  1054. i = j;
  1055. break;
  1056. }
  1057. }
  1058. }
  1059. }
  1060. return i;
  1061. }
  1062. }
  1063. return i;
  1064. }
  1065. /**
  1066. * @private
  1067. */
  1068. private function setFocusToNextObject(event:FocusEvent):void
  1069. {
  1070. focusChanged = false;
  1071. if (focusableObjects.length == 0)
  1072. return;
  1073. var focusInfo:FocusInfo = getNextFocusManagerComponent2(event.shiftKey, fauxFocus);
  1074. // trace("winner = ", o);
  1075. // If we are about to wrap focus around, send focus back to the parent.
  1076. if (!popup && focusInfo.wrapped)
  1077. {
  1078. if (getParentBridge())
  1079. {
  1080. moveFocusToParent(event.shiftKey);
  1081. return;
  1082. }
  1083. }
  1084. setFocusToComponent(focusInfo.displayObject, event.shiftKey);
  1085. }
  1086. private function setFocusToComponent(o:Object, shiftKey:Boolean):void
  1087. {
  1088. focusChanged = false;
  1089. if (o)
  1090. {
  1091. if (o is ISWFLoader && ISWFLoader(o).swfBridge)
  1092. {
  1093. // send message to child swf to move focus.
  1094. // trace("pass focus from " + this.form.systemManager.loaderInfo.url + " to " + DisplayObject(o).loaderInfo.url);
  1095. var request:SWFBridgeRequest = new SWFBridgeRequest(SWFBridgeRequest.MOVE_FOCUS_REQUEST,
  1096. false, true, null,
  1097. shiftKey ? FocusRequestDirection.BOTTOM :
  1098. FocusRequestDirection.TOP);
  1099. var sandboxBridge:IEventDispatcher = ISWFLoader(o).swfBridge;
  1100. if (sandboxBridge)
  1101. {
  1102. sandboxBridge.dispatchEvent(request);
  1103. focusChanged = request.data;
  1104. }
  1105. }
  1106. else if (o is IFocusManagerComplexComponent)
  1107. {
  1108. IFocusManagerComplexComponent(o).assignFocus(shiftKey ? "bottom" : "top");
  1109. focusChanged = true;
  1110. }
  1111. else if (o is IFocusManagerComponent)
  1112. {
  1113. setFocus(IFocusManagerComponent(o));
  1114. focusChanged = true;
  1115. }
  1116. }
  1117. }
  1118. /**
  1119. * @private
  1120. */
  1121. private function setFocusToTop():void
  1122. {
  1123. setFocusToNextIndex(-1, false);
  1124. }
  1125. /**
  1126. * @private
  1127. */
  1128. private function setFocusToBottom():void
  1129. {
  1130. setFocusToNextIndex(focusableObjects.length, true);
  1131. }
  1132. /**
  1133. * @private
  1134. */
  1135. private function setFocusToNextIndex(index:int, shiftKey:Boolean):void
  1136. {
  1137. if (focusableObjects.length == 0)
  1138. return;
  1139. // I think we'll have time to do this here instead of at creation time
  1140. // this makes and orders the focusableCandidates array
  1141. if (calculateCandidates)
  1142. {
  1143. sortFocusableObjects();
  1144. calculateCandidates = false;
  1145. }
  1146. var focusInfo:FocusInfo = getNextFocusManagerComponent2(shiftKey, null, index);
  1147. // If we are about to wrap focus around, send focus back to the parent.
  1148. if (!popup && focusInfo.wrapped)
  1149. {
  1150. if (getParentBridge())
  1151. {
  1152. moveFocusToParent(shiftKey);
  1153. return;
  1154. }
  1155. }
  1156. setFocusToComponent(focusInfo.displayObject, shiftKey);
  1157. }
  1158. /**
  1159. * @inheritDoc
  1160. */
  1161. public function getNextFocusManagerComponent(
  1162. backward:Boolean = false):IFocusManagerComponent
  1163. {
  1164. return getNextFocusManagerComponent2(false, fauxFocus).displayObject as IFocusManagerComponent;
  1165. }
  1166. /**
  1167. * Find the next object to set focus to.
  1168. *
  1169. * @param backward true if moving in the backwards in the tab order, false if moving forward.
  1170. * @param fromObject object to move focus from, if null move from the current focus.
  1171. * @param formIndex index to move focus from, if specified use fromIndex to find the
  1172. * object, not fromObject.
  1173. */
  1174. private function getNextFocusManagerComponent2(
  1175. backward:Boolean = false,
  1176. fromObject:DisplayObject = null,
  1177. fromIndex:int = FROM_INDEX_UNSPECIFIED):FocusInfo
  1178. {
  1179. if (focusableObjects.length == 0)
  1180. return null;
  1181. // I think we'll have time to do this here instead of at creation time
  1182. // this makes and orders the focusableCandidates array
  1183. if (calculateCandidates)
  1184. {
  1185. sortFocusableObjects();
  1186. calculateCandidates = false;
  1187. }
  1188. // trace("focus was at " + o);
  1189. // trace("focusableObjects " + focusableObjects.length);
  1190. var i:int = fromIndex;
  1191. if (fromIndex == FROM_INDEX_UNSPECIFIED)
  1192. {
  1193. // if there is no passed in object, then get the object that has the focus
  1194. var o:DisplayObject = fromObject;
  1195. if (!o)
  1196. o = form.systemManager.stage.focus;
  1197. o = DisplayObject(findFocusManagerComponent2(InteractiveObject(o)));
  1198. var g:String = "";
  1199. if (o is IFocusManagerGroup)
  1200. {
  1201. var tg:IFocusManagerGroup = IFocusManagerGroup(o);
  1202. g = tg.groupName;
  1203. }
  1204. i = getIndexOfFocusedObject(o);
  1205. }
  1206. // trace(" starting at " + i);
  1207. var bSearchAll:Boolean = false;
  1208. var start:int = i;
  1209. if (i == -1) // we didn't find it
  1210. {
  1211. if (backward)
  1212. i = focusableCandidates.length;
  1213. bSearchAll = true;
  1214. // trace("search all " + i);
  1215. }
  1216. var j:int = getIndexOfNextObject(i, backward, bSearchAll, g);
  1217. // if we wrapped around, get if we have a parent we should pass
  1218. // focus to.
  1219. var wrapped:Boolean = false;
  1220. if (backward)
  1221. {
  1222. if (j >= i)
  1223. wrapped = true;
  1224. }
  1225. else if (j <= i)
  1226. wrapped = true;
  1227. var focusInfo:FocusInfo = new FocusInfo();
  1228. focusInfo.displayObject = findFocusManagerComponent2(focusableCandidates[j]);
  1229. focusInfo.wrapped = wrapped;
  1230. return focusInfo;
  1231. }
  1232. /**
  1233. * @private
  1234. */
  1235. private function getTopLevelFocusTarget(o:InteractiveObject):InteractiveObject
  1236. {
  1237. while (o != InteractiveObject(form))
  1238. {
  1239. if (o is IFocusManagerComponent &&
  1240. IFocusManagerComponent(o).focusEnabled &&
  1241. IFocusManagerComponent(o).mouseFocusEnabled &&
  1242. (o is IUIComponent ? IUIComponent(o).enabled : true))
  1243. return o;
  1244. // if we cross a boundry into a bridged application, then return null so
  1245. // the target is only processed at the lowest level
  1246. if (o.parent is ISWFLoader)
  1247. {
  1248. if (ISWFLoader(o.parent).swfBridge)
  1249. return null;
  1250. }
  1251. o = o.parent;
  1252. if (o == null)
  1253. break;
  1254. }
  1255. return null;
  1256. }
  1257. /**
  1258. * Returns a String representation of the component hosting the FocusManager object,
  1259. * with the String <code>".focusManager"</code> appended to the end of the String.
  1260. *
  1261. * @return Returns a String representation of the component hosting the FocusManager object,
  1262. * with the String <code>".focusManager"</code> appended to the end of the String.
  1263. */
  1264. public function toString():String
  1265. {
  1266. return Object(form).toString() + ".focusManager";
  1267. }
  1268. //--------------------------------------------------------------------------
  1269. //
  1270. // Event handlers
  1271. //
  1272. //--------------------------------------------------------------------------
  1273. /**
  1274. * @private
  1275. * Listen for children being added
  1276. * and see if they are focus candidates.
  1277. */
  1278. private function addedHandler(event:Event):void
  1279. {
  1280. var target:DisplayObject = DisplayObject(event.target);
  1281. // trace("FM: addedHandler: got added for " + target);
  1282. // if it is truly parented, add it, otherwise it will get added when the top of the tree
  1283. // gets parented.
  1284. if (target.stage)
  1285. {
  1286. // trace("FM: addedHandler: adding focusables");
  1287. addFocusables(DisplayObject(event.target));
  1288. }
  1289. }
  1290. /**
  1291. * @private
  1292. * Listen for children being removed.
  1293. */
  1294. private function removedHandler(event:Event):void
  1295. {
  1296. var i:int;
  1297. var o:DisplayObject = DisplayObject(event.target);
  1298. // trace("FM got added for " + event.target);
  1299. if (o is IFocusManagerComponent)
  1300. {
  1301. for (i = 0; i < focusableObjects.length; i++)
  1302. {
  1303. if (o == focusableObjects[i])
  1304. {
  1305. if (o == _lastFocus)
  1306. {
  1307. _lastFocus.drawFocus(false);
  1308. _lastFocus = null;
  1309. }
  1310. // trace("FM removed " + o);
  1311. o.removeEventListener("tabEnabledChange", tabEnabledChangeHandler);
  1312. o.removeEventListener("tabIndexChange", tabIndexChangeHandler);
  1313. focusableObjects.splice(i, 1);
  1314. focusableCandidates = [];
  1315. calculateCandidates = true;
  1316. break;
  1317. }
  1318. }
  1319. }
  1320. removeFocusables(o, false);
  1321. }
  1322. /**
  1323. * After the form is added to the stage, if there are no focusable objects,
  1324. * add the form and its children to the list of focuable objects because
  1325. * this application may have been loaded before the
  1326. * top-level system manager was added to the stage.
  1327. */
  1328. private function addedToStageHandler(event:Event):void
  1329. {
  1330. _form.removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
  1331. if (focusableObjects.length == 0)
  1332. {
  1333. addFocusables(DisplayObject(_form));
  1334. calculateCandidates = true;
  1335. }
  1336. }
  1337. /**
  1338. * @private
  1339. */
  1340. private function removeFocusables(o:DisplayObject, dontRemoveTabChildrenHandler:Boolean):void
  1341. {
  1342. var i:int;
  1343. if (o is DisplayObjectContainer)
  1344. {
  1345. if (!dontRemoveTabChildrenHandler)
  1346. o.removeEventListener("tabChildrenChange", tabChildrenChangeHandler);
  1347. for (i = 0; i < focusableObjects.length; i++)
  1348. {
  1349. if (isParent(DisplayObjectContainer(o), focusableObjects[i]))
  1350. {
  1351. if (focusableObjects[i] == _lastFocus)
  1352. {
  1353. _lastFocus.drawFocus(false);
  1354. _lastFocus = null;
  1355. }
  1356. // trace("FM removed " + focusableObjects[i]);
  1357. focusableObjects[i].removeEventListener(
  1358. "tabEnabledChange", tabEnabledChangeHandler);
  1359. focusableObjects[i].removeEventListener(
  1360. "tabIndexChange", tabIndexChangeHandler);
  1361. focusableObjects.splice(i, 1);
  1362. i = i - 1; // because increment would skip one
  1363. focusableCandidates = [];
  1364. calculateCandidates = true;
  1365. }
  1366. }
  1367. }
  1368. }
  1369. /**
  1370. * @private
  1371. */
  1372. private function showHandler(event:Event):void
  1373. {
  1374. form.systemManager.activate(form);
  1375. }
  1376. /**
  1377. * @private
  1378. */
  1379. private function hideHandler(event:Event):void
  1380. {
  1381. form.systemManager.deactivate(form);
  1382. }
  1383. /**
  1384. * @private
  1385. */
  1386. private function creationCompleteHandler(event:FlexEvent):void
  1387. {
  1388. var o:DisplayObject = DisplayObject(form);
  1389. if (o.parent && o.visible && !activated)
  1390. form.systemManager.activate(form);
  1391. }
  1392. /**
  1393. * @private
  1394. * Add or remove if tabbing properties change.
  1395. */
  1396. private function tabIndexChangeHandler(event:Event):void
  1397. {
  1398. calculateCandidates = true;
  1399. }
  1400. /**
  1401. * @private
  1402. * Add or remove if tabbing properties change.
  1403. */
  1404. private function tabEnabledChangeHandler(event:Event):void
  1405. {
  1406. calculateCandidates = true;
  1407. var o:InteractiveObject = InteractiveObject(event.target);
  1408. var n:int = focusableObjects.length;
  1409. for (var i:int = 0; i < n; i++)
  1410. {
  1411. if (focusableObjects[i] == o)
  1412. break;
  1413. }
  1414. if (o.tabEnabled)
  1415. {
  1416. if (i == n && isTabVisible(o))
  1417. {
  1418. // trace("FM tpc added " + o);
  1419. // add it if were not already
  1420. if (focusableObjects.indexOf(o) == -1)
  1421. focusableObjects.push(o);
  1422. }
  1423. }
  1424. else
  1425. {
  1426. // remove it
  1427. if (i < n)
  1428. {
  1429. // trace("FM tpc removed " + o);
  1430. focusableObjects.splice(i, 1);
  1431. }
  1432. }
  1433. }
  1434. /**
  1435. * @private
  1436. * Add or remove if tabbing properties change.
  1437. */
  1438. private function tabChildrenChangeHandler(event:Event):void
  1439. {
  1440. if (event.target != event.currentTarget)
  1441. return;
  1442. calculateCandidates = true;
  1443. var o:DisplayObjectContainer = DisplayObjectContainer(event.target);
  1444. if (o.tabChildren)
  1445. {
  1446. addFocusables(o, true);
  1447. }
  1448. else
  1449. {
  1450. removeFocusables(o, true);
  1451. }
  1452. }
  1453. /**
  1454. * @private
  1455. * This gets called when mouse clicks on a focusable object.
  1456. * We block player behavior
  1457. */
  1458. private function mouseFocusChangeHandler(event:FocusEvent):void
  1459. {
  1460. // trace("FocusManager: mouseFocusChangeHandler in = " + this._form.systemManager.loaderInfo.url);
  1461. // trace("FocusManager: mouseFocusChangeHandler " + event);
  1462. // If relatedObject is null because we don't have access to the
  1463. // object getting focus then allow the Player to set focus
  1464. // to the object. The isRelatedObjectInaccessible property is
  1465. // Player 10 only so we have to test if it is available. We
  1466. // will only see isRelatedObjectInaccessible if we are a version "10" swf
  1467. // (-target-player=10). Version "9" swfs will not see the property
  1468. // even if running in Player 10.
  1469. if (event.relatedObject == null &&
  1470. "isRelatedObjectInaccessible" in event &&
  1471. event["isRelatedObjectInaccessible"] == true)
  1472. {
  1473. // lost focus to a control in different sandbox.
  1474. return;
  1475. }
  1476. if (event.relatedObject is TextField)
  1477. {
  1478. var tf:TextField = event.relatedObject as TextField;
  1479. if (tf.type == "input" || tf.selectable)
  1480. {
  1481. return; // pass it on
  1482. }
  1483. }
  1484. event.preventDefault();
  1485. }
  1486. /**
  1487. * @private
  1488. * This gets called when the tab key is hit.
  1489. */
  1490. private function keyFocusChangeHandler(event:FocusEvent):void
  1491. {
  1492. // trace("keyFocusChangeHandler handled by " + this);
  1493. // trace("keyFocusChangeHandler event = " + event);
  1494. var sm:ISystemManager = form.systemManager;
  1495. // if the target is in a bridged application, let it handle the click.
  1496. if (sm.isDisplayObjectInABridgedApplication(DisplayObject(event.target)))
  1497. return;
  1498. showFocusIndicator = true;
  1499. focusChanged = false;
  1500. if (event.keyCode == Keyboard.TAB && !event.isDefaultPrevented())
  1501. {
  1502. if (browserFocusComponent)
  1503. {
  1504. if (browserFocusComponent.tabIndex == LARGE_TAB_INDEX)
  1505. browserFocusComponent.tabIndex = -1;
  1506. browserFocusComponent = null;
  1507. if (SystemManager(form.systemManager).useSWFBridge())
  1508. {
  1509. // out of children, pass focus to parent
  1510. moveFocusToParent(event.shiftKey);
  1511. if (focusChanged)
  1512. event.preventDefault();
  1513. }
  1514. return;
  1515. }
  1516. // trace("tabHandled by " + this);
  1517. setFocusToNextObject(event);
  1518. if (focusChanged)
  1519. event.preventDefault();
  1520. }
  1521. }
  1522. /**
  1523. * @private
  1524. * Watch for TAB keys.
  1525. */
  1526. private function keyDownHandler(event:KeyboardEvent):void
  1527. {
  1528. // trace("onKeyDown handled by " + this);
  1529. // trace("onKeyDown event = " + event);
  1530. // if the target is in a bridged application, let it handle the click.
  1531. var sm:ISystemManager = form.systemManager;
  1532. if (sm.isDisplayObjectInABridgedApplication(DisplayObject(event.target)))
  1533. return;
  1534. if (sm is SystemManager)
  1535. SystemManager(sm).idleCounter = 0;
  1536. if (event.keyCode == Keyboard.TAB)
  1537. {
  1538. lastAction = "KEY";
  1539. // I think we'll have time to do this here instead of at creation time
  1540. // this makes and orders the focusableCandidates array
  1541. if (calculateCandidates)
  1542. {
  1543. sortFocusableObjects();
  1544. calculateCandidates = false;
  1545. }
  1546. }
  1547. if (browserMode)
  1548. {
  1549. if (event.keyCode == Keyboard.TAB && focusableCandidates.length > 0)
  1550. {
  1551. // get the object that has the focus
  1552. var o:DisplayObject = fauxFocus;
  1553. if (!o)
  1554. {
  1555. o = form.systemManager.stage.focus;
  1556. }
  1557. // trace("focus was at " + o);
  1558. // trace("focusableObjects " + focusableObjects.length);
  1559. o = DisplayObject(findFocusManagerComponent2(InteractiveObject(o)));
  1560. var g:String = "";
  1561. if (o is IFocusManagerGroup)
  1562. {
  1563. var tg:IFocusManagerGroup = IFocusManagerGroup(o);
  1564. g = tg.groupName;
  1565. }
  1566. var i:int = getIndexOfFocusedObject(o);
  1567. var j:int = getIndexOfNextObject(i, event.shiftKey, false, g);
  1568. if (event.shiftKey)
  1569. {
  1570. if (j >= i)
  1571. {
  1572. // we wrapped so let browser have it
  1573. browserFocusComponent = getBrowserFocusComponent(event.shiftKey);
  1574. if (browserFocusComponent.tabIndex == -1)
  1575. browserFocusComponent.tabIndex = 0;
  1576. }
  1577. }
  1578. else
  1579. {
  1580. if (j <= i)
  1581. {
  1582. // we wrapped so let browser have it
  1583. browserFocusComponent = getBrowserFocusComponent(event.shiftKey);
  1584. if (browserFocusComponent.tabIndex == -1)
  1585. browserFocusComponent.tabIndex = LARGE_TAB_INDEX;
  1586. }
  1587. }
  1588. }
  1589. }
  1590. }
  1591. /**
  1592. * @private
  1593. * Watch for ENTER key.
  1594. */
  1595. private function defaultButtonKeyHandler(event:KeyboardEvent):void
  1596. {
  1597. var sm:ISystemManager = form.systemManager;
  1598. if (sm.isDisplayObjectInABridgedApplication(DisplayObject(event.target)))
  1599. return;
  1600. if (defaultButtonEnabled && event.keyCode == Keyboard.ENTER &&
  1601. defaultButton && defButton.enabled)
  1602. {
  1603. sendDefaultButtonEvent();
  1604. }
  1605. }
  1606. /**
  1607. * @private
  1608. * This gets called when the focus changes due to a mouse click.
  1609. *
  1610. * Note: If the focus is changing to a TextField, we don't call
  1611. * setFocus() on it because the player handles it;
  1612. * calling setFocus() on a TextField which has scrollable text
  1613. * causes the text to autoscroll to the end, making the
  1614. * mouse click set the insertion point in the wrong place.
  1615. */
  1616. private function mouseDownHandler(event:MouseEvent):void
  1617. {
  1618. // trace("FocusManager mouseDownHandler in = " + this._form.systemManager.loaderInfo.url);
  1619. // trace("FocusManager mouseDownHandler target " + event.target);
  1620. if (event.isDefaultPrevented())
  1621. return;
  1622. // if the target is in a bridged application, let it handle the click.
  1623. var sm:ISystemManager = form.systemManager;
  1624. var o:DisplayObject = getTopLevelFocusTarget(
  1625. InteractiveObject(event.target));
  1626. if (!o)
  1627. return;
  1628. showFocusIndicator = false;
  1629. // trace("FocusManager mouseDownHandler on " + o);
  1630. // Make sure the containing component gets notified.
  1631. // As the note above says, we don't set focus to a TextField ever
  1632. // because the player already did and took care of where
  1633. // the insertion point is, and we also don't call setfocus
  1634. // on a component that last the last focused object unless
  1635. // the last action was just to activate the player and didn't
  1636. // involve tabbing or clicking on a component
  1637. if ((o != _lastFocus || lastAction == "ACTIVATE") && !(o is TextField))
  1638. setFocus(IFocusManagerComponent(o));
  1639. else if (_lastFocus)
  1640. // trace("FM: skipped setting focus to " + _lastFocus);
  1641. // if in a sandbox, create a focus-in event and dispatch.
  1642. if (!_lastFocus && o is IEventDispatcher && SystemManager(form.systemManager).useSWFBridge())
  1643. IEventDispatcher(o).dispatchEvent(new FocusEvent(FocusEvent.FOCUS_IN));
  1644. lastAction = "MOUSEDOWN";
  1645. dispatchActivatedFocusManagerEvent(null);
  1646. lastActiveFocusManager = this;
  1647. }
  1648. /**
  1649. * @private
  1650. *
  1651. * A request across a bridge from another FocusManager to change the
  1652. * focus.
  1653. */
  1654. private function focusRequestMoveHandler(event:Event):void
  1655. {
  1656. // trace("focusRequestHandler in = " + this._form.systemManager.loaderInfo.url);
  1657. // ignore messages we send to ourselves.
  1658. if (event is SWFBridgeRequest)
  1659. {
  1660. // trace("ignored focus in " + this._form.systemManager.loaderInfo.url);
  1661. return;
  1662. }
  1663. focusSetLocally = false;
  1664. var request:SWFBridgeRequest = SWFBridgeRequest.marshal(event);
  1665. if (request.data == FocusRequestDirection.TOP || request.data == FocusRequestDirection.BOTTOM)
  1666. {
  1667. // move focus to the top or bottom child. If there are no children then
  1668. // send focus back up to the parent.
  1669. if (focusableObjects.length == 0)
  1670. {
  1671. // trace("focusRequestMoveHandler: no focusable objects, setting focus back to parent");
  1672. moveFocusToParent(request.data == FocusRequestDirection.TOP ? false : true);
  1673. event["data"] = focusChanged;
  1674. return;
  1675. }
  1676. if (request.data == FocusRequestDirection.TOP)
  1677. {
  1678. setFocusToTop();
  1679. }
  1680. else
  1681. {
  1682. setFocusToBottom();
  1683. }
  1684. event["data"] = focusChanged;
  1685. }
  1686. else
  1687. {
  1688. // move forward or backward
  1689. var startingPosition:DisplayObject = DisplayObject(_form.systemManager.
  1690. swfBridgeGroup.getChildBridgeProvider(IEventDispatcher(event.target)));
  1691. moveFocus(request.data as String, startingPosition);
  1692. event["data"] = focusChanged;
  1693. }
  1694. if (focusSetLocally)
  1695. {
  1696. dispatchActivatedFocusManagerEvent(null);
  1697. lastActiveFocusManager = this;
  1698. }
  1699. }
  1700. private function focusRequestActivateHandler(event:Event):void
  1701. {
  1702. // trace("FM focusRequestActivateHandler");
  1703. skipBridge = IEventDispatcher(event.target);
  1704. activate();
  1705. skipBridge = null;
  1706. }
  1707. private function focusRequestDeactivateHandler(event:Event):void
  1708. {
  1709. // trace("FM focusRequestDeactivateHandler");
  1710. skipBridge = IEventDispatcher(event.target);
  1711. deactivate();
  1712. skipBridge = null;
  1713. }
  1714. private function bridgeEventActivateHandler(event:Event):void
  1715. {
  1716. // ignore message to self
  1717. if (event is SWFBridgeEvent)
  1718. return;
  1719. //trace("FM bridgeEventActivateHandler for " + form.systemManager.loaderInfo.url);
  1720. // clear last focus if we aren't active.
  1721. lastActiveFocusManager = null;
  1722. _lastFocus = null;
  1723. dispatchActivatedFocusManagerEvent(IEventDispatcher(event.target));
  1724. }
  1725. /**
  1726. * @private
  1727. *
  1728. * A request across a bridge from another FocusManager to change the
  1729. * value of the setShowFocusIndicator property.
  1730. */
  1731. private function setShowFocusIndicatorRequestHandler(event:Event):void
  1732. {
  1733. // ignore messages we send to ourselves.
  1734. if (event is SWFBridgeRequest)
  1735. return;
  1736. var request:SWFBridgeRequest = SWFBridgeRequest.marshal(event);
  1737. _showFocusIndicator = request.data;
  1738. // relay the message to parent and children
  1739. dispatchSetShowFocusIndicatorRequest(_showFocusIndicator, IEventDispatcher(event.target));
  1740. }
  1741. /**
  1742. * This is called on the top-level focus manager and the parent focus
  1743. * manager for each new bridge of focusable content created.
  1744. * When the parent focus manager of the new focusable content is
  1745. * called, focusable content will become part of the tab order.
  1746. * When the top-level focus manager is called the bridge becomes
  1747. * one of the focus managers managed by the top-level focus manager.
  1748. */
  1749. public function addSWFBridge(bridge:IEventDispatcher, owner:DisplayObject):void
  1750. {
  1751. // trace("FocusManager.addFocusManagerBridge: in = " + this._form.systemManager.loaderInfo.url);
  1752. if (!owner)
  1753. return;
  1754. var sm:ISystemManager = _form.systemManager;
  1755. if (focusableObjects.indexOf(owner) == -1)
  1756. {
  1757. focusableObjects.push(owner);
  1758. calculateCandidates = true;
  1759. }
  1760. // listen for move requests from the bridge.
  1761. swfBridgeGroup.addChildBridge(bridge, ISWFBridgeProvider(owner));
  1762. bridge.addEventListener(SWFBridgeRequest.MOVE_FOCUS_REQUEST, focusRequestMoveHandler);
  1763. bridge.addEventListener(SWFBridgeRequest.SET_SHOW_FOCUS_INDICATOR_REQUEST,
  1764. setShowFocusIndicatorRequestHandler);
  1765. bridge.addEventListener(SWFBridgeEvent.BRIDGE_FOCUS_MANAGER_ACTIVATE,
  1766. bridgeEventActivateHandler);
  1767. }
  1768. /**
  1769. * @inheritdoc
  1770. */
  1771. public function removeSWFBridge(bridge:IEventDispatcher):void
  1772. {
  1773. var sm:ISystemManager = _form.systemManager;
  1774. var displayObject:DisplayObject = DisplayObject(swfBridgeGroup.getChildBridgeProvider(bridge));
  1775. if (displayObject)
  1776. {
  1777. var index:int = focusableObjects.indexOf(displayObject);
  1778. if (index != -1)
  1779. {
  1780. focusableObjects.splice(index, 1);
  1781. calculateCandidates = true;
  1782. }
  1783. }
  1784. else
  1785. throw new Error(); // should never get here.
  1786. bridge.removeEventListener(SWFBridgeRequest.MOVE_FOCUS_REQUEST, focusRequestMoveHandler);
  1787. bridge.removeEventListener(SWFBridgeRequest.SET_SHOW_FOCUS_INDICATOR_REQUEST,
  1788. setShowFocusIndicatorRequestHandler);
  1789. bridge.removeEventListener(SWFBridgeEvent.BRIDGE_FOCUS_MANAGER_ACTIVATE,
  1790. bridgeEventActivateHandler);
  1791. swfBridgeGroup.removeChildBridge(bridge);
  1792. }
  1793. /**
  1794. */
  1795. private function removeFromParentBridge(event:Event):void
  1796. {
  1797. // add ourselves to our parent focus manager if this is a bridged
  1798. // application not a dialog or other popup.
  1799. var sm:ISystemManager = form.systemManager;
  1800. if (sm.useSWFBridge())
  1801. {
  1802. sm.removeEventListener(SWFBridgeEvent.BRIDGE_APPLICATION_UNLOADING, removeFromParentBridge);
  1803. // have the child listen to move requests from the parent.
  1804. var bridge:IEventDispatcher = swfBridgeGroup.parentBridge;
  1805. if (bridge)
  1806. {
  1807. bridge.removeEventListener(SWFBridgeRequest.MOVE_FOCUS_REQUEST, focusRequestMoveHandler);
  1808. bridge.removeEventListener(SWFBridgeRequest.SET_SHOW_FOCUS_INDICATOR_REQUEST,
  1809. setShowFocusIndicatorRequestHandler);
  1810. }
  1811. // add listener activate/deactivate requests
  1812. if (bridge && !(form.systemManager is SystemManagerProxy))
  1813. {
  1814. bridge.removeEventListener(SWFBridgeRequest.ACTIVATE_FOCUS_REQUEST, focusRequestActivateHandler);
  1815. bridge.removeEventListener(SWFBridgeRequest.DEACTIVATE_FOCUS_REQUEST, focusRequestDeactivateHandler);
  1816. bridge.removeEventListener(SWFBridgeEvent.BRIDGE_FOCUS_MANAGER_ACTIVATE,
  1817. bridgeEventActivateHandler);
  1818. }
  1819. }
  1820. }
  1821. /**
  1822. * @private
  1823. *
  1824. * Send a message to the parent to move focus a component in the parent.
  1825. *
  1826. * @param shiftKey - if true move focus to a component
  1827. *
  1828. * @return true if focus moved to parent, false otherwise.
  1829. */
  1830. private function moveFocusToParent(shiftKey:Boolean):Boolean
  1831. {
  1832. // trace("pass focus from " + this.form.systemManager.loaderInfo.url + " to parent ");
  1833. var request:SWFBridgeRequest = new SWFBridgeRequest(SWFBridgeRequest.MOVE_FOCUS_REQUEST,
  1834. false, true, null,
  1835. shiftKey ? FocusRequestDirection.BACKWARD :
  1836. FocusRequestDirection.FORWARD);
  1837. var sandboxBridge:IEventDispatcher = _form.systemManager.swfBridgeGroup.parentBridge;
  1838. // the handler will set the data property to whether focus changed
  1839. sandboxBridge.dispatchEvent(request);
  1840. focusChanged = request.data;
  1841. return focusChanged;
  1842. }
  1843. /**
  1844. * Get the bridge to the parent focus manager.
  1845. *
  1846. * @return parent bridge or null if there is no parent bridge.
  1847. */
  1848. private function getParentBridge():IEventDispatcher
  1849. {
  1850. if (swfBridgeGroup)
  1851. return swfBridgeGroup.parentBridge;
  1852. return null;
  1853. }
  1854. /**
  1855. * @private
  1856. *
  1857. * Send a request for all other focus managers to update
  1858. * their ShowFocusIndicator property.
  1859. */
  1860. private function dispatchSetShowFocusIndicatorRequest(value:Boolean, skip:IEventDispatcher):void
  1861. {
  1862. var request:SWFBridgeRequest = new SWFBridgeRequest(
  1863. SWFBridgeRequest.SET_SHOW_FOCUS_INDICATOR_REQUEST,
  1864. false,
  1865. false,
  1866. null, // bridge is set before it is dispatched
  1867. value);
  1868. dispatchEventFromSWFBridges(request, skip);
  1869. }
  1870. /**
  1871. * @private
  1872. *
  1873. * Broadcast an ACTIVATED_FOCUS_MANAGER message.
  1874. *
  1875. * @param eObj if a SandboxBridgeEvent, then propagate the message,
  1876. * if null, start a new message.
  1877. */
  1878. private function dispatchActivatedFocusManagerEvent(skip:IEventDispatcher = null):void
  1879. {
  1880. // Don't send the ACTIVATED_FOCUS_MANAGER message if we are already
  1881. // active.
  1882. if (lastActiveFocusManager == this)
  1883. {
  1884. // trace("FM: dispatchActivatedFocusManagerEvent already active, skipping messages");
  1885. return; // already active
  1886. }
  1887. var event:SWFBridgeEvent = new SWFBridgeEvent(SWFBridgeEvent.BRIDGE_FOCUS_MANAGER_ACTIVATE);
  1888. dispatchEventFromSWFBridges(event, skip);
  1889. }
  1890. /**
  1891. * A Focus Manager has its own set of child bridges that may be different from the child
  1892. * bridges of its System Manager if the Focus Manager is managing a pop up. In the case of
  1893. * a pop up don't send messages to the SM parent bridge because that will be the form. But
  1894. * do send the messages to the bridges in bridgeFocusManagers dictionary.
  1895. */
  1896. private function dispatchEventFromSWFBridges(event:Event, skip:IEventDispatcher = null):void
  1897. {
  1898. var clone:Event;
  1899. // trace(">>dispatchEventFromSWFBridges", this, event.type);
  1900. var sm:ISystemManager = form.systemManager;
  1901. if (!popup)
  1902. {
  1903. var parentBridge:IEventDispatcher = swfBridgeGroup.parentBridge;
  1904. if (parentBridge && parentBridge != skip)
  1905. {
  1906. // Ensure the requestor property has the correct bridge.
  1907. clone = event.clone();
  1908. if (clone is SWFBridgeRequest)
  1909. SWFBridgeRequest(clone).requestor = parentBridge;
  1910. parentBridge.dispatchEvent(clone);
  1911. }
  1912. }
  1913. var children:Array = swfBridgeGroup.getChildBridges();
  1914. for (var i:int = 0; i < children.length; i++)
  1915. {
  1916. if (children[i] != skip)
  1917. {
  1918. // trace("send to child", i, event.type);
  1919. clone = event.clone();
  1920. // Ensure the requestor property has the correct bridge.
  1921. if (clone is SWFBridgeRequest)
  1922. SWFBridgeRequest(clone).requestor = IEventDispatcher(children[i]);
  1923. IEventDispatcher(children[i]).dispatchEvent(clone);
  1924. }
  1925. }
  1926. // trace("<<dispatchEventFromSWFBridges", this, event.type);
  1927. }
  1928. private function getBrowserFocusComponent(shiftKey:Boolean):InteractiveObject
  1929. {
  1930. var focusComponent:InteractiveObject = form.systemManager.stage.focus;
  1931. // if the focus is null it means focus is in an application we
  1932. // don't have access to. Use either the last object or the first
  1933. // object in this focus manager's list.
  1934. if (!focusComponent)
  1935. {
  1936. var index:int = shiftKey ? 0 : focusableCandidates.length - 1;
  1937. focusComponent = focusableCandidates[index];
  1938. }
  1939. return focusComponent;
  1940. }
  1941. }
  1942. }
  1943. import flash.display.DisplayObject;
  1944. /**
  1945. * @private
  1946. *
  1947. * Plain old class to return multiple items of info about the potential
  1948. * change in focus.
  1949. */
  1950. class FocusInfo
  1951. {
  1952. public var displayObject:DisplayObject; // object to get focus
  1953. public var wrapped:Boolean; // true if focus wrapped around
  1954. }