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