| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // Licensed to the Apache Software Foundation (ASF) under one or more |
| // contributor license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright ownership. |
| // The ASF licenses this file to You under the Apache License, Version 2.0 |
| // (the "License"); you may not use this file except in compliance with |
| // the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| package mx.managers.systemClasses |
| { |
| |
| import flash.display.DisplayObject; |
| import flash.display.InteractiveObject; |
| import flash.display.Sprite; |
| import flash.events.Event; |
| import flash.events.EventDispatcher; |
| import flash.events.FocusEvent; |
| import flash.events.IEventDispatcher; |
| import flash.events.MouseEvent; |
| |
| import mx.core.IChildList; |
| import mx.core.IFlexModuleFactory; |
| import mx.core.IRawChildrenContainer; |
| import mx.core.IUIComponent; |
| import mx.core.Singleton; |
| import mx.core.mx_internal; |
| import mx.events.DynamicEvent; |
| import mx.events.Request; |
| import mx.managers.IActiveWindowManager; |
| import mx.managers.IFocusManagerContainer; |
| import mx.managers.ISystemManager; |
| |
| use namespace mx_internal; |
| |
| [ExcludeClass] |
| [Mixin] |
| |
| public class ActiveWindowManager extends EventDispatcher implements IActiveWindowManager |
| { |
| include "../../core/Version.as"; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Class Method |
| // |
| //-------------------------------------------------------------------------- |
| |
| public static function init(fbs:IFlexModuleFactory):void |
| { |
| Singleton.registerClass("mx.managers::IActiveWindowManager", ActiveWindowManager); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Constructor. |
| * |
| * <p>This is the starting point for all Flex applications. |
| * This class is set to be the root class of a Flex SWF file. |
| * Flash Player instantiates an instance of this class, |
| * causing this constructor to be called.</p> |
| */ |
| public function ActiveWindowManager(systemManager:ISystemManager = null) |
| { |
| super(); |
| |
| if (!systemManager) |
| return; |
| |
| this.systemManager = systemManager; |
| // capture mouse down so we can switch top level windows and activate |
| // the right focus manager before the components inside start |
| // processing the event |
| if (systemManager.isTopLevelRoot() || systemManager.getSandboxRoot() == systemManager) |
| systemManager.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler, true); |
| |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var systemManager:ISystemManager; |
| |
| /** |
| * @private |
| * List of top level windows. |
| */ |
| mx_internal var forms:Array = []; |
| |
| /** |
| * @private |
| * The current top level window. |
| * |
| * Will be of type IFocusManagerContainer if the form |
| * in the top-level system manager's application domain |
| * or a child of that application domain. Otherwise the |
| * form will be of type RemotePopUp. |
| */ |
| mx_internal var form:Object; |
| |
| |
| //---------------------------------- |
| // numModalWindows |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the numModalWindows property. |
| */ |
| private var _numModalWindows:int = 0; |
| |
| /** |
| * The number of modal windows. Modal windows don't allow |
| * clicking in another windows which would normally |
| * activate the FocusManager in that window. The PopUpManager |
| * modifies this count as it creates and destroys modal windows. |
| */ |
| public function get numModalWindows():int |
| { |
| return _numModalWindows; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set numModalWindows(value:int):void |
| { |
| _numModalWindows = value; |
| systemManager.numModalWindows = value; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods: Focus |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @inheritDoc |
| */ |
| public function activate(f:Object):void |
| { |
| activateForm(f); |
| } |
| |
| /** |
| * @private |
| * |
| * New version of activate that does not require a |
| * IFocusManagerContainer. |
| */ |
| private function activateForm(f:Object):void |
| { |
| var e:DynamicEvent; |
| |
| // trace("SM: activate " + f + " " + forms.length); |
| if (form) |
| { |
| if (form != f && forms.length > 1) |
| { |
| if (hasEventListener("activateForm")) |
| { |
| e = new DynamicEvent("activateForm", false, true); |
| e.form = f; |
| } |
| // Switch the active form. |
| if (!e || dispatchEvent(e)) |
| { |
| var z:IFocusManagerContainer = IFocusManagerContainer(form); |
| // trace("OLW " + f + " deactivating old form " + z); |
| z.focusManager.deactivate(); |
| } |
| } |
| } |
| |
| form = f; |
| |
| var e2:DynamicEvent; |
| if (hasEventListener("activatedForm")) |
| { |
| e2 = new DynamicEvent("activatedForm", false, true); |
| e2.form = f; |
| } |
| if (!e2 || dispatchEvent(e2)) |
| { |
| if (f.focusManager) |
| { |
| // trace("has focus manager"); |
| f.focusManager.activate(); |
| } |
| } |
| // trace("END SM: activate " + f); |
| } |
| |
| /** |
| * @inheritDoc |
| */ |
| public function deactivate(f:Object):void |
| { |
| deactivateForm(Object(f)); |
| } |
| |
| /** |
| * @private |
| * |
| * New version of deactivate that works with remote pop ups. |
| * |
| */ |
| private function deactivateForm(f:Object):void |
| { |
| var e:DynamicEvent; |
| |
| // trace(">>SM: deactivate " + f); |
| |
| if (form) |
| { |
| // If there's more than one form and this is it, find a new form. |
| if (form == f && forms.length > 1) |
| { |
| if (hasEventListener("deactivateForm")) |
| { |
| e = new DynamicEvent("deactivateForm", false, true); |
| e.form = form; |
| } |
| if (!e || dispatchEvent(e)) |
| form.focusManager.deactivate(); |
| |
| form = findLastActiveForm(f); |
| |
| var e2:DynamicEvent; |
| // make sure we have a valid top level window. |
| // This can be null if top level window has been hidden for some reason. |
| if (form) |
| { |
| if (hasEventListener("deactivatedForm")) |
| { |
| e2 = new DynamicEvent("deactivatedForm", false, true); |
| e2.form = form; |
| } |
| if (!e2 || dispatchEvent(e2)) |
| { |
| // make sure we have a valid top level window. |
| // This can be null if top level window has been hidden for some reason. |
| if (form) |
| { |
| form.focusManager.activate(); |
| } |
| } |
| } |
| } |
| } |
| |
| // trace("<<SM: deactivate " + f); |
| } |
| |
| |
| /** |
| * @private |
| * |
| * @param f form being deactivated |
| * |
| * @return the next form to activate, excluding the form being deactivated. |
| */ |
| private function findLastActiveForm(f:Object):Object |
| { |
| var n:int = forms.length; |
| for (var i:int = forms.length - 1; i >= 0; i--) |
| { |
| // Verify the form is visible and enabled |
| if (!areFormsEqual(forms[i], f) && canActivatePopUp(forms[i])) |
| return forms[i]; |
| } |
| |
| return null; // should never get here |
| } |
| |
| /** |
| * Test if two forms are equal. |
| * |
| * @param form1 - may be of type a DisplayObjectContainer or a RemotePopUp |
| * @param form2 - may be of type a DisplayObjectContainer or a RemotePopUp |
| * |
| * @return true if the forms are equal, false otherwise. |
| */ |
| private function areFormsEqual(form1:Object, form2:Object):Boolean |
| { |
| if (form1 == form2) |
| return true; |
| |
| // if the forms are both remote forms, then compare them, otherwise |
| // return false. |
| if (form1 is RemotePopUp && form2 is RemotePopUp) |
| { |
| return areRemotePopUpsEqual(form1, form2); |
| } |
| |
| return false; |
| } |
| |
| /** |
| * @private |
| * |
| * @return true if form1 and form2 are both of type RemotePopUp and are equal, false otherwise. |
| */ |
| private static function areRemotePopUpsEqual(form1:Object, form2:Object):Boolean |
| { |
| if (!(form1 is RemotePopUp)) |
| return false; |
| |
| if (!(form2 is RemotePopUp)) |
| return false; |
| |
| var remotePopUp1:RemotePopUp = RemotePopUp(form1); |
| var remotePopUp2:RemotePopUp = RemotePopUp(form2); |
| |
| if (remotePopUp1.window == remotePopUp2.window && |
| remotePopUp1.bridge && remotePopUp2.bridge) |
| return true; |
| |
| return false; |
| } |
| |
| |
| /** |
| * @private |
| * |
| * @return true if the form can be activated, false otherwise. |
| */ |
| private function canActivatePopUp(f:Object):Boolean |
| { |
| var e:Request; |
| |
| if (hasEventListener("canActivateForm")) |
| { |
| e = new Request("canActivateForm", false, true); |
| e.value = f; |
| if (!dispatchEvent(e)) |
| { |
| return e.value; |
| } |
| } |
| |
| if (canActivateLocalComponent(f)) |
| return true; |
| |
| return false; |
| } |
| |
| |
| /** |
| * @private |
| * |
| * Test is a local component can be activated. |
| */ |
| private function canActivateLocalComponent(o:Object):Boolean |
| { |
| |
| if (o is Sprite && o is IUIComponent && |
| Sprite(o).visible && IUIComponent(o).enabled) |
| return true; |
| |
| return false; |
| } |
| |
| /** |
| * @inheritDoc |
| */ |
| public function addFocusManager(f:IFocusManagerContainer):void |
| { |
| // trace("OLW: add focus manager" + f); |
| |
| forms.push(f); |
| |
| // trace("END OLW: add focus manager" + f); |
| } |
| |
| /** |
| * @inheritDoc |
| */ |
| public function removeFocusManager(f:IFocusManagerContainer):void |
| { |
| // trace("OLW: remove focus manager" + f); |
| |
| var n:int = forms.length; |
| for (var i:int = 0; i < n; i++) |
| { |
| if (forms[i] == f) |
| { |
| if (form == f) |
| deactivate(f); |
| |
| // If this is a bridged application, send a message to the parent |
| // to let them know the form has been deactivated so they can |
| // activate a new form. |
| if (hasEventListener("removeFocusManager")) |
| dispatchEvent(new FocusEvent("removeFocusManager", false, false, InteractiveObject(f))); |
| |
| forms.splice(i, 1); |
| |
| // trace("END OLW: successful remove focus manager" + f); |
| return; |
| } |
| } |
| |
| // trace("END OLW: remove focus manager" + f); |
| } |
| |
| /** |
| * @private |
| */ |
| private function findHighestModalForm():int |
| { |
| var n:int = forms.length; |
| var rc:IChildList = systemManager.rawChildren; |
| for (var i:int = n - 1; i >= 0; i--) |
| { |
| var f:Object = forms[i]; |
| if (f is DisplayObject) |
| { |
| var index:int = rc.getChildIndex(f as DisplayObject); |
| if (index > 0) |
| { |
| var under:DisplayObject = rc.getChildAt(index - 1); |
| if (under.name == "modalWindow") |
| return i; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| /** |
| * @private |
| * Track mouse clicks to see if we change top-level forms. |
| */ |
| private function mouseDownHandler(event:MouseEvent):void |
| { |
| // trace("SM:mouseDownHandler " + this); |
| if (hasEventListener(MouseEvent.MOUSE_DOWN)) |
| if (!dispatchEvent(new FocusEvent(MouseEvent.MOUSE_DOWN, false, true, InteractiveObject(event.target)))) |
| return; |
| |
| var startIndex:int = 0; |
| if (numModalWindows > 0) |
| { |
| // if there is a modal window, only the top modal and non-modal |
| // windows above it are in play |
| startIndex = findHighestModalForm(); |
| } |
| |
| if (!systemManager.isTopLevelRoot() || forms.length > 1) |
| { |
| var n:int = forms.length; |
| var p:DisplayObject = DisplayObject(event.target); |
| var isApplication:Boolean = systemManager.document is IRawChildrenContainer ? |
| IRawChildrenContainer(systemManager.document).rawChildren.contains(p) : |
| systemManager.document.contains(p); |
| while (p) |
| { |
| for (var i:int = startIndex; i < n; i++) |
| { |
| var form_i:Object = forms[i]; |
| if (hasEventListener("actualForm")) |
| { |
| var request:Request = new Request("actualForm", false, true); |
| request.value = forms[i]; |
| if (!dispatchEvent(request)) |
| form_i = forms[i].window; |
| } |
| if (form_i == p) |
| { |
| var j:int = 0; |
| var index:int; |
| var newIndex:int; |
| var childList:IChildList; |
| |
| if (((p != form) && p is IFocusManagerContainer) || |
| (!systemManager.isTopLevelRoot() && p == form)) |
| { |
| if (systemManager.isTopLevelRoot()) |
| activate(IFocusManagerContainer(p)); |
| |
| if (p == systemManager.document) |
| { |
| if (hasEventListener("activateApplication")) |
| dispatchEvent(new Event("activateApplication")); |
| } |
| else if (p is DisplayObject) |
| { |
| if (hasEventListener("activateWindow")) |
| dispatchEvent(new FocusEvent("activateWindow", false, false, InteractiveObject(p))); |
| } |
| } |
| |
| if (systemManager.popUpChildren.contains(p)) |
| childList = systemManager.popUpChildren; |
| else |
| childList = systemManager; |
| |
| index = childList.getChildIndex(p); |
| newIndex = index; |
| // activating top-most modal so don't switch it to the top |
| if (p == forms[startIndex]) |
| return; |
| |
| //we need to reset n because activating p's |
| //FocusManager could have caused |
| //forms.length to have changed. |
| n = forms.length; |
| for (j = startIndex; j < n; j++) |
| { |
| var f:DisplayObject; |
| isRemotePopUp = false; |
| if (hasEventListener("isRemote")) |
| { |
| request = new Request("isRemote", false, true); |
| request.value = forms[j]; |
| var isRemotePopUp:Boolean = false; |
| if (!dispatchEvent(request)) |
| isRemotePopUp = request.value as Boolean; |
| } |
| if (isRemotePopUp) |
| { |
| if (forms[j].window is String) |
| continue; |
| f = forms[j].window; |
| } |
| else |
| f = forms[j]; |
| if (isRemotePopUp) |
| { |
| var fChildIndex:int = getChildListIndex(childList, f); |
| if (fChildIndex > index) |
| newIndex = Math.max(fChildIndex, newIndex); |
| } |
| else if (childList.contains(f)) |
| if (childList.getChildIndex(f) > index) |
| newIndex = Math.max(childList.getChildIndex(f), newIndex); |
| } |
| if (newIndex > index && !isApplication) |
| childList.setChildIndex(p, newIndex); |
| |
| return; |
| } |
| } |
| p = p.parent; |
| } |
| } |
| else if (hasEventListener("activateApplication")) |
| dispatchEvent(new Event("activateApplication")); |
| } |
| |
| /** |
| * @private |
| * |
| * Get the index of an object in a given child list. |
| * |
| * @return index of f in childList, -1 if f is not in childList. |
| */ |
| private static function getChildListIndex(childList:IChildList, f:Object):int |
| { |
| var index:int = -1; |
| try |
| { |
| index = childList.getChildIndex(DisplayObject(f)); |
| } |
| catch (e:ArgumentError) |
| { |
| // index has been preset to -1 so just continue. |
| } |
| |
| return index; |
| } |
| |
| } |
| |
| } |
| |
| |