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