blob: a6923a3c18df4015fce363e01b7f6cc7a2f935d7 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// 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
{
import flash.accessibility.Accessibility;
import flash.accessibility.AccessibilityProperties;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Graphics;
import flash.display.InteractiveObject;
import flash.display.Sprite;
import flash.display.Stage;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.system.Capabilities;
import mx.automation.IAutomationObject;
import mx.core.FlexGlobals;
import mx.core.FlexSprite;
import mx.core.FlexVersion;
import mx.core.IChildList;
import mx.core.IFlexDisplayObject;
import mx.core.IFlexModule;
import mx.core.IFlexModuleFactory;
import mx.core.IInvalidating;
import mx.core.ILayoutDirectionElement;
import mx.core.IUIComponent;
import mx.core.LayoutDirection;
import mx.core.UIComponent;
import mx.core.UIComponentGlobals;
import mx.core.mx_internal;
import mx.effects.Blur;
import mx.effects.Fade;
import mx.effects.IEffect;
import mx.events.DynamicEvent;
import mx.events.EffectEvent;
import mx.events.FlexEvent;
import mx.events.FlexMouseEvent;
import mx.events.Request;
import mx.managers.systemClasses.ActiveWindowManager;
import mx.styles.IStyleClient;
use namespace mx_internal;
[ExcludeClass]
/**
* @private
* The PopUpManager singleton class creates new top-level windows and
* places or removes those windows from the layer on top of all other
* visible windows. See the SystemManager for a description of the layering.
* It is used for popup dialogs, menus, and dropdowns in the ComboBox control
* and in similar components.
*
* <p>The PopUpManager also provides modality, so that windows below the popup
* cannot receive mouse events, and also provides an event if the user clicks
* the mouse outside the window so the developer can choose to dismiss
* the window or warn the user.</p>
*
* @see PopUpManagerChildList
*/
public class PopUpManagerImpl extends EventDispatcher implements IPopUpManager
{
include "../core/Version.as";
//--------------------------------------------------------------------------
//
// Class variables
//
//--------------------------------------------------------------------------
/**
* @private
*/
private static var instance:IPopUpManager;
/**
* @private
*
* Place to hook in additional classes
*/
public static var mixins:Array;
mx_internal static var popUpInfoClass:Class;
mx_internal static function createPopUpData():PopUpData
{
if (!popUpInfoClass)
return new PopUpData();
return new popUpInfoClass() as PopUpData;
}
//--------------------------------------------------------------------------
//
// Class methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
private static function weakDependency():void { ActiveWindowManager };
/**
* @private
*/
public static function getInstance():IPopUpManager
{
if (!instance)
instance = new PopUpManagerImpl();
return instance;
}
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* @private
*/
public function PopUpManagerImpl()
{
super();
if (mixins)
{
var n:int = mixins.length;
for (var i:int = 0; i < n; i++)
{
new mixins[i](this);
}
}
if (hasEventListener("initialize"))
dispatchEvent(new Event("initialize"));
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
* The class used to create the shield that makes a window appear modal.
*/
mx_internal var modalWindowClass:Class;
/**
* @private
* An array of information about currently active popups
*/
mx_internal var popupInfo:Array = [];
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* Creates a top-level window and places it above other windows in the
* z-order.
* It is good practice to call the <code>removePopUp()</code> method
* to remove popups created by using the <code>createPopUp()</code> method.
*
* If the class implements IFocusManagerContainer, the window will have its
* own FocusManager so that, if the user uses the TAB key to navigate between
* controls, only the controls in the window will be accessed.
*
* <p><b>Example</b></p>
*
* <pre>pop = mx.managers.PopUpManager.createPopUp(pnl, TitleWindow, false); </pre>
*
* <p>Creates a popup window based on the TitleWindow class, using <code>pnl</code> as the MovieClip
* for determining where to place the popup. It is defined to be a non-modal window
* meaning that other windows can receive mouse events</p>
*
* @param parent DisplayObject to be used for determining which SystemManager's layers
* to use and optionally the reference point for centering the new
* top level window. It may not be the actual parent of the popup as all popups
* are parented by the SystemManager.
*
* @param className Class of object that is to be created for the popup.
* The class must implement IFlexDisplayObject.
*
* @param modal If <code>true</code>, the window is modal which means that
* the user will not be able to interact with other popups until the window
* is removed.
*
* @param childList The child list in which to add the popup.
* One of <code>PopUpManagerChildList.APPLICATION</code>,
* <code>PopUpManagerChildList.POPUP</code>,
* or <code>PopUpManagerChildList.PARENT</code> (default).
*
* @param moduleFactory The moduleFactory where this pop-up should look for
* its embedded fonts and style manager.
*
* @return Reference to new top-level window.
*
* @see PopUpManagerChildList
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function createPopUp(parent:DisplayObject,
className:Class,
modal:Boolean = false,
childList:String = null,
moduleFactory:IFlexModuleFactory = null):IFlexDisplayObject
{
const window:IUIComponent = new className();
addPopUp(window, parent, modal, childList, moduleFactory);
return window;
}
/**
* Pops up a top-level window.
* It is good practice to call <code>removePopUp()</code> to remove popups
* created by using the <code>createPopUp()</code> method.
* If the class implements IFocusManagerContainer, the window will have its
* own FocusManager so that, if the user uses the TAB key to navigate between
* controls, only the controls in the window will be accessed.
*
* <p><b>Example</b></p>
*
* <pre>var tw = new TitleWindow();
* tw.title = "My Title";
* mx.managers.PopUpManager.addPopUp(tw, pnl, false);</pre>
*
* <p>Creates a popup window using the <code>tw</code> instance of the
* TitleWindow class and <code>pnl</code> as the Sprite for determining
* where to place the popup.
* It is defined to be a non-modal window.</p>
*
* @param window The IFlexDisplayObject to be popped up.
*
* @param parent DisplayObject to be used for determining which SystemManager's layers
* to use and optionally the reference point for centering the new
* top level window. It may not be the actual parent of the popup as all popups
* are parented by the SystemManager. Also, it must not be a descendant of
* the popup.
*
* @param modal If <code>true</code>, the window is modal which means that
* the user will not be able to interact with other popups until the window
* is removed.
*
* @param childList The child list in which to add the pop-up.
* One of <code>PopUpManagerChildList.APPLICATION</code>,
* <code>PopUpManagerChildList.POPUP</code>,
* or <code>PopUpManagerChildList.PARENT</code> (default).
*
* @param moduleFactory The moduleFactory where this pop-up should look for
* its embedded fonts and style manager.
*
* @see PopUpManagerChildList
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function addPopUp(window:IFlexDisplayObject,
parent:DisplayObject,
modal:Boolean = false,
childList:String = null,
moduleFactory:IFlexModuleFactory = null):void
{
// trace("POPUP: window is " + window);
// All popups go on the local root.
// trace("POPUP: root is " + parent.root);
// trace("POPUP: initial parent is " + parent);
const visibleFlag:Boolean = window.visible;
if (parent is IUIComponent && window is IUIComponent &&
IUIComponent(window).document == null)
IUIComponent(window).document = IUIComponent(parent).document;
if (window is IFlexModule && IFlexModule(window).moduleFactory == null)
{
if (moduleFactory)
IFlexModule(window).moduleFactory = moduleFactory;
else if (parent is IUIComponent && IUIComponent(parent).document is IFlexModule)
IFlexModule(window).moduleFactory = IFlexModule(IUIComponent(parent).document).moduleFactory;
}
var sm:ISystemManager = getTopLevelSystemManager(parent);
var children:IChildList;
var topMost:Boolean;
if (!sm)
{
// check if parent is our sandbox root
sm = ISystemManager(SystemManagerGlobals.topLevelSystemManagers[0]);
if (sm.getSandboxRoot() != parent)
{
//trace("error: popup root was not SystemManager");
return; // and maybe a nice error message
}
}
// smp is the actual systemManager that will parent the popup
// it might get changed by the request
var smp:ISystemManager = sm;
if (hasEventListener("addPopUp"))
{
var request:Request = new Request("addPopUp", false, true, { parent: parent, sm: sm, modal: modal, childList: childList} );
if (!dispatchEvent(request))
smp = request.value as ISystemManager;
}
if (window is IUIComponent)
IUIComponent(window).isPopUp = true;
if (!childList || childList == PopUpManagerChildList.PARENT)
topMost = smp.popUpChildren.contains(parent);
else
topMost = (childList == PopUpManagerChildList.POPUP);
children = topMost ? smp.popUpChildren : smp;
if (DisplayObject(window).parent != children)
children.addChild(DisplayObject(window));
window.visible = false;
const o:PopUpData = createPopUpData();
o.owner = DisplayObject(window);
o.topMost = topMost;
o.systemManager = smp;
popupInfo.push(o);
var awm:IActiveWindowManager =
IActiveWindowManager(smp.getImplementation("mx.managers::IActiveWindowManager"));
if (window is IFocusManagerContainer)
{
if (IFocusManagerContainer(window).focusManager)
{
awm.addFocusManager(IFocusManagerContainer(window));
}
else
// Popups get their own focus loop
IFocusManagerContainer(window).focusManager =
new FocusManager(IFocusManagerContainer(window), true);
}
if (hasEventListener("addPlaceHolder"))
{
var event:DynamicEvent = new DynamicEvent("addPlaceHolder");
event.sm = sm;
event.window = window;
dispatchEvent(event);
}
// force into automation hierarchy
if (window is IAutomationObject)
IAutomationObject(window).showInAutomationHierarchy = true;
if (window is ILayoutManagerClient )
UIComponentGlobals.layoutManager.validateClient(ILayoutManagerClient (window), true);
o.parent = parent;
if (window is IUIComponent)
{
IUIComponent(window).setActualSize(
IUIComponent(window).getExplicitOrMeasuredWidth(),
IUIComponent(window).getExplicitOrMeasuredHeight());
}
// The layout direction may have changed since last time the
// popup was opened so need to reinit mirroring fields.
if (FlexVersion.compatibilityVersion >= FlexVersion.VERSION_4_0)
{
if (window is ILayoutDirectionElement)
ILayoutDirectionElement(window).invalidateLayoutDirection();
}
if (modal)
{
// handles accessibility for modal popUps.
if(Capabilities.hasAccessibility && Accessibility.active)
window.addEventListener(FlexEvent.CREATION_COMPLETE, modalPopUpCreationCompleteHandler, false, 0, true);
// create a modal window shield which blocks input and sets up mouseDownOutside logic
createModalWindow(parent, o, children, visibleFlag, smp, smp.getSandboxRoot());
}
else
{
o._mouseDownOutsideHandler = nonmodalMouseDownOutsideHandler;
o._mouseWheelOutsideHandler = nonmodalMouseWheelOutsideHandler;
window.visible = visibleFlag;
}
// Add show/hide listener so mouse out listeners can be added when
// a pop up is shown because applications can be launched and
// terminated between the time a pop up is hidden to when it is
// shown again.
o.owner.addEventListener(FlexEvent.SHOW, showOwnerHandler);
o.owner.addEventListener(FlexEvent.HIDE, hideOwnerHandler);
addMouseOutEventListeners(o);
// Listen for unload so we know to kill the window (and the modalWindow if modal)
// this handles _all_ cleanup
window.addEventListener(Event.REMOVED, popupRemovedHandler);
if (window is IFocusManagerContainer && visibleFlag)
{
if (hasEventListener("addedPopUp"))
{
event = new DynamicEvent("addedPopUp", false, true);
event.window = window;
event.systemManager = smp;
dispatchEvent(event);
}
else
awm.activate(IFocusManagerContainer(window));
}
// trace("END POPUP: addPopUp" + parent);
}
mx_internal function getTopLevelSystemManager(parent:DisplayObject):ISystemManager
{
var localRoot:DisplayObjectContainer;
var sm:ISystemManager;
if (hasEventListener("topLevelSystemManager"))
{
var request:Request = new Request("topLevelSystemManager", false, true);
request.value = parent;
if (!dispatchEvent(request))
localRoot = request.value as DisplayObjectContainer;
}
if (!localRoot)
localRoot = DisplayObjectContainer(parent.root);
// If the parent isn't rooted yet,
// Or the root is the stage (which is the case in a second AIR window)
// use the global system manager instance.
if ((!localRoot || localRoot is Stage) && parent is IUIComponent)
localRoot = DisplayObjectContainer(IUIComponent(parent).systemManager);
if (localRoot is ISystemManager)
{
sm = ISystemManager(localRoot);
if (!sm.isTopLevel())
sm = sm.topLevelSystemManager;
}
return sm;
}
/**
* Centers a popup window over whatever window was used in the call
* to the <code>createPopUp()</code> or <code>addPopUp()</code> method.
*
* <p>Note that the position of the popup window may not
* change immediately after this call since Flex may wait to measure and layout the
* popup window before centering it.</p>
*
* @param The IFlexDisplayObject representing the popup.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function centerPopUp(popUp:IFlexDisplayObject):void
{
if (popUp is IInvalidating)
IInvalidating(popUp).validateNow();
const o:PopUpData = findPopupInfoByOwner(popUp);
// If we don't find the pop owner or if the owner's parent is not specified or is not on the
// stage, then center based on the popUp's current parent.
var popUpParent:DisplayObject = (o && o.parent && o.parent.stage) ? o.parent : popUp.parent;
if (popUpParent)
{
//FLEX-28967 : https://issues.apache.org/jira/browse/FLEX-28967. Fix by miroslav.havrlent
var systemManager:ISystemManager;
if (o != null) {
systemManager = o.systemManager;
} else if (popUpParent.hasOwnProperty("systemManager")) {
systemManager = popUpParent["systemManager"];
} else if (popUpParent is ISystemManager) {
systemManager = popUpParent as ISystemManager;
}
if (!systemManager)
return; // or throw exception maybe ?
var x:Number;
var y:Number;
var appWidth:Number;
var appHeight:Number;
var parentWidth:Number;
var parentHeight:Number;
var rect:Rectangle;
var clippingOffset:Point = new Point();
var pt:Point;
var isTopLevelRoot:Boolean;
var sbRoot:DisplayObject = systemManager.getSandboxRoot();
var request:Request;
if (hasEventListener("isTopLevelRoot"))
{
request = new Request("isTopLevelRoot", false, true);
}
if (request && !dispatchEvent(request))
isTopLevelRoot = Boolean(request.value);
else
isTopLevelRoot = systemManager.isTopLevelRoot();
if (isTopLevelRoot && (FlexVersion.compatibilityVersion < FlexVersion.VERSION_4_6))
{
// The sandbox root is the top level root.
// The application width is just the screen width.
var screen:Rectangle = systemManager.screen;
appWidth = screen.width;
appHeight = screen.height;
}
else
{
rect = systemManager.getVisibleApplicationRect();
rect.topLeft = DisplayObject(systemManager).globalToLocal(rect.topLeft);
rect.bottomRight = DisplayObject(systemManager).globalToLocal(rect.bottomRight);
// Offset the top, left of the window to bring it into view.
clippingOffset = rect.topLeft.clone();
appWidth = rect.width;
appHeight = rect.height;
}
// If parent is a UIComponent, check for clipping between
// the object and its SystemManager
if (popUpParent is UIComponent)
{
rect = UIComponent(popUpParent).getVisibleRect();
if (UIComponent(popUpParent).systemManager != sbRoot)
rect = UIComponent(popUpParent).systemManager.getVisibleApplicationRect(rect);
var offset:Point = popUpParent.globalToLocal(rect.topLeft);
clippingOffset.x += offset.x;
clippingOffset.y += offset.y;
parentWidth = rect.width;
parentHeight = rect.height;
}
else
{
parentWidth = popUpParent.width;
parentHeight = popUpParent.height;
}
// The appWidth may smaller than parentWidth if the application is
// clipped by the parent application.
x = Math.max(0, (Math.min(appWidth, parentWidth) - popUp.width) / 2);
y = Math.max(0, (Math.min(appHeight, parentHeight) - popUp.height) / 2);
// If the layout has been mirrored, then 0,0 is the uppper
// right corner; compensate here.
if (FlexVersion.compatibilityVersion >= FlexVersion.VERSION_4_0)
{
// If popUp has layout direction different than the parent (or parent doesn't
// have layout direction and popUp is RTL) flip it to the other side of the x axis.
const popUpLDE:ILayoutDirectionElement = popUp as ILayoutDirectionElement;
const parentLDE:ILayoutDirectionElement = popUpParent as ILayoutDirectionElement;
if (popUpLDE)
{
if ((parentLDE && parentLDE.layoutDirection != popUpLDE.layoutDirection) ||
(!parentLDE && popUpLDE.layoutDirection == LayoutDirection.RTL))
{
//(x = -x) to flip it on the other side of the x axis.
//(-popUp.width) because 0 is the right edge.
x = -x -popUp.width;
}
if (popUpLDE.layoutDirection == LayoutDirection.RTL)
{
//Corrects the x offset for RTL.
clippingOffset.x += popUpParent.width;
}
}
}
pt = new Point(clippingOffset.x, clippingOffset.y);
pt = popUpParent.localToGlobal(pt);
pt = popUp.parent.globalToLocal(pt);
popUp.move(Math.round(x) + pt.x, Math.round(y) + pt.y);
}
}
/**
* Removes a popup window popped up by
* the <code>createPopUp()</code> or <code>addPopUp()</code> method.
*
* @param window The IFlexDisplayObject representing the popup window.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function removePopUp(popUp:IFlexDisplayObject):void
{
// all we want to do here is verify that this popup is one of ours
// and remove it from the display list; the REMOVED handler will do the rest
// (this is so that we never leak memory, popups will self-manage even if
// removePopUp is not called).
if (popUp && popUp.parent)
{
const o:PopUpData = findPopupInfoByOwner(popUp);
if (o)
{
var sm:ISystemManager = o.systemManager;
if (!sm)
{
var iui:IUIComponent = popUp as IUIComponent;
// cross-versioning error sometimes returns wrong parent
if (iui)
sm = ISystemManager(iui.systemManager);
else
return;
}
if (o.topMost)
sm.popUpChildren.removeChild(DisplayObject(popUp));
else
sm.removeChild(DisplayObject(popUp));
}
}
}
/**
* Makes sure a popup window is higher than other objects in its child list
* The SystemManager does this automatically if the popup is a top level window
* and is moused on,
* but otherwise you have to take care of this yourself.
*
* @param The IFlexDisplayObject representing the popup.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function bringToFront(popUp:IFlexDisplayObject):void
{
if (popUp && popUp.parent)
{
const o:PopUpData = findPopupInfoByOwner(popUp);
if (o)
{
if (hasEventListener("bringToFront"))
{
var dynamicEvent:DynamicEvent = new DynamicEvent("bringToFront", false, true);
dynamicEvent.popUpData = o;
dynamicEvent.popUp = popUp;
if (!dispatchEvent(dynamicEvent))
return;
}
const sm:ISystemManager = ISystemManager(popUp.parent);
if (o.topMost)
sm.popUpChildren.setChildIndex(DisplayObject(popUp), sm.popUpChildren.numChildren - 1);
else
sm.setChildIndex(DisplayObject(popUp), sm.numChildren - 1);
}
}
}
/**
* @private
*
* Create the modal window.
* This is called in two different cases.
* 1. Create a modal window for a local pop up.
* 2. Create a modal window for a remote pop up. In this case o.owner will be null.
*/
mx_internal function createModalWindow(parentReference:DisplayObject,
o:PopUpData,
childrenList:IChildList,
visibleFlag:Boolean,
sm:ISystemManager,
sbRoot:DisplayObject):void
{
const popup:IFlexDisplayObject = IFlexDisplayObject(o.owner);
const popupStyleClient:IStyleClient = popup as IStyleClient;
var duration:Number = 0;
// Create a modalWindow the size of the stage
// that eats all mouse clicks.
var modalWindow:Sprite;
if (modalWindowClass)
{
modalWindow = new modalWindowClass();
}
else
{
modalWindow = new FlexSprite();
modalWindow.name = "modalWindow";
}
if (!sm && parentReference)
sm = IUIComponent(parentReference).systemManager;
var awm:IActiveWindowManager =
IActiveWindowManager(sm.getImplementation("mx.managers::IActiveWindowManager"));
awm.numModalWindows++;
// Add it to the collection just below the popup
if (popup)
childrenList.addChildAt(modalWindow,
childrenList.getChildIndex(DisplayObject(popup)));
else
childrenList.addChild(modalWindow);
// force into the automation hierarchy
if (popup is IAutomationObject)
IAutomationObject(popup).showInAutomationHierarchy = true;
o.modalWindow = modalWindow;
if (popupStyleClient)
modalWindow.alpha = popupStyleClient.getStyle("modalTransparency");
else
modalWindow.alpha = 0;
modalWindow.tabEnabled = false;
const screen:Rectangle = sm.screen;
const g:Graphics = modalWindow.graphics;
var c:Number = 0xFFFFFF;
if (popupStyleClient)
{
c = popupStyleClient.getStyle("modalTransparencyColor");
}
if (hasEventListener("createModalWindow"))
{
var dynamicEvent:DynamicEvent = new DynamicEvent("createModalWindow", false, true);
dynamicEvent.popUpData = o;
dynamicEvent.popUp = popup;
dynamicEvent.color = c;
dynamicEvent.visibleFlag = visibleFlag;
dynamicEvent.childrenList = childrenList;
if (!dispatchEvent(dynamicEvent))
c = dynamicEvent.color;
}
// trace("createModalWindow: drawing modal " + s);
g.clear();
g.beginFill(c, 100);
g.drawRect(screen.x-1, screen.y-1, screen.width+2, screen.height+2);
g.endFill();
if (hasEventListener("updateModalMask"))
{
dynamicEvent = new DynamicEvent("updateModalMask");
dynamicEvent.popUpData = o;
dynamicEvent.popUp = popup;
dynamicEvent.childrenList = childrenList;
dispatchEvent(dynamicEvent);
}
// a modal mousedownoutside handler just dispatches the event
o._mouseDownOutsideHandler = dispatchMouseDownOutsideEvent;
o._mouseWheelOutsideHandler = dispatchMouseWheelOutsideEvent;
// the following handlers all get removed in REMOVED on the popup
// Set the resize handler so the modal can stay the size of the screen
sm.addEventListener(Event.RESIZE, o.resizeHandler);
if (popup)
{
// Listen for show so we know to show the modal window
popup.addEventListener(FlexEvent.SHOW, popupShowHandler);
// Listen for hide so we know to hide the modal window
popup.addEventListener(FlexEvent.HIDE, popupHideHandler);
}
if (visibleFlag)
showModalWindow(o, sm, false);
else if(popup)
popup.visible = visibleFlag;
if (hasEventListener("createdModalWindow"))
{
dynamicEvent = new DynamicEvent("createdModalWindow");
dynamicEvent.popUpData = o;
dynamicEvent.popUp = popup;
dynamicEvent.visibleFlag = visibleFlag;
dynamicEvent.childrenList = childrenList;
dispatchEvent(dynamicEvent);
}
}
/**
* @private
*/
private function endEffects(o:PopUpData):void
{
if (o.fade)
{
o.fade.end();
o.fade = null;
}
if (o.blur)
{
o.blur.end();
o.blur = null;
}
}
mx_internal function showModalWindow(o:PopUpData, sm:ISystemManager, sendRequest:Boolean = true):void
{
const popUpStyleClient:IStyleClient = o.owner as IStyleClient;
var duration:Number = 0;
var alpha:Number = 0;
if (popUpStyleClient)
{
duration = popUpStyleClient.getStyle("modalTransparencyDuration");
}
if (popUpStyleClient)
{
alpha = popUpStyleClient.getStyle("modalTransparency");
}
var blurAmount:Number = 0;
if (popUpStyleClient)
{
blurAmount = popUpStyleClient.getStyle("modalTransparencyBlur");
}
var transparencyColor:Number = 0xFFFFFF;
if (popUpStyleClient)
{
transparencyColor = popUpStyleClient.getStyle("modalTransparencyColor");
}
var sbRoot:DisplayObject = sm.getSandboxRoot();
if (hasEventListener("showModalWindow"))
{
var dynamicEvent:DynamicEvent = new DynamicEvent("showModalWindow", false, true);
dynamicEvent.popUpData = o;
dynamicEvent.sendRequest = sendRequest;
dynamicEvent.alpha = alpha;
dynamicEvent.blurAmount = blurAmount;
dynamicEvent.duration = duration;
dynamicEvent.systemManager = sm;
dynamicEvent.transparencyColor = transparencyColor;
if (!dispatchEvent(dynamicEvent))
{
alpha = dynamicEvent.alpha;
blurAmount = dynamicEvent.blurAmount;
duration = dynamicEvent.duration;
transparencyColor = dynamicEvent.transparencyColor;
}
}
o.modalWindow.alpha = alpha;
showModalWindowInternal(o, duration, alpha, transparencyColor, blurAmount, sm, sbRoot);
}
private function setModalPopupVisible(popup:DisplayObject, value:Boolean):void
{
popup.removeEventListener(FlexEvent.SHOW, popupShowHandler);
popup.removeEventListener(FlexEvent.HIDE, popupHideHandler);
popup.visible = value;
popup.addEventListener(FlexEvent.SHOW, popupShowHandler);
popup.addEventListener(FlexEvent.HIDE, popupHideHandler);
}
/**
* @private
* Show the modal transparency blocker, playing effects if needed.
*/
private function showModalWindowInternal(o:PopUpData,
transparencyDuration:Number,
transparency:Number,
transparencyColor:Number,
transparencyBlur:Number,
sm:ISystemManager,
sbRoot:DisplayObject):void
{
// NO POPUP Data
// End any effects that are currently playing for this popup.
endEffects(o);
if (transparencyDuration)
{
// Fade effect on the modal transparency blocker
const fade:Fade = new Fade(o.modalWindow);
fade.alphaFrom = 0;
fade.alphaTo = transparency;
fade.duration = transparencyDuration;
fade.addEventListener(EffectEvent.EFFECT_END, fadeInEffectEndHandler);
o.modalWindow.alpha = 0;
o.modalWindow.visible = true;
o.fade = fade;
if (o.owner)
setModalPopupVisible(o.owner, false);
fade.play();
// Blur effect on the application
var blurAmount:Number = transparencyBlur;
if (blurAmount)
{
// Ensure we blur the appropriate top level document.
if (DisplayObject(sm).parent is Stage)
{
// Checking this case first allows WindowedSystemManagers be the blur target.
o.blurTarget = sm.document;
}
else if (sm != sbRoot)
{
// Get the application document of the sandbox root.
// Use a request to get the document so APIs may change
// between Flex versions.
var sbRootApp:Object; // sbRoot.application;
if (hasEventListener("blurTarget"))
{
var request:Request = new Request("blurTarget", false, true, { popUpData: o });
if (!dispatchEvent(request))
{
o.blurTarget = request.value;
}
}
}
else
o.blurTarget = FlexGlobals.topLevelApplication;
const blur:Blur = new Blur(o.blurTarget);
blur.blurXFrom = blur.blurYFrom = 0;
blur.blurXTo = blur.blurYTo = blurAmount;
blur.duration = transparencyDuration;
blur.addEventListener(EffectEvent.EFFECT_END, effectEndHandler);
o.blur = blur;
blur.play();
}
}
else
{
if (o.owner)
setModalPopupVisible(o.owner, true);
o.modalWindow.visible = true;
}
}
/**
* @private
* Hide the modal transparency blocker, playing effects if needed.
*
*/
mx_internal function hideModalWindow(o:PopUpData, destroy:Boolean = false):void
{
const popUpStyleClient:IStyleClient = o.owner as IStyleClient;
var duration:Number = 0;
if (popUpStyleClient)
duration = popUpStyleClient.getStyle("modalTransparencyDuration");
// end any effects that are current playing for this popup
endEffects(o);
if (duration)
{
// Fade effect on the modal transparency blocker
const fade:Fade = new Fade(o.modalWindow);
fade.alphaFrom = o.modalWindow.alpha;
fade.alphaTo = 0;
fade.duration = duration;
fade.addEventListener(EffectEvent.EFFECT_END,
destroy ? fadeOutDestroyEffectEndHandler : fadeOutCloseEffectEndHandler);
o.modalWindow.visible = true;
o.fade = fade;
fade.play();
var sm:ISystemManager = o.systemManager;
var awm:IActiveWindowManager =
IActiveWindowManager(sm.getImplementation("mx.managers::IActiveWindowManager"));
// don't remove blur unless this is the last modal window
if (awm.numModalWindows == 1)
{
// Blur effect on the application
const blurAmount:Number = popUpStyleClient.getStyle("modalTransparencyBlur");
if (blurAmount)
{
const blur:Blur = new Blur(o.blurTarget);
blur.blurXFrom = blur.blurYFrom = blurAmount;
blur.blurXTo = blur.blurYTo = 0;
blur.duration = duration;
blur.addEventListener(EffectEvent.EFFECT_END, effectEndHandler);
o.blur = blur;
blur.play();
}
}
}
else
{
o.modalWindow.visible = false;
}
if (hasEventListener("hideModalWindow"))
{
var dynamicEvent:DynamicEvent = new DynamicEvent("hideModalWindow", false, false);
dynamicEvent.popUpData = o;
dynamicEvent.destroy = destroy;
dispatchEvent(dynamicEvent);
}
}
/**
* @private
* Returns the index position of the PopUpData in the popupInfo array (or -1)
* for a given popupInfo.owner
*/
private function findPopupInfoIndexByOwner(owner:Object):int
{
const n:int = popupInfo.length;
for (var i:int = 0; i < n; i++)
{
var o:PopUpData = popupInfo[i];
if (o.owner == owner)
return i;
}
return -1;
}
/**
* @private
* Returns the PopUpData (or null) for a given popupInfo.owner
*/
private function findPopupInfoByOwner(owner:Object):PopUpData
{
var index:int = findPopupInfoIndexByOwner(owner);
return index > -1 ? popupInfo[index] : null;
}
/**
* @private
* Add mouse out listeners for modal and non-modal windows.
*/
private function addMouseOutEventListeners(o:PopUpData):void
{
var sbRoot:DisplayObject = o.systemManager.getSandboxRoot();
if (o.modalWindow)
{
o.modalWindow.addEventListener(MouseEvent.MOUSE_DOWN, o.mouseDownOutsideHandler);
o.modalWindow.addEventListener(MouseEvent.MOUSE_WHEEL, o.mouseWheelOutsideHandler, true);
}
else
{
sbRoot.addEventListener(MouseEvent.MOUSE_DOWN, o.mouseDownOutsideHandler);
sbRoot.addEventListener(MouseEvent.MOUSE_WHEEL, o.mouseWheelOutsideHandler, true);
}
if (hasEventListener("addMouseOutEventListeners"))
{
var dynamicEvent:DynamicEvent = new DynamicEvent("addMouseOutEventListeners", false, false);
dynamicEvent.popUpData = o;
dispatchEvent(dynamicEvent);
}
}
/**
* @private
* Remove mouse out listeners for modal and non-modal windows.
*/
private function removeMouseOutEventListeners(o:PopUpData):void
{
var sbRoot:DisplayObject = o.systemManager.getSandboxRoot();
if (o.modalWindow)
{
o.modalWindow.removeEventListener(MouseEvent.MOUSE_DOWN, o.mouseDownOutsideHandler);
o.modalWindow.removeEventListener(MouseEvent.MOUSE_WHEEL, o.mouseWheelOutsideHandler, true);
}
else
{
sbRoot.removeEventListener(MouseEvent.MOUSE_DOWN, o.mouseDownOutsideHandler);
sbRoot.removeEventListener(MouseEvent.MOUSE_WHEEL, o.mouseWheelOutsideHandler, true);
}
if (hasEventListener("removeMouseOutEventListeners"))
{
var dynamicEvent:DynamicEvent = new DynamicEvent("removeMouseOutEventListeners", false, false);
dynamicEvent.popUpData = o;
dispatchEvent(dynamicEvent);
}
}
//--------------------------------------------------------------------------
//
// Event handlers
//
//--------------------------------------------------------------------------
/**
* @private
* Set by PopUpManager on modal windows so they show when the parent shows.
*/
private function popupShowHandler(event:FlexEvent):void
{
const o:PopUpData = findPopupInfoByOwner(event.target);
if (o)
showModalWindow(o, getTopLevelSystemManager(o.parent));
}
/**
* @private
* Set by PopUpManager on modal windows so they hide when the parent hide
*/
private function popupHideHandler(event:FlexEvent):void
{
const o:PopUpData = findPopupInfoByOwner(event.target);
if (o)
hideModalWindow(o);
}
/**
* @private
*/
private function showOwnerHandler(event:FlexEvent):void
{
const o:PopUpData = findPopupInfoByOwner(event.target);
if (o)
{
// add mouse out listeners.
addMouseOutEventListeners(o);
}
}
/**
* @private
*/
private function hideOwnerHandler(event:FlexEvent):void
{
const o:PopUpData = findPopupInfoByOwner(event.target);
if (o)
{
// remove mouse out listeners
removeMouseOutEventListeners(o);
}
}
/**
* @private
* Set by PopUpManager on modal windows to monitor when the parent window gets killed.
* PopUps self-manage their memory -- when they are removed using removePopUp OR
* manually removed with removeChild, they will clean themselves up when they leave the
* display list (including all references to PopUpManager).
*/
private function popupRemovedHandler(event:Event):void
{
const n:int = popupInfo.length;
for (var i:int = 0; i < n; i++)
{
var o:PopUpData = popupInfo[i],
popUp:DisplayObject = o.owner;
if (popUp == event.target)
{
var popUpParent:DisplayObject = o.parent,
modalWindow:DisplayObject = o.modalWindow,
sm:ISystemManager = o.systemManager;
if (!sm.isTopLevel())
sm = sm.topLevelSystemManager;
if (popUp is IUIComponent)
IUIComponent(popUp).isPopUp = false;
var awm:IActiveWindowManager =
IActiveWindowManager(sm.getImplementation("mx.managers::IActiveWindowManager"));
if (popUp is IFocusManagerContainer)
{
awm.removeFocusManager(IFocusManagerContainer(popUp));
}
popUp.removeEventListener(Event.REMOVED, popupRemovedHandler);
if (hasEventListener("removeMouseOutEventListeners"))
{
var event2:DynamicEvent = new DynamicEvent("popUpRemoved");
event2.popUpData = o;
dispatchEvent(event2);
}
if (o.owner)
{
o.owner.removeEventListener(FlexEvent.SHOW, showOwnerHandler);
o.owner.removeEventListener(FlexEvent.HIDE, hideOwnerHandler);
}
removeMouseOutEventListeners(o);
// modal
if (modalWindow)
{
// restore accessibility to document
removeModalPopUpAccessibility(popUp);
// clean up all handlers
sm.removeEventListener(Event.RESIZE, o.resizeHandler);
popUp.removeEventListener(FlexEvent.SHOW, popupShowHandler);
popUp.removeEventListener(FlexEvent.HIDE, popupHideHandler);
hideModalWindow(o, true);
awm.numModalWindows--;
}
popupInfo.splice(i, 1);
break;
}
}
}
/**
* @private
* Show the modal window after the fade effect finishes
*/
private function fadeInEffectEndHandler(event:EffectEvent):void
{
effectEndHandler(event);
const n:int = popupInfo.length;
for (var i:int = 0; i < n; i++)
{
var o:PopUpData = popupInfo[i];
if (o.owner && o.modalWindow == event.effectInstance.target)
{
setModalPopupVisible(o.owner, true);
break;
}
}
}
/**
* @private
* Remove the modal window after the fade effect finishes
*/
private function fadeOutDestroyEffectEndHandler(event:EffectEvent):void
{
effectEndHandler(event);
const obj:DisplayObject = DisplayObject(event.effectInstance.target);
var modalMask:DisplayObject = obj.mask;
if (modalMask)
{
// modal mask is always added to the popupChildren list.
obj.mask = null;
sm.popUpChildren.removeChild(modalMask);
}
if (obj.parent is ISystemManager)
{
const sm:ISystemManager = ISystemManager(obj.parent)
if (sm.popUpChildren.contains(obj))
sm.popUpChildren.removeChild(obj);
else
sm.removeChild(obj);
}
else
{
if (obj.parent) // Mustella can already take you off stage
obj.parent.removeChild(obj);
}
}
/**
* @private
* Remove the modal window after the fade effect finishes
*/
private function fadeOutCloseEffectEndHandler(event:EffectEvent):void
{
effectEndHandler(event);
DisplayObject(event.effectInstance.target).visible = false;
}
/**
* @private
*/
private function effectEndHandler(event:EffectEvent):void
{
const n:int = popupInfo.length;
for (var i:int = 0; i < n; i++)
{
var o:PopUpData = popupInfo[i];
var e:IEffect = event.effectInstance.effect;
if (e == o.fade)
o.fade = null;
else if (e == o.blur)
o.blur = null;
}
}
/**
* @private
* If not modal, use this kind of mouseDownOutside logic
*/
private static function nonmodalMouseDownOutsideHandler(owner:DisplayObject, evt:MouseEvent):void
{
// shapeFlag is false here for performance reasons
if (owner.hitTestPoint(evt.stageX, evt.stageY, true))
{
}
else
{
if (owner is IUIComponent)
if (IUIComponent(owner).owns(DisplayObject(evt.target)))
return;
dispatchMouseDownOutsideEvent(owner, evt);
}
}
/**
* @private
* If not modal, use this kind of mouseWheelOutside logic
*/
private static function nonmodalMouseWheelOutsideHandler(owner:DisplayObject, evt:MouseEvent):void
{
// shapeFlag is false here for performance reasons
if (owner.hitTestPoint(evt.stageX, evt.stageY, true))
{
}
else
{
if (owner is IUIComponent)
if (IUIComponent(owner).owns(DisplayObject(evt.target)))
return;
dispatchMouseWheelOutsideEvent(owner, evt);
}
}
/**
* @private
* This mouseWheelOutside handler just dispatches the event.
*/
private static function dispatchMouseWheelOutsideEvent(owner:DisplayObject, evt:MouseEvent):void
{
// this is a modal window without a popup owner to dispatch the message to.
if (!owner)
return;
const event:MouseEvent = new FlexMouseEvent(FlexMouseEvent.MOUSE_WHEEL_OUTSIDE);
const pt:Point = owner.globalToLocal(new Point(evt.stageX, evt.stageY));
event.localX = pt.x;
event.localY = pt.y;
event.buttonDown = evt.buttonDown;
event.shiftKey = evt.shiftKey;
event.altKey = evt.altKey;
event.ctrlKey = evt.ctrlKey;
event.delta = evt.delta;
event.relatedObject = InteractiveObject(evt.target);
owner.dispatchEvent(event);
}
/**
* @private
* This mouseDownOutside handler just dispatches the event.
*/
private static function dispatchMouseDownOutsideEvent(owner:DisplayObject, evt:MouseEvent):void
{
// this is a modal window without a popup owner to dispatch the message to.
if (!owner)
return;
const event:MouseEvent = new FlexMouseEvent(FlexMouseEvent.MOUSE_DOWN_OUTSIDE);
const pt:Point = owner.globalToLocal(new Point(evt.stageX, evt.stageY));
event.localX = pt.x;
event.localY = pt.y;
event.buttonDown = evt.buttonDown;
event.shiftKey = evt.shiftKey;
event.altKey = evt.altKey;
event.ctrlKey = evt.ctrlKey;
event.delta = evt.delta;
event.relatedObject = InteractiveObject(evt.target);
owner.dispatchEvent(event);
}
/**
* @private
* This method handles the creation of a modal popUp
*/
private function modalPopUpCreationCompleteHandler(event:FlexEvent):void
{
event.target.removeEventListener(FlexEvent.CREATION_COMPLETE, modalPopUpCreationCompleteHandler);
addModalPopUpAccessibility(event.currentTarget as DisplayObject);
}
/**
* @private
* This method handles the creation of a modal popUp when assistive
* technology is active, by silencing the content of the top-level document.
*/
private function addModalPopUpAccessibility(popUp:DisplayObject):Boolean
{
if (Capabilities.hasAccessibility && Accessibility.active)
{
const p:PopUpData = findPopupInfoByOwner(popUp);
if (p)
{
const n:int = popupInfo.length;
for (var i:int = 0; i < n; i++)
{
var o:PopUpData = popupInfo[i];
if (o && o != p && o.owner.accessibilityProperties)
{
o.owner.accessibilityProperties.silent = true;
}
}
var sbRoot:Object = p.systemManager.getSandboxRoot(); // getTopLevelSystemManager(p.parent);
if (!sbRoot.document.accessibilityProperties)
sbRoot.document.accessibilityProperties = new AccessibilityProperties();
// This hides top-level document content from assistive technology.
sbRoot.document.accessibilityProperties.silent = true;
try {
Accessibility.updateProperties();
}
catch(e:Error) {
// Flash Player issue causes hasAccessibility and active to be true when accessibility is turned off
}
}
return true;
}
return false;
}
/**
* @private
* This method handles the removal of a modal popUp when assistive technology is active,
* by exposing the content of the top-level document.
*
* @return
*/
private function removeModalPopUpAccessibility(popUp:DisplayObject):Boolean
{
if (Capabilities.hasAccessibility && Accessibility.active)
{
const p:PopUpData = findPopupInfoByOwner(popUp);
if (p)
{
handleAccessibilityForNestedPopups(popUp);
if (popupInfo.length<=1)
{
var sbRoot:Object = p.systemManager.getSandboxRoot();
if (sbRoot.document.accessibilityProperties)
sbRoot.document.accessibilityProperties.silent = false;
}
try {
Accessibility.updateProperties();
}
catch(e:Error) {
// Flash Player issue on windows causes hasAccessibility and active to be true when accessibility is turned off
}
}
return true;
}
return false;
}
/**
* @private
* This method handles accessibility for nested popUps.
* If this method is called from outside removeModalPopUpAccessibility,
* it is mandatory to invoke Accessibility.updateProperties() to communicate
* the individual popup's accessibilityProperties change to the screen reader.
*
* <p>This method should only come into play with modal popUps, in which case it will
* either expose accessibility of a modal popUp underneath the popUp being closed
* without exposing the underlying content, or continue exposing accessibility
* of popUps until there are no more to expose.</p>
*/
private function handleAccessibilityForNestedPopups(popUpBeingVisited:DisplayObject):void
{
if (!popUpBeingVisited)
return;
var index:int = findPopupInfoIndexByOwner(popUpBeingVisited);
var popupData:PopUpData = index > -1 ? popupInfo[index] : null;
var underneathPopUpData:PopUpData;
if (index == 0)
{
var sm:ISystemManager = getTopLevelSystemManager(popupData.parent);
if (sm)
{
// If this is the only popUp, we should expose accessibility
// of the top-level system manager's document
if (sm.document.accessibilityProperties)
sm.document.accessibilityProperties.silent = false;
// We should also expose accessibility
// of the sandbox root's document.
var sbRoot:Object = popupData.systemManager.getSandboxRoot();
if (sbRoot.document.accessibilityProperties)
sbRoot.document.accessibilityProperties.silent = false;
}
}
else if (index > 0)
{
// If more than one popUp is open, we should expose accessibility
// of the underlying popUp
underneathPopUpData = popupInfo[index - 1];
if (underneathPopUpData.owner.accessibilityProperties)
underneathPopUpData.owner.accessibilityProperties.silent = false;
// All the nested popUp's AccessibilityProperties
// changes should be handled by a single
// Accessibility.updateProperties
// call in removeModalPopUpAccessibility.
// If the underlying popUp is modal, we should stop recursing.
if (underneathPopUpData.modalWindow)
return;
handleAccessibilityForNestedPopups(underneathPopUpData.owner);
}
}
}
}