| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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 |
| { |
| |
| import flash.display.DisplayObjectContainer; |
| import flash.display.InteractiveObject; |
| import flash.display.NativeMenu; |
| import flash.display.NativeMenuItem; |
| import flash.display.Stage; |
| import flash.events.Event; |
| import flash.events.EventDispatcher; |
| import flash.events.TimerEvent; |
| import flash.ui.Keyboard; |
| import flash.utils.Timer; |
| import flash.xml.XMLNode; |
| |
| import mx.automation.IAutomationObject; |
| import mx.collections.ArrayCollection; |
| import mx.collections.ICollectionView; |
| import mx.collections.XMLListCollection; |
| import mx.collections.errors.ItemPendingError; |
| import mx.controls.menuClasses.IMenuDataDescriptor; |
| import mx.controls.treeClasses.DefaultDataDescriptor; |
| import mx.core.Application; |
| import mx.core.EventPriority; |
| import mx.core.UIComponent; |
| import mx.core.UIComponentGlobals; |
| import mx.core.mx_internal; |
| import mx.events.CollectionEvent; |
| import mx.events.CollectionEventKind; |
| import mx.events.FlexNativeMenuEvent; |
| import mx.managers.ILayoutManagerClient; |
| import mx.managers.ISystemManager; |
| |
| use namespace mx_internal; |
| |
| //-------------------------------------- |
| // Events |
| //-------------------------------------- |
| |
| /** |
| * Dispatched before a menu or submenu is displayed. |
| * |
| * @eventType mx.events.FlexNativeMenuEvent.MENU_SHOW |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="menuShow", type="mx.events.FlexNativeMenuEvent")] |
| |
| /** |
| * Dispatched when a menu item is selected. |
| * |
| * @eventType mx.events.FlexNativeMenuEvent.ITEM_CLICK |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="itemClick", type="mx.events.FlexNativeMenuEvent")] |
| |
| /** |
| * The FlexNativeMenu component provides a wrapper for AIR's NativeMenu class. The FlexNativeMenu |
| * provides a way to define native operating system menus (such as window, application, and |
| * context menus) using techniques that are familiar to Flex developers and consistent with |
| * other Flex menu components, such as using MXML and data providers to specify menu structure. |
| * However, unlike Flex menu components, the menus that are defined by a FlexNativeMenu |
| * component are rendered by the host operating system as part of an AIR application, rather |
| * than being created as visual components by Flex. |
| * |
| * <p>Like other Flex menu components, to define the structure of a menu represented by a |
| * FlexNativeMenu component, you create a data provider such as an XML hierarchy or an array |
| * of objects containing data to be used to define the menu. Several properties can be set to |
| * define how the data provider data is interpreted, such as the <code>labelField</code> property |
| * to specify the data field that is used for the menu item label, the <code>keyEquivalentField</code> |
| * property to specify the field that defines a keyboard equivalent shortcut for the menu item, |
| * and the <code>mnemonicIndexField</code> property to specify the field that defines the index |
| * position of the character in the label that is used as the menu item's mnemonic.</p> |
| * |
| * <p>The data provider for FlexNativeMenu items can specify several attributes that determine how |
| * the item is displayed and behaves, as the following XML data provider shows:</p> |
| * <pre> |
| * <mx:XML format="e4x" id="myMenuData"> |
| * <root> |
| * <menuitem label="MenuItem A"> |
| * <menuitem label="SubMenuItem A-1" enabled="False"/> |
| * <menuitem label="SubMenuItem A-2"/> |
| * </menuitem> |
| * <menuitem label="MenuItem B" type="check" toggled="true"/> |
| * <menuitem label="MenuItem C" type="check" toggled="false"/> |
| * <menuitem type="separator"/> |
| * <menuitem label="MenuItem D"> |
| * <menuitem label="SubMenuItem D-1"/> |
| * <menuitem label="SubMenuItem D-2"/> |
| * <menuitem label="SubMenuItem D-3"/> |
| * </menuitem> |
| * </root> |
| * </mx:XML></pre> |
| * |
| * <p>The following table lists the attributes you can specify, |
| * their data types, their purposes, and how the data provider must represent |
| * them if the menu uses the DefaultDataDescriptor class to parse the data provider:</p> |
| * |
| * <table class="innertable"> |
| * <tr> |
| * <th>Attribute</th> |
| * <th>Type</th> |
| * <th>Description</th> |
| * </tr> |
| * <tr> |
| * <td><code>altKey</code></td> |
| * <td>Boolean</td> |
| * <td>Specifies whether the Alt key is required as part of the key equivalent for the item.</td> |
| * </tr> |
| * <tr> |
| * <td><code>cmdKey</code></td> |
| * <td>Boolean</td> |
| * <td><strong>Note:</strong> this attribute is deprecated as of Flex 3.2. Use |
| * <code>commandKey</code> instead. Specifies whether the Command key is required as part |
| * of the key equivalent for the item.</td> |
| * </tr> |
| * <tr> |
| * <td><code>commandKey</code></td> |
| * <td>Boolean</td> |
| * <td>Specifies whether the Command key is required as part of the key equivalent for the item.</td> |
| * </tr> |
| * <tr> |
| * <td><code>controlKey</code></td> |
| * <td>Boolean</td> |
| * <td>Specifies whether the Control key is required as part of the key equivalent for the item.</td> |
| * </tr> |
| * <tr> |
| * <td><code>ctrlKey</code></td> |
| * <td>Boolean</td> |
| * <td><strong>Note:</strong> this attribute is deprecated as of Flex 3.2. Use |
| * <code>controlKey</code> instead. Specifies whether the Control key is required as part |
| * of the key equivalent for the item.</td> |
| * </tr> |
| * <tr> |
| * <td><code>enabled</code></td> |
| * <td>Boolean</td> |
| * <td>Specifies whether the user can select the menu item (<code>true</code>), |
| * or not (<code>false</code>). If not specified, Flex treats the item as if |
| * the value were <code>true</code>. |
| * If you use the default data descriptor, data providers must use an <code>enabled</code> |
| * XML attribute or object field to specify this characteristic.</td> |
| * </tr> |
| * <tr> |
| * <td><code>keyEquivalent</code></td> |
| * <td>String</td> |
| * <td>Specifies a keyboard character which, when pressed, triggers an event as though |
| * the menu item was selected. The menu's <code>keyEquivalentField</code> or |
| * <code>keyEquivalentFunction</code> property determines the name of the field |
| * in the data that specifies the key equivalent, or a function for determining |
| * the key equivalents. (If the data provider is in E4X XML format, you must specify |
| * one of these properties to assign a key equivalent.)</td> |
| * </tr> |
| * <tr> |
| * <td><code>label</code></td> |
| * <td>String</td> |
| * <td>Specifies the text that appears in the control. This item is used for all |
| * menu item types except <code>separator</code>. |
| * The menu's <code>labelField</code> or <code>labelFunction</code> property |
| * determines the name of the field in the data that specifies the label, |
| * or a function for determining the labels. (If the data provider is in E4X XML format, |
| * you must specify one of these properties to display a label.) |
| * If the data provider is an Array of Strings, Flex uses the String value as the label.</td> |
| * </tr> |
| * <tr> |
| * <td><code>mnemonicIndex</code></td> |
| * <td>Integer</td> |
| * <td>Specifies the index position of the character in the label that is used as the |
| * mnemonic for the menu item. The menu's <code>mnemonicIndexField</code> or |
| * <code>mnemonicIndexFunction</code> property determines the name of the field |
| * in the data that specifies the mnemonic index, or a function for determining |
| * mnemonic index. (If the data provider is in E4X XML format, you must specify |
| * one of these properties to specify a mnemonic index in the data.) Alternatively, |
| * you can indicate that a character in the label is the menu item's mnemonic by |
| * including an underscore immediately to the left of that character.</td> |
| * </tr> |
| * <tr> |
| * <td><code>shiftKey</code></td> |
| * <td>Boolean</td> |
| * <td>Specifies whether the Shift key is required as part of the key equivalent for the item.</td> |
| * </tr> |
| * <tr> |
| * <td><code>toggled</code></td> |
| * <td>Boolean</td> |
| * <td>Specifies whether a check item is selected. |
| * If not specified, Flex treats the item as if the value were <code>false</code> |
| * and the item is not selected. |
| * If you use the default data descriptor, data providers must use a <code>toggled</code> |
| * XML attribute or object field to specify this characteristic.</td> |
| * </tr> |
| * <tr> |
| * <td><code>type</code></td> |
| * <td>String</td> |
| * <td>Specifies the type of menu item. Meaningful values are <code>separator</code> and |
| * <code>check</code>. Flex treats all other values, |
| * or nodes with no type entry, as normal menu entries. |
| * If you use the default data descriptor, data providers must use a <code>type</code> |
| * XML attribute or object field to specify this characteristic.</td> |
| * </tr> |
| * </table> |
| * |
| * <p>To create a window menu, set the FlexNativeMenu as the <code>menu</code> property of the |
| * Window or WindowedApplication instance on which the menu should appear. To create an application |
| * menu, assign the FlexNativeMenu as the <code>menu</code> property of the application's |
| * WindowedApplication. To assign a FlexNativeMenu as the context menu for a portion of the user interface, |
| * call the FlexNativeMenu instance's <code>setContextMenu()</code> method, passing the UI object |
| * as an argument. Call the FlexNativeMenu component's <code>display()</code> method to display the |
| * menu as a pop-up menu anywhere on one of the application's windows.</p> |
| * |
| * <p>To detect when menu items commands are triggered, register a listener for the <code>itemClick</code> |
| * event. You can also register a listener for the <code>menuShow</code> event to determine when |
| * any menu or submenu is opened.</p> |
| * |
| * @mxml |
| * <p>The <code><mx:FlexNativeMenu></code> tag supports the following tag attributes:</p> |
| * |
| * <pre> |
| * <mx:FlexNativeMenu |
| * <b>Properties</b> |
| * dataDescriptor="<i>mx.controls.treeClasses.DefaultDataDescriptor</i>" |
| * dataProvider="<i>undefined</i>" |
| * keyEquivalentField="keyEquivalent" |
| * keyEquivalentFunction="<i>undefined</i>" |
| * keyEquivalentModifiersFunction="<i>undefined</i>" |
| * labelField="label" |
| * labelFunction="<i>undefined</i>" |
| * mnemonicIndexField="mnemonicIndex" |
| * mnemonicIndexFunction="<i>undefined</i>" |
| * showRoot="true" |
| * |
| * <b>Events</b> |
| * itemClick="<i>No default</i>" |
| * menuShow="<i>No default</i>" |
| * /> |
| * </pre> |
| * |
| * @see flash.display.NativeMenu |
| * @see mx.events.FlexNativeMenuEvent |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public class FlexNativeMenu extends EventDispatcher implements |
| ILayoutManagerClient, IFlexContextMenu, IAutomationObject |
| { |
| include "../core/Version.as"; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Class variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * The character to use to indicate the mnemonic index in a label. By |
| * default, it is the underscore character, so in "C_ut", u would become |
| * the character for the mnemonic index. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| private static var MNEMONIC_INDEX_CHARACTER:String = "_"; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Constructor. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function FlexNativeMenu() |
| { |
| super(); |
| |
| _nativeMenu.addEventListener(Event.DISPLAYING, menuDisplayHandler, false, 0, true); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties: IAutomationObject |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // automationDelegate |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the <code>automationDelegate</code> property. |
| */ |
| private var _automationDelegate:IAutomationObject; |
| |
| /** |
| * The delegate object that handles the automation-related functionality. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get automationDelegate():Object |
| { |
| return _automationDelegate; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set automationDelegate(value:Object):void |
| { |
| _automationDelegate = value as IAutomationObject; |
| } |
| |
| //---------------------------------- |
| // automationName |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the <code>automationName</code> property. |
| */ |
| private var _automationName:String = null; |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get automationName():String |
| { |
| if (_automationName) |
| return _automationName; |
| if (automationDelegate) |
| return automationDelegate.automationName; |
| |
| return ""; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set automationName(value:String):void |
| { |
| _automationName = value; |
| } |
| |
| /** |
| * @copy mx.automation.IAutomationObject#automationValue |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get automationValue():Array |
| { |
| if (automationDelegate) |
| return automationDelegate.automationValue; |
| |
| return []; |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get numAutomationChildren():int |
| { |
| if (automationDelegate) |
| return automationDelegate.numAutomationChildren; |
| return 0; |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get automationTabularData():Object |
| { |
| if (automationDelegate) |
| return automationDelegate.automationTabularData; |
| return null; |
| } |
| |
| //---------------------------------- |
| // automationOwner |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _automationOwner:DisplayObjectContainer; |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get automationOwner():DisplayObjectContainer |
| { |
| return _automationOwner ? _automationOwner : automationParent; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set automationOwner(value:DisplayObjectContainer):void |
| { |
| _automationOwner = value; |
| } |
| |
| //---------------------------------- |
| // automationParent |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _automationParent:DisplayObjectContainer; |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get automationParent():DisplayObjectContainer |
| { |
| return _automationParent; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set automationParent(value:DisplayObjectContainer):void |
| { |
| _automationParent = value; |
| } |
| |
| //---------------------------------- |
| // automationEnabled |
| //---------------------------------- |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get automationEnabled():Boolean |
| { |
| // this is always enabled |
| return true; |
| } |
| |
| //---------------------------------- |
| // automationVisible |
| //---------------------------------- |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get automationVisible():Boolean |
| { |
| // this is always "visible" (may be a context menu and hidden at the time, but |
| // in terms of automation, this is always visible) |
| return true; |
| } |
| |
| //---------------------------------- |
| // showInAutomationHierarchy |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the <code>showInAutomationHierarchy</code> property. |
| */ |
| private var _showInAutomationHierarchy:Boolean = true; |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get showInAutomationHierarchy():Boolean |
| { |
| return _showInAutomationHierarchy; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set showInAutomationHierarchy(value:Boolean):void |
| { |
| _showInAutomationHierarchy = value; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties: ILayoutManagerClient |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // initialized |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the initialized property. |
| */ |
| private var _initialized:Boolean = false; |
| |
| /** |
| * @copy mx.core.UIComponent#initialized |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get initialized():Boolean |
| { |
| return _initialized; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set initialized(value:Boolean):void |
| { |
| _initialized = value; |
| } |
| |
| //---------------------------------- |
| // nestLevel |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the nestLevel property. |
| */ |
| private var _nestLevel:int = 1; |
| |
| // no one will likely set nestLevel (but there's a setter in case |
| // someone wants to. We default nestLevel to 1 as it's a top-level |
| // component that goes in the chrome. |
| |
| /** |
| * @copy mx.core.UIComponent#nestLevel |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get nestLevel():int |
| { |
| return _nestLevel; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set nestLevel(value:int):void |
| { |
| _nestLevel = value; |
| |
| // After nestLevel is initialized, add this object to the |
| // LayoutManager's queue, so that it is drawn at least once |
| invalidateProperties(); |
| } |
| |
| //---------------------------------- |
| // processedDescriptors |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the processedDescriptors property. |
| */ |
| private var _processedDescriptors:Boolean = false; |
| |
| /** |
| * @copy mx.core.UIComponent#processedDescriptors |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get processedDescriptors():Boolean |
| { |
| return _processedDescriptors; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set processedDescriptors(value:Boolean):void |
| { |
| _processedDescriptors = value; |
| } |
| |
| //---------------------------------- |
| // updateCompletePendingFlag |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the updateCompletePendingFlag property. |
| */ |
| private var _updateCompletePendingFlag:Boolean = false; |
| |
| /** |
| * A flag that determines if an object has been through all three phases |
| * of layout validation (provided that any were required). |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get updateCompletePendingFlag():Boolean |
| { |
| return _updateCompletePendingFlag; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set updateCompletePendingFlag(value:Boolean):void |
| { |
| _updateCompletePendingFlag = value; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables: Invalidation |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Whether this component needs to have its |
| * commitProperties() method called. |
| */ |
| private var invalidatePropertiesFlag:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var _nativeMenu:NativeMenu = new NativeMenu(); |
| |
| [Bindable("nativeMenuUpdate")] |
| |
| //---------------------------------- |
| // nativeMenu |
| //---------------------------------- |
| |
| /** |
| * Returns the flash.display.NativeMenu managed by this object, |
| * or null if there is not one. |
| * |
| * Any changes made directly to the underlying NativeMenu instance |
| * may be lost when changes are made to the menu or the underlying |
| * data provider. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get nativeMenu() : NativeMenu |
| { |
| return _nativeMenu; |
| } |
| |
| //---------------------------------- |
| // dataDescriptor |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var dataDescriptorChanged:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var _dataDescriptor:IMenuDataDescriptor = |
| new DefaultDataDescriptor(); |
| |
| [Inspectable(category="Data")] |
| |
| /** |
| * The object that accesses and manipulates data in the data provider. |
| * The FlexNativeMenu control delegates to the data descriptor for information |
| * about its data. This data is then used to parse and move about the |
| * data source. The data descriptor defined for the FlexNativeMenu is used for |
| * all child menus and submenus. |
| * |
| * <p>When you specify this property as an attribute in MXML, you must |
| * use a reference to the data descriptor, not the string name of the |
| * descriptor. Use the following format for setting the property:</p> |
| * |
| * <pre><mx:FlexNativeMenu id="flexNativeMenu" dataDescriptor="{new MyCustomDataDescriptor()}"/></pre> |
| * |
| * <p>Alternatively, you can specify the property in MXML as a nested |
| * subtag, as the following example shows:</p> |
| * |
| * <pre><mx:FlexNativeMenu> |
| * <mx:dataDescriptor> |
| * <myCustomDataDescriptor> |
| * </mx:dataDescriptor> |
| * ...</pre> |
| * |
| * <p>The default value is an internal instance of the |
| * DefaultDataDescriptor class.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get dataDescriptor():IMenuDataDescriptor |
| { |
| return IMenuDataDescriptor(_dataDescriptor); |
| } |
| |
| /** |
| * @private |
| */ |
| public function set dataDescriptor(value:IMenuDataDescriptor):void |
| { |
| _dataDescriptor = value; |
| |
| dataDescriptorChanged = true; |
| } |
| |
| //---------------------------------- |
| // dataProvider |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var dataProviderChanged:Boolean = false; |
| |
| /** |
| * @private |
| * Storage variable for the original dataProvider |
| */ |
| mx_internal var _rootModel:ICollectionView; |
| |
| [Bindable("collectionChange")] |
| [Inspectable(category="Data")] |
| |
| /** |
| * The hierarchy of objects that are used to define the structure |
| * of menu items in the NativeMenu. Individual data objects define |
| * menu items, and items with child items become menus and submenus. |
| * |
| * <p>The FlexNativeMenu control handles the source data object as follows:</p> |
| * |
| * <ul> |
| * <li>A String containing valid XML text is converted to an XML object.</li> |
| * <li>An XMLNode is converted to an XML object.</li> |
| * <li>An XMLList is converted to an XMLListCollection.</li> |
| * <li>Any object that implements the ICollectionView interface is cast to |
| * an ICollectionView.</li> |
| * <li>An Array is converted to an ArrayCollection.</li> |
| * <li>Any other type object is wrapped in an Array with the object as its sole |
| * entry.</li> |
| * </ul> |
| * |
| * @default "undefined" |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get dataProvider():Object |
| { |
| if (_rootModel) |
| { |
| return _rootModel; |
| } |
| else return null; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set dataProvider(value:Object):void |
| { |
| if (_rootModel) |
| { |
| _rootModel.removeEventListener(CollectionEvent.COLLECTION_CHANGE, |
| collectionChangeHandler); |
| } |
| |
| // handle strings and xml |
| if (typeof(value)=="string") |
| value = new XML(value); |
| else if (value is XMLNode) |
| value = new XML(XMLNode(value).toString()); |
| else if (value is XMLList) |
| value = new XMLListCollection(value as XMLList); |
| |
| if (value is XML) |
| { |
| _hasRoot = true; |
| var xl:XMLList = new XMLList(); |
| xl += value; |
| _rootModel = new XMLListCollection(xl); |
| } |
| //if already a collection dont make new one |
| else if (value is ICollectionView) |
| { |
| _rootModel = ICollectionView(value); |
| if (_rootModel.length == 1) |
| _hasRoot = true; |
| } |
| else if (value is Array) |
| { |
| _rootModel = new ArrayCollection(value as Array); |
| } |
| //all other types get wrapped in an ArrayCollection |
| else if (value is Object) |
| { |
| _hasRoot = true; |
| // convert to an array containing this one item |
| var tmp:Array = []; |
| tmp.push(value); |
| _rootModel = new ArrayCollection(tmp); |
| } |
| else |
| { |
| _rootModel = new ArrayCollection(); |
| } |
| //add listeners as weak references |
| _rootModel.addEventListener(CollectionEvent.COLLECTION_CHANGE, |
| collectionChangeHandler, false, 0, true); |
| //flag for processing in commitProps |
| dataProviderChanged = true; |
| invalidateProperties(); |
| |
| var event:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); |
| event.kind = CollectionEventKind.RESET; |
| collectionChangeHandler(event); |
| dispatchEvent(event); |
| } |
| |
| //---------------------------------- |
| // hasRoot |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Flag to indicate if the model has a root |
| */ |
| private var _hasRoot:Boolean = false; |
| |
| /** |
| * @copy mx.controls.Menu#hasRoot |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get hasRoot():Boolean |
| { |
| return _hasRoot; |
| } |
| |
| //---------------------------------- |
| // keyEquivalentField |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var keyEquivalentFieldChanged:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var _keyEquivalentField:String = "keyEquivalent"; |
| |
| [Bindable("keyEquivalentChanged")] |
| [Inspectable(category="Data", defaultValue="keyEquivalent")] |
| |
| /** |
| * The name of the field in the data provider that determines the |
| * key equivalent for each menu item. The set of values is defined |
| * in the Keyboard class, in the <code>KEYNAME_XXXX</code> constants. For example, |
| * consult that list for the value for a control character such as Home, Insert, etc. |
| * |
| * <p>Setting the <code>keyEquivalentFunction</code> property causes this property to be ignored.</p> |
| * |
| * @default "keyEquivalent" |
| * @see flash.ui.Keyboard |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get keyEquivalentField():String |
| { |
| return _keyEquivalentField; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set keyEquivalentField(value:String):void |
| { |
| if (_keyEquivalentField != value) |
| { |
| _keyEquivalentField = value; |
| keyEquivalentFieldChanged = true; |
| |
| invalidateProperties(); |
| |
| dispatchEvent(new Event("keyEquivalentFieldChanged")); |
| } |
| } |
| |
| //---------------------------------- |
| // keyEquivalentFunction |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _keyEquivalentFunction:Function; |
| |
| [Bindable("keyEquivalentFunctionChanged")] |
| [Inspectable(category="Data")] |
| |
| /** |
| * The function that determines the key equivalent for each menu item. |
| * If you omit this property, Flex uses the contents of the field or |
| * attribute specified by the <code>keyEquivalentField</code> property. |
| * If you specify this property, Flex ignores any <code>keyEquivalentField</code> |
| * property value. |
| * |
| * <p>The <code>keyEquivalentFunction</code> property is good for handling formatting, |
| * localization, and platform independence.</p> |
| * |
| * <p>The key equivalent function must take a single argument, which is the item |
| * in the data provider, and must return a String.</p> |
| * |
| * <pre><code>myKeyEquivalentFunction(item:Object):String</code></pre> |
| * |
| * @default "undefined" |
| * @see flash.ui.Keyboard |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get keyEquivalentFunction():Function |
| { |
| return _keyEquivalentFunction; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set keyEquivalentFunction(value:Function):void |
| { |
| if (_keyEquivalentFunction != value) |
| { |
| _keyEquivalentFunction = value; |
| keyEquivalentFieldChanged = true; |
| |
| invalidateProperties(); |
| |
| dispatchEvent(new Event("keyEquivalentFunctionChanged")); |
| } |
| } |
| |
| //---------------------------------- |
| // keyEquivalentModifiersFunction |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var keyEquivalentModifiersFunctionChanged:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var _keyEquivalentModifiersFunction:Function = keyEquivalentModifiersDefaultFunction; |
| |
| private function keyEquivalentModifiersDefaultFunction(data:Object):Array |
| { |
| var modifiers:Array = []; |
| var xmlModifiers:Array = ["@altKey", "@cmdKey", "@ctrlKey", |
| "@shiftKey", "@commandKey", "@controlKey"]; |
| var objectModifiers:Array = ["altKey", "cmdKey", "ctrlKey", |
| "shiftKey", "commandKey", "controlKey"]; |
| var keyboardModifiers:Array = [Keyboard.ALTERNATE, Keyboard.COMMAND, |
| Keyboard.CONTROL, Keyboard.SHIFT, |
| Keyboard.COMMAND, Keyboard.CONTROL]; |
| |
| if (data is XML) |
| { |
| for (var i:int = 0; i < xmlModifiers.length; i++) |
| { |
| try |
| { |
| var modifier:* = data[xmlModifiers[i]]; |
| if (modifier[0] == true) |
| modifiers.push(keyboardModifiers[i]); |
| } |
| catch(e:Error) |
| { |
| } |
| } |
| } |
| else if (data is Object) |
| { |
| for (i = 0; i < objectModifiers.length; i++) |
| { |
| try |
| { |
| modifier = data[objectModifiers[i]]; |
| if (String(modifier).toLowerCase() == "true") |
| modifiers.push(keyboardModifiers[i]); |
| } |
| catch(e:Error) |
| { |
| } |
| } |
| } |
| |
| return modifiers; |
| } |
| |
| [Bindable("keyEquivalentModifiersFunctionChanged")] |
| [Inspectable(category="Data")] |
| |
| /** |
| * The function that determines the key equivalent modifiers for each menu item. |
| * |
| * If you omit this property, Flex uses its own default function to determine the |
| * Array of modifiers by looking in the data provider data for the presence of |
| * the following (boolean) fields: <code>altKey</code>, <code>commandKey</code>, |
| * <code>controlKey</code>, and <code>shiftKey</code>. |
| * |
| * <p>The <code>keyEquivalentModifiersFunction</code> property is good for handling |
| * formatting, localization, and platform independence.</p> |
| * |
| * <p>The key equivalent modifiers function must take a single argument, which |
| * is the item in the data provider, and must return an array of modifier key names.</p> |
| * |
| * <pre><code>myKeyEquivalentModifiersFunction(item:Object):Array</code></pre> |
| * |
| * @default "undefined" |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get keyEquivalentModifiersFunction():Function |
| { |
| return _keyEquivalentModifiersFunction; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set keyEquivalentModifiersFunction(value:Function):void |
| { |
| if (_keyEquivalentModifiersFunction != value) |
| { |
| _keyEquivalentModifiersFunction = value; |
| keyEquivalentModifiersFunctionChanged = true; |
| |
| invalidateProperties(); |
| |
| dispatchEvent(new Event("keyEquivalentModifiersFunctionChanged")); |
| } |
| } |
| |
| //---------------------------------- |
| // labelField |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var labelFieldChanged:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var _labelField:String = "label"; |
| |
| [Bindable("labelFieldChanged")] |
| [Inspectable(category="Data", defaultValue="label")] |
| |
| /** |
| * The name of the field in the data provider that determines the |
| * text to display for each menu item. If the data provider is an Array of |
| * Strings, Flex uses each string value as the label. If the data |
| * provider is an E4X XML object, you must set this property explicitly. |
| * For example, if each XML elementin an E4X XML Object includes a "label" |
| * attribute containing the text to display for each menu item, set |
| * the labelField to <code>"@label"</code>. |
| * |
| * <p>In a label, you can specify the character to be used as the mnemonic index |
| * by preceding it with an underscore. For example, a label value of <code>"C_ut"</code> |
| * sets the mnemonic index to 1. Only the first underscore present is used for this |
| * purpose. To display a literal underscore character in the label, you can escape it |
| * using a double underscore. For example, a label value of <code>"C__u_t"</code> would |
| * result in a menu item with the label "C_ut" and a mnemonic index of 3 (the "t" |
| * character). If the field defined in the <code>mnemonicIndexField</code> property |
| * is present and set to a value greater than zero, that value takes precedence over |
| * any underscore-specified mnemonic index value.</p> |
| * |
| * <p>Setting the <code>labelFunction</code> property causes this property to be ignored.</p> |
| * |
| * @default "label" |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get labelField():String |
| { |
| return _labelField; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set labelField(value:String):void |
| { |
| if (_labelField != value) |
| { |
| _labelField = value; |
| labelFieldChanged = true; |
| |
| invalidateProperties(); |
| |
| dispatchEvent(new Event("labelFieldChanged")); |
| } |
| } |
| |
| //---------------------------------- |
| // labelFunction |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _labelFunction:Function; |
| |
| [Bindable("labelFunctionChanged")] |
| [Inspectable(category="Data")] |
| |
| /** |
| * The function that determines the text to display for each menu item. |
| * The label function must find the appropriate field or fields in the |
| * data provider and return a displayable string. |
| * |
| * <p>If you omit this property, Flex uses the contents of the field or |
| * attribute specified by the <code>labelField</code> property. |
| * If you specify this property, Flex ignores any <code>labelField</code> |
| * property value.</p> |
| * |
| * <p>The <code>labelFunction</code> property can be helpful for handling formatting, |
| * localization, and platform-independence.</p> |
| * |
| * <p>The label function must take a single argument, which is the item |
| * in the data provider, and must return a String.</p> |
| * |
| * <pre><code>myLabelFunction(item:Object):String</code></pre> |
| * |
| * @default "undefined" |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get labelFunction():Function |
| { |
| return _labelFunction; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set labelFunction(value:Function):void |
| { |
| if (_labelFunction != value) |
| { |
| _labelFunction = value; |
| labelFieldChanged = true; |
| |
| invalidateProperties(); |
| |
| dispatchEvent(new Event("labelFunctionChanged")); |
| } |
| } |
| |
| //---------------------------------- |
| // mnemonicIndexField |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var mnemonicIndexFieldChanged:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var _mnemonicIndexField:String = "mnemonicIndex"; |
| |
| [Bindable("mnemonicIndexChanged")] |
| [Inspectable(category="Data", defaultValue="mnemonicIndex")] |
| |
| /** |
| * The name of the field in the data provider that determines the |
| * mnemonic index for each menu item. |
| * |
| * <p>If the field specified by this property contains a number greater |
| * than zero, that mnemonic index |
| * takes precedence over one specified by an underscore in the label.</p> |
| * |
| * <p>Setting the <code>mnemonicIndexFunction</code> property causes |
| * this property to be ignored.</p> |
| * |
| * @default "mnemonicIndex" |
| * |
| * @see #labelField |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get mnemonicIndexField():String |
| { |
| return _mnemonicIndexField; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set mnemonicIndexField(value:String):void |
| { |
| if (_mnemonicIndexField != value) |
| { |
| _mnemonicIndexField = value; |
| mnemonicIndexFieldChanged = true; |
| |
| invalidateProperties(); |
| |
| dispatchEvent(new Event("mnemonicIndexFieldChanged")); |
| } |
| } |
| |
| //---------------------------------- |
| // mnemonicIndexFunction |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _mnemonicIndexFunction:Function; |
| |
| [Bindable("mnemonicIndexFunctionChanged")] |
| [Inspectable(category="Data")] |
| |
| /** |
| * The function that determines the mnemonic index for each menu item. |
| * |
| * <p>If you omit this property, Flex uses the contents of the field or |
| * attribute specified by the <code>mnemonicIndexField</code> property. |
| * If you specify this property, Flex ignores any <code>mnemonicIndexField</code> |
| * property value.</p> |
| * |
| * <p>If this property is defined and the function returns a number greater than |
| * zero for a data item, the returned mnemonic index |
| * takes precedence over one specified by an underscore in the label.</p> |
| * |
| * <p>The <code>mnemonicIndexFunction</code> property is good for handling formatting, |
| * localization, and platform independence.</p> |
| * |
| * <p>The mnemonic index function must take a single argument which is the item |
| * in the data provider and return an int.</p> |
| * |
| * <pre><code>myMnemonicIndexFunction(item:Object):int</code></pre> |
| * |
| * @default "undefined" |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get mnemonicIndexFunction():Function |
| { |
| return _mnemonicIndexFunction; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set mnemonicIndexFunction(value:Function):void |
| { |
| if (_mnemonicIndexFunction != value) |
| { |
| _mnemonicIndexFunction = value; |
| mnemonicIndexFieldChanged = true; |
| |
| invalidateProperties(); |
| |
| dispatchEvent(new Event("mnemonicIndexFunctionChanged")); |
| } |
| } |
| |
| //---------------------------------- |
| // showRoot |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage variable for showRoot flag. |
| */ |
| private var _showRoot:Boolean = true; |
| |
| /** |
| * @private |
| */ |
| private var showRootChanged:Boolean = false; |
| |
| [Inspectable(category="Data", enumeration="true,false", defaultValue="false")] |
| |
| /** |
| * A Boolean flag that specifies whether to display the data provider's |
| * root node. |
| * |
| * <p>If the data provider has a root node, and the <code>showRoot</code> property |
| * is set to <code>false</code>, the top-level menu items displayed by the |
| * FlexNativeMenu control correspond to the immediate descendants of the root node.</p> |
| * |
| * <p>This flag has no effect when using a data provider without a root nodes, |
| * such as a List or Array.</p> |
| * |
| * @default true |
| * @see #hasRoot |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get showRoot():Boolean |
| { |
| return _showRoot; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set showRoot(value:Boolean):void |
| { |
| if (_showRoot != value) |
| { |
| showRootChanged = true; |
| _showRoot = value; |
| invalidateProperties(); |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @copy mx.core.UIComponent#invalidateProperties() |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function invalidateProperties():void |
| { |
| // Don't try to add the object to the display list queue until we've |
| // been assigned a nestLevel, or we'll get added at the wrong place in |
| // the LayoutManager's priority queue. |
| if (!invalidatePropertiesFlag && nestLevel > 0) |
| { |
| invalidatePropertiesFlag = true; |
| if (UIComponentGlobals.layoutManager) |
| UIComponentGlobals.layoutManager.invalidateProperties(this); |
| else |
| { |
| var myTimer:Timer = new Timer(100, 1); |
| myTimer.addEventListener(TimerEvent.TIMER, validatePropertiesTimerHandler); |
| myTimer.start(); |
| } |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| public function validatePropertiesTimerHandler(event:TimerEvent):void |
| { |
| validateProperties(); |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function validateProperties():void |
| { |
| if (invalidatePropertiesFlag) |
| { |
| commitProperties(); |
| |
| invalidatePropertiesFlag = false; |
| } |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function validateSize(recursive:Boolean = false):void |
| { |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function validateDisplayList():void |
| { |
| } |
| |
| /** |
| * Validates and updates the properties and layout of this object |
| * and redraws it, if necessary. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function validateNow():void |
| { |
| // Since we don't have commit/measure/layout phases, |
| // all we need to do here is the commit phase |
| if (invalidatePropertiesFlag) |
| validateProperties(); |
| } |
| |
| /** |
| * Sets the context menu of the InteractiveObject to the underlying native menu. |
| * |
| * @param data The interactive object. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function setContextMenu(component:InteractiveObject):void |
| { |
| component.contextMenu = nativeMenu; |
| |
| if (component is Application) |
| { |
| var systemManager:ISystemManager = Application(component).systemManager; |
| |
| if (systemManager is InteractiveObject) |
| InteractiveObject(systemManager).contextMenu = nativeMenu; |
| } |
| |
| automationParent = component as DisplayObjectContainer; |
| automationOwner = component as DisplayObjectContainer; |
| |
| component.dispatchEvent(new Event("flexContextMenuChanged")); |
| } |
| |
| /** |
| * Unsets the context menu of the InteractiveObject that has been set to |
| * the underlying native menu. |
| * |
| * @param data The interactive object. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function unsetContextMenu(component:InteractiveObject):void |
| { |
| component.contextMenu = null; |
| |
| automationParent = null; |
| automationOwner = null; |
| |
| component.dispatchEvent(new Event("flexContextMenuChanged")); |
| } |
| |
| /** |
| * Processes the properties set on the component. |
| * |
| * @see mx.core.UIComponent#commitProperties() |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function commitProperties():void |
| { |
| if (showRootChanged) |
| { |
| if (!_hasRoot) |
| showRootChanged = false; |
| } |
| |
| if (dataProviderChanged ||showRootChanged || |
| labelFieldChanged || dataDescriptorChanged) |
| { |
| var tmpCollection:ICollectionView; |
| |
| //reset flags |
| dataProviderChanged = false; |
| showRootChanged = false; |
| labelFieldChanged = false; |
| dataDescriptorChanged = false; |
| |
| // are we swallowing the root? |
| if (_rootModel && !_showRoot && _hasRoot) |
| { |
| var rootItem:* = _rootModel.createCursor().current; |
| if (rootItem != null && |
| _dataDescriptor.isBranch(rootItem, _rootModel) && |
| _dataDescriptor.hasChildren(rootItem, _rootModel)) |
| { |
| // then get rootItem children |
| tmpCollection = |
| _dataDescriptor.getChildren(rootItem, _rootModel); |
| } |
| } |
| |
| // remove all items first. This is better than creating a new NativeMenu |
| // as the root since we have the same reference |
| clearMenu(_nativeMenu); |
| |
| // make top level items |
| if (_rootModel) |
| { |
| if (!tmpCollection) |
| tmpCollection = _rootModel; |
| // not really a default handler, but we need to |
| // be later than the wrapper |
| tmpCollection.addEventListener(CollectionEvent.COLLECTION_CHANGE, |
| collectionChangeHandler, |
| false, |
| EventPriority.DEFAULT_HANDLER, true); |
| |
| populateMenu(_nativeMenu, tmpCollection); |
| } |
| |
| dispatchEvent(new Event("nativeMenuChange")); |
| } |
| } |
| |
| /** |
| * Creates a menu and adds appropriate listeners |
| * |
| * @private |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| private function createMenu():NativeMenu |
| { |
| var menu:NativeMenu = new NativeMenu(); |
| // need to do this in the constructor for the root nativeMenu |
| menu.addEventListener(Event.DISPLAYING, menuDisplayHandler, false, 0, true); |
| |
| return menu; |
| } |
| |
| /** |
| * Clears out all items in a given menu |
| * |
| * @private |
| */ |
| private function clearMenu(menu:NativeMenu):void |
| { |
| var numItems:int = menu.numItems; |
| for (var i:int = 0; i < numItems; i++) |
| { |
| menu.removeItemAt(0); |
| } |
| } |
| |
| /** |
| * Populates a menu and the related submenus given a collection |
| * |
| * @private |
| */ |
| private function populateMenu(menu:NativeMenu, collection:ICollectionView):NativeMenu |
| { |
| var collectionLength:int = collection.length; |
| for (var i:int = 0; i < collectionLength; i++) |
| { |
| try |
| { |
| insertMenuItem(menu, i, collection[i]); |
| } |
| catch(e:ItemPendingError) |
| { |
| //we probably dont need to actively recover from here |
| } |
| } |
| |
| return menu; |
| } |
| |
| /** |
| * Adds the NativeMenuItem to the NativeMenu. This methods looks at the |
| * properties of the data sent in and sets them properly on the NativeMenuItem. |
| * |
| * @private |
| */ |
| private function insertMenuItem(menu:NativeMenu, index:int, data:Object):void |
| { |
| if (dataProviderChanged) |
| { |
| commitProperties(); |
| return; |
| } |
| |
| var type:String = dataDescriptor.getType(data).toLowerCase(); |
| var isSeparator:Boolean = (type == "separator"); |
| |
| // label changes later, but separator is read-only so need to know here |
| var nativeMenuItem:NativeMenuItem = new NativeMenuItem("", isSeparator); |
| |
| if (!isSeparator) |
| { |
| // enabled |
| nativeMenuItem.enabled = dataDescriptor.isEnabled(data); |
| |
| // checked |
| nativeMenuItem.checked = type == "check" && dataDescriptor.isToggled(data); |
| |
| // data |
| nativeMenuItem.data = dataDescriptor.getData(data, _rootModel); |
| |
| // key equivalent |
| nativeMenuItem.keyEquivalent = itemToKeyEquivalent(data); |
| |
| // key equivalent modifiers |
| nativeMenuItem.keyEquivalentModifiers = itemToKeyEquivalentModifiers(data); |
| |
| // label and mnemonic index |
| var labelData:String = itemToLabel(data); |
| var mnemonicIndex:int = itemToMnemonicIndex(data); |
| |
| if (mnemonicIndex >= 0) |
| { |
| nativeMenuItem.label = parseLabelToString(labelData); |
| nativeMenuItem.mnemonicIndex = mnemonicIndex; |
| } |
| else |
| { |
| nativeMenuItem.label = parseLabelToString(labelData); |
| nativeMenuItem.mnemonicIndex = parseLabelToMnemonicIndex(labelData); |
| } |
| |
| // event listeners |
| nativeMenuItem.addEventListener(flash.events.Event.SELECT, itemSelectHandler, false, 0, true); |
| |
| // recursive |
| if (dataDescriptor.isBranch(data, _rootModel) && |
| dataDescriptor.hasChildren(data, _rootModel)) |
| { |
| nativeMenuItem.submenu = createMenu(); |
| populateMenu(nativeMenuItem.submenu, |
| dataDescriptor.getChildren(data, _rootModel)); |
| } |
| } |
| |
| // done! |
| menu.addItem(nativeMenuItem); |
| } |
| |
| /** |
| * Pops up this menu at the specified location. |
| * |
| * @param stage The Stage object on which to display this menu. |
| * |
| * @param x The number of horizontal pixels, relative to the origin of stage, |
| * at which to display this menu. |
| * |
| * @param y The number of vertical pixels, relative to the origin of stage, |
| * at which to display this menu. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function display(stage:Stage, x:int, y:int):void |
| { |
| nativeMenu.display(stage, x, y); |
| } |
| |
| /** |
| * Returns the key equivalent for the given data object |
| * based on the <code>keyEquivalentField</code> and <code>keyEquivalentFunction</code> |
| * properties. If the method cannot convert the parameter to a String, it returns an |
| * empty string. |
| * |
| * @param data Object to be displayed. |
| * |
| * @return The key equivalent based on the data. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function itemToKeyEquivalent(data:Object):String |
| { |
| if (data == null) |
| return ""; |
| |
| if (keyEquivalentFunction != null) |
| return keyEquivalentFunction(data); |
| |
| if (data is XML) |
| { |
| try |
| { |
| if (data[keyEquivalentField].length() != 0) |
| { |
| data = data[keyEquivalentField]; |
| return data.toString(); |
| } |
| |
| //if (XMLList(data.@keyEquivalent).length() != 0) |
| //{ |
| // data = data.@keyEquivalent; |
| //} |
| } |
| catch(e:Error) |
| { |
| } |
| } |
| else if (data is Object) |
| { |
| try |
| { |
| if (data[keyEquivalentField] != null) |
| { |
| data = data[keyEquivalentField]; |
| return data.toString(); |
| } |
| } |
| catch(e:Error) |
| { |
| } |
| } |
| |
| return ""; |
| } |
| |
| /** |
| * Returns the key equivalent modifiers for the given data object |
| * based on the <code>keyEquivalentModifiersFunction</code> property. |
| * If the method cannot convert the parameter to an Array of modifiers, |
| * it returns an empty Array. |
| * |
| * @param data Object to be displayed. |
| * |
| * @return The array of key equivalent modifiers based on the data. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function itemToKeyEquivalentModifiers(data:Object):Array |
| { |
| if (data == null) |
| return []; |
| |
| if (keyEquivalentModifiersFunction != null) |
| return keyEquivalentModifiersFunction(data); |
| |
| return []; |
| } |
| |
| /** |
| * Returns the String to use as the menu item label for the given data |
| * object, based on the <code>labelField</code> and <code>labelFunction</code> |
| * properties. |
| * If the method cannot convert the parameter to a String, it returns a |
| * single space. |
| * |
| * @param data Object to be displayed. |
| * |
| * @return The string to be displayed based on the data. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function itemToLabel(data:Object):String |
| { |
| if (data == null) |
| return " "; |
| |
| if (labelFunction != null) |
| return labelFunction(data); |
| |
| if (data is XML) |
| { |
| try |
| { |
| if (data[labelField].length() != 0) |
| data = data[labelField]; |
| |
| //if (XMLList(data.@label).length() != 0) |
| //{ |
| // data = data.@label; |
| //} |
| } |
| catch(e:Error) |
| { |
| } |
| } |
| else if (data is Object) |
| { |
| try |
| { |
| if (data[labelField] != null) |
| data = data[labelField]; |
| } |
| catch(e:Error) |
| { |
| } |
| } |
| else if (data is String) |
| return String(data); |
| |
| try |
| { |
| return data.toString(); |
| } |
| catch(e:Error) |
| { |
| } |
| |
| return " "; |
| } |
| |
| /** |
| * Returns the mnemonic index for the given data object |
| * based on the <code>mnemonicIndexField</code> and <code>mnemonicIndexFunction</code> |
| * properties. If the method cannot convert the parameter to an integer, it returns -1. |
| * |
| * @param data Object to be displayed. |
| * |
| * @return The mnemonic index based on the data. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function itemToMnemonicIndex(data:Object):int |
| { |
| if (data == null) |
| return -1; |
| |
| var mnemonicIndex:int; |
| |
| if (mnemonicIndexFunction != null) |
| return mnemonicIndexFunction(data); |
| |
| if (data is XML) |
| { |
| try |
| { |
| if (data[mnemonicIndexField].length() != 0) |
| { |
| mnemonicIndex = data[mnemonicIndexField]; // no need for parseInt?? |
| return mnemonicIndex; |
| } |
| |
| //if (XMLList(data.@mnemonicIndex).length() != 0) |
| //{ |
| // data = data.@mnemonicIndex; |
| //} |
| } |
| catch(e:Error) |
| { |
| } |
| } |
| else if (data is Object) |
| { |
| try |
| { |
| if (data[mnemonicIndexField] != null) |
| { |
| mnemonicIndex = data[mnemonicIndexField]; |
| return mnemonicIndex; |
| } |
| } |
| catch(e:Error) |
| { |
| } |
| } |
| |
| return -1; |
| } |
| |
| /** |
| * Determines the actual label to be used for the NativeMenuItem |
| * by removing underscore characters and converting escaped underscore |
| * characters, if there are any. |
| * |
| * @param data The data to parse for the label. |
| * |
| * @return The label. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function parseLabelToString(data:String):String |
| { |
| const singleCharacter:RegExp = new RegExp(MNEMONIC_INDEX_CHARACTER, "g"); |
| const doubleCharacter:RegExp = new RegExp(MNEMONIC_INDEX_CHARACTER + MNEMONIC_INDEX_CHARACTER, "g"); |
| var dataWithoutEscapedUnderscores:Array = data.split(doubleCharacter); |
| |
| // now need to find lone underscores and remove it |
| var len:int = dataWithoutEscapedUnderscores.length; |
| for(var i:int = 0; i < len; i++) |
| { |
| var str:String = String(dataWithoutEscapedUnderscores[i]); |
| dataWithoutEscapedUnderscores[i] = str.replace(singleCharacter, ""); |
| } |
| |
| return dataWithoutEscapedUnderscores.join(MNEMONIC_INDEX_CHARACTER); |
| } |
| |
| /** |
| * Extracts the mnemonic index from a label based on the presence of |
| * an underscore character. It finds the leading underscore character if |
| * there is one and uses that as the index. |
| * |
| * @param data The data to parse for the index. |
| * |
| * @return The index. |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function parseLabelToMnemonicIndex(data:String):int |
| { |
| const doubleCharacter:RegExp = new RegExp(MNEMONIC_INDEX_CHARACTER + MNEMONIC_INDEX_CHARACTER, "g"); |
| var dataWithoutEscapedUnderscores:Array = data.split(doubleCharacter); |
| |
| // now need to find first underscore |
| var len:int = dataWithoutEscapedUnderscores.length; |
| var strLengthUpTo:int = 0; // length of string accumulator |
| for(var i:int = 0; i < len; i++) |
| { |
| var str:String = String(dataWithoutEscapedUnderscores[i]); |
| var index:int = str.indexOf(MNEMONIC_INDEX_CHARACTER); |
| |
| if (index >= 0) |
| return index + strLengthUpTo; |
| |
| strLengthUpTo += str.length + MNEMONIC_INDEX_CHARACTER.length; |
| } |
| |
| return -1; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods: IAutomationObject |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function createAutomationIDPart(child:IAutomationObject):Object |
| { |
| if (automationDelegate) |
| return automationDelegate.createAutomationIDPart(child); |
| return null; |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function createAutomationIDPartWithRequiredProperties(child:IAutomationObject, |
| properties:Array):Object |
| { |
| if (automationDelegate) |
| return automationDelegate.createAutomationIDPartWithRequiredProperties(child, properties); |
| return null; |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function resolveAutomationIDPart(criteria:Object):Array |
| { |
| if (automationDelegate) |
| return automationDelegate.resolveAutomationIDPart(criteria); |
| return []; |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function getAutomationChildAt(index:int):IAutomationObject |
| { |
| if (automationDelegate) |
| return automationDelegate.getAutomationChildAt(index); |
| return null; |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function getAutomationChildren():Array |
| { |
| if (automationDelegate) |
| return automationDelegate.getAutomationChildren(); |
| return null; |
| } |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function replayAutomatableEvent(event:Event):Boolean |
| { |
| if (automationDelegate) |
| return automationDelegate.replayAutomatableEvent(event); |
| return false; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Event handlers |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| private function itemSelectHandler(event:Event):void |
| { |
| var nativeMenuItem:NativeMenuItem = event.target as NativeMenuItem; |
| |
| var type:String = dataDescriptor.getType(nativeMenuItem.data).toLowerCase(); |
| if (type == "check") |
| { |
| var checked:Boolean = !dataDescriptor.isToggled(nativeMenuItem.data); |
| nativeMenuItem.checked = checked; |
| dataDescriptor.setToggled(nativeMenuItem.data, checked); |
| // this causes an update event which ends up re-creating |
| // the whole menu... (SDK-13109) |
| } |
| |
| var menuEvent:FlexNativeMenuEvent = new FlexNativeMenuEvent(FlexNativeMenuEvent.ITEM_CLICK); |
| menuEvent.nativeMenu = nativeMenuItem.menu; |
| menuEvent.index = nativeMenuItem.menu.getItemIndex(nativeMenuItem); |
| menuEvent.nativeMenuItem = nativeMenuItem; |
| menuEvent.label = nativeMenuItem.label; |
| menuEvent.item = nativeMenuItem.data; |
| dispatchEvent(menuEvent); |
| } |
| |
| /** |
| * @private |
| */ |
| private function menuDisplayHandler(event:Event):void |
| { |
| var nativeMenu:NativeMenu = event.target as NativeMenu; |
| |
| var menuEvent:FlexNativeMenuEvent = new FlexNativeMenuEvent(FlexNativeMenuEvent.MENU_SHOW); |
| menuEvent.nativeMenu = nativeMenu; |
| dispatchEvent(menuEvent); |
| } |
| |
| /** |
| * @private |
| */ |
| private function collectionChangeHandler(ce:CollectionEvent):void |
| { |
| //trace("[FlexNativeMenu] caught Model changed"); |
| if (ce.kind == CollectionEventKind.ADD) |
| { |
| dataProviderChanged = true; |
| invalidateProperties(); |
| // should handle elegantly with better performance |
| //trace("[FlexNativeMenu] add event"); |
| } |
| else if (ce.kind == CollectionEventKind.REMOVE) |
| { |
| dataProviderChanged = true; |
| invalidateProperties(); |
| // should handle elegantly with better performance |
| //trace("[FlexNativeMenu] remove event at:", ce.location); |
| } |
| else if (ce.kind == CollectionEventKind.REFRESH) |
| { |
| dataProviderChanged = true; |
| dataProvider = dataProvider; //start over |
| invalidateProperties(); |
| //trace("[FlexNativeMenu] refresh event"); |
| } |
| else if (ce.kind == CollectionEventKind.RESET) |
| { |
| dataProviderChanged = true; |
| invalidateProperties(); |
| //trace("[FlexNativeMenu] reset event"); |
| } |
| else if (ce.kind == CollectionEventKind.UPDATE) |
| { |
| dataProviderChanged = true; |
| invalidateProperties(); |
| // should handle elegantly with better performance |
| // but can't right now |
| //trace("[FlexNativeMenu] update event"); |
| } |
| } |
| } |
| |
| } |