blob: 471f544f8ab98f96bb344d45ff13874762e4e593 [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.controls.alertClasses
{
import flash.display.DisplayObject;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.text.TextLineMetrics;
import flash.ui.Keyboard;
import mx.controls.Alert;
import mx.controls.Button;
import mx.core.IFlexModuleFactory;
import mx.core.IFontContextComponent;
import mx.core.mx_internal;
import mx.core.IUITextField;
import mx.core.UIComponent;
import mx.core.UITextField;
import mx.events.CloseEvent;
import mx.managers.IActiveWindowManager;
import mx.managers.IFocusManagerContainer;
import mx.managers.ISystemManager;
import mx.managers.PopUpManager;
use namespace mx_internal;
include "../../styles/metadata/LeadingStyle.as"
include "../../styles/metadata/TextStyles.as"
[ExcludeClass]
/**
* @private
* The AlertForm control exists within the Alert control, and contains
* messages, buttons, and, optionally, an icon. It is not intended for
* direct use by application developers.
*
* @see mx.controls.TextArea
* @see mx.controls.Alert
* @see mx.controls.Button
*/
public class AlertForm extends UIComponent implements IFontContextComponent
{
include "../../core/Version.as";
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function AlertForm()
{
super();
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* The UITextField that displays the text of the Alert control.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
mx_internal var textField:IUITextField;
/**
* @private
* Width of the text object.
*/
private var textWidth:Number;
/**
* @private
* Height of the text object.
*/
private var textHeight:Number;
/**
* The DisplayObject that displays the icon.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
private var icon:DisplayObject;
/**
* An Array that contains any Buttons appearing in the Alert control.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
mx_internal var buttons:Array = [];
/**
* @private
*/
mx_internal var defaultButton:Button;
/**
* @private
*/
private var defaultButtonChanged:Boolean = false;
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// fontContext
//----------------------------------
/**
* @private
*/
public function get fontContext():IFlexModuleFactory
{
return moduleFactory;
}
/**
* @private
*/
public function set fontContext(moduleFactory:IFlexModuleFactory):void
{
this.moduleFactory = moduleFactory;
}
//--------------------------------------------------------------------------
//
// Overridden methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function createChildren():void
{
super.createChildren();
// Create the UITextField to display the message.
createTextField(-1);
// Create the icon object, if any.
var iconClass:Class = Alert(parent).iconClass;
if (iconClass && !icon)
{
icon = new iconClass();
addChild(icon);
}
// Create the button objects
var alert:Alert = Alert(parent);
var buttonFlags:uint = alert.buttonFlags;
var defaultButtonFlag:uint = alert.defaultButtonFlag;
var label:String;
var button:Button;
if (buttonFlags & Alert.OK)
{
label = String(Alert.okLabel);
button = createButton(label, "OK");
if (defaultButtonFlag == Alert.OK)
defaultButton = button;
}
if (buttonFlags & Alert.YES)
{
label = String(Alert.yesLabel);
button = createButton(label, "YES");
if (defaultButtonFlag == Alert.YES)
defaultButton = button;
}
if (buttonFlags & Alert.NO)
{
label = String(Alert.noLabel);
button = createButton(label, "NO");
if (defaultButtonFlag == Alert.NO)
defaultButton = button;
}
if (buttonFlags & Alert.CANCEL)
{
label = String(Alert.cancelLabel);
button = createButton(label, "CANCEL");
if (defaultButtonFlag == Alert.CANCEL)
defaultButton = button;
}
if (!defaultButton && buttons.length)
defaultButton = buttons[0];
// Set the default button to have focus.
if (defaultButton)
{
defaultButtonChanged = true;
invalidateProperties();
}
}
/**
* @private
*/
override protected function commitProperties():void
{
super.commitProperties();
// if the font changed and we already created the label, we will need to
// destory it so it can be re-created, possibly in a different swf context.
if (hasFontContextChanged() && textField != null)
{
var index:int = getChildIndex(DisplayObject(textField));
removeTextField();
createTextField(index);
}
if (defaultButtonChanged && defaultButton)
{
defaultButtonChanged = false;
Alert(parent).defaultButton = defaultButton;
if (parent is IFocusManagerContainer)
{
var sm:ISystemManager = Alert(parent).systemManager;
var awm:IActiveWindowManager =
IActiveWindowManager(sm.getImplementation("mx.managers::IActiveWindowManager"));
if (awm)
awm.activate(IFocusManagerContainer(parent));
}
defaultButton.setFocus();
}
}
/**
* @private
*/
override protected function measure():void
{
super.measure();
// Get the width of the title text
var title:String = Alert(parent).title;
var lineMetrics:TextLineMetrics =
Alert(parent).getTitleTextField().
getUITextFormat().measureText(title);
// Calculate the width based solely on the title and the buttons
var numButtons:int = Math.max(buttons.length, 2);
var buttonWidth:Number = numButtons * buttons[0].width +
(numButtons - 1) * 8;
var buttonAndTitleWidth:Number = Math.max(buttonWidth,
lineMetrics.width);
// Set the textField to word-wrap if the text is more
// than twice as wide as the buttons and title
textField.width = 2 * buttonAndTitleWidth;
textWidth = textField.textWidth + UITextField.TEXT_WIDTH_PADDING;
// Window is wider of buttons or text, but not more than twice as
// wide as buttons.
var prefWidth:Number = Math.max(buttonAndTitleWidth, textWidth);
prefWidth = Math.min(prefWidth, 2 * buttonAndTitleWidth);
// Need this because TextField likes to use multiple lines
// even for single words.
if (textWidth < prefWidth && textField.multiline == true)
{
textField.multiline = false;
textField.wordWrap = false;
}
else if (textField.multiline == false)
{
textField.wordWrap = true;
textField.multiline = true;
}
// Add space for 8-pixel padding along the left/right.
prefWidth += 16;
// Add space for icon, if any.
if (icon)
prefWidth += icon.width + 8;
// Make height tall enough for text and icon.
textHeight = textField.textHeight + UITextField.TEXT_HEIGHT_PADDING;
var prefHeight:Number = textHeight;
if (icon)
prefHeight = Math.max(prefHeight, icon.height);
// Limit the height if it exceeds the height of the Stage.
prefHeight = Math.min(prefHeight, screen.height * 0.75);
// Add space for buttons, spacing between buttons and text,
// and top/bottom margins
prefHeight += buttons[0].height + (3 * 8);
measuredWidth = prefWidth;
measuredHeight = prefHeight;
}
/**
* @private
*/
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
var newX:Number;
var newY:Number;
var newWidth:Number;
// Layout buttons first.
newY = unscaledHeight - buttons[0].height;
newWidth = buttons.length * (buttons[0].width + 8) - 8;
// Center the buttons.
newX = Math.round((unscaledWidth - newWidth) / 2);
for (var i:int = 0; i < buttons.length; i++)
{
buttons[i].move(newX, newY);
buttons[i].tabIndex = i + 1;
newX += buttons[i].width + 8;
}
// Get the width of the text and icon together.
newWidth = textWidth;
if (icon)
newWidth += icon.width + 8;
// Center the text and icon horizontally and vertically.
newX = Math.round((unscaledWidth - newWidth) / 2);
if (icon)
{
icon.x = newX;
icon.y = (newY - icon.height) / 2;
newX += icon.width + 8;
}
var newHeight:Number = textField.getExplicitOrMeasuredHeight();
var paddingTop:Number = textField.getStyle("paddingTop");
var paddingBottom:Number = textField.getStyle("paddingBottom");
var maxHeight:Number = unscaledHeight - buttons[0].height
- paddingTop - paddingBottom;
if (newHeight > maxHeight)
newHeight = maxHeight;
// try and make at least one line of text show even if it overlapps buttons
// this may occur if the height of alert box is set to be too small
var lineMetrics:TextLineMetrics = textField.getUITextFormat().measureText(textField.text);
if (newHeight < lineMetrics.height)
newHeight = lineMetrics.height;
textField.move(newX, Math.round((newY - newHeight) / 2));
textField.setActualSize(textWidth+5, newHeight);
}
/**
* @private
*/
override public function styleChanged(styleProp:String):void
{
super.styleChanged(styleProp);
if (!styleProp ||
styleProp == "styleName" ||
styleProp == "buttonStyleName")
{
if (buttons)
{
var buttonStyleName:String = getStyle("buttonStyleName");
var n:int = buttons.length;
for (var i:int = 0; i < n; i++)
{
buttons[i].styleName = buttonStyleName;
}
}
}
}
/**
* @private
* Updates the button labels.
*/
override protected function resourcesChanged():void
{
super.resourcesChanged();
var b:Button;
b = Button(getChildByName("OK"));
if (b)
b.label = String(Alert.okLabel);
b = Button(getChildByName("CANCEL"));
if (b)
b.label = String(Alert.cancelLabel);
b = Button(getChildByName("YES"));
if (b)
b.label = String(Alert.yesLabel);
b = Button(getChildByName("NO"));
if (b)
b.label = String(Alert.noLabel);
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
* Creates the title text field child
* and adds it as a child of this component.
*
* @param childIndex The index of where to add the child.
* If -1, the text field isappended to the end of the list.
*/
mx_internal function createTextField(childIndex:int):void
{
if (!textField)
{
textField = IUITextField(createInFontContext(UITextField));
textField.styleName = this;
textField.text = Alert(parent).text;
textField.multiline = true;
textField.wordWrap = true;
textField.selectable = true;
if (childIndex == -1)
addChild(DisplayObject(textField));
else
addChildAt(DisplayObject(textField), childIndex);
}
}
/**
* @private
* Removes the title text field from this component.
*/
mx_internal function removeTextField():void
{
if (textField)
{
removeChild(DisplayObject(textField));
textField = null;
}
}
/**
* @private
*/
private function createButton(label:String, name:String):Button
{
var button:Button = new Button();
button.label = label;
// The name is "YES", "NO", "OK", or "CANCEL".
button.name = name;
var buttonStyleName:String = getStyle("buttonStyleName");
if (buttonStyleName)
button.styleName = buttonStyleName;
button.addEventListener(MouseEvent.CLICK, clickHandler);
button.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
button.owner = parent;
addChild(button);
button.setActualSize(Alert.buttonWidth, Alert.buttonHeight);
buttons.push(button);
return button;
}
/**
* @private
* Remove the popup and dispatch Click event corresponding to the Button Pressed.
*/
private function removeAlert(buttonPressed:String):void
{
var alert:Alert = Alert(parent);
alert.visible = false;
var closeEvent:CloseEvent = new CloseEvent(CloseEvent.CLOSE);
if (buttonPressed == "YES")
closeEvent.detail = Alert.YES;
else if (buttonPressed == "NO")
closeEvent.detail = Alert.NO;
else if (buttonPressed == "OK")
closeEvent.detail = Alert.OK;
else if (buttonPressed == "CANCEL")
closeEvent.detail = Alert.CANCEL;
alert.dispatchEvent(closeEvent);
mx.managers.PopUpManager.removePopUp(alert);
}
//--------------------------------------------------------------------------
//
// Overridden event handlers: UIComponent
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function keyDownHandler(event:KeyboardEvent):void
{
var buttonFlags:uint = Alert(parent).buttonFlags;
if (event.keyCode == Keyboard.ESCAPE)
{
if ((buttonFlags & Alert.CANCEL) || !(buttonFlags & Alert.NO))
removeAlert("CANCEL");
else if (buttonFlags & Alert.NO)
removeAlert("NO");
}
}
//--------------------------------------------------------------------------
//
// Event handlers
//
//--------------------------------------------------------------------------
/**
* @private
* On a button click, dismiss the popup and send notification.
*/
private function clickHandler(event:MouseEvent):void
{
var name:String = Button(event.currentTarget).name;
removeAlert(name);
}
}
}