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