//////////////////////////////////////////////////////////////////////////////// // // 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.DisplayObject; import flash.display.Graphics; import flash.display.Shape; import flash.display.Sprite; import flash.events.Event; import flash.events.KeyboardEvent; import flash.events.MouseEvent; import flash.geom.Point; import flash.geom.Rectangle; import flash.ui.Keyboard; import flash.utils.Dictionary; import flash.utils.describeType; import flash.utils.getDefinitionByName; import flash.utils.getQualifiedClassName; import mx.collections.ArrayCollection; import mx.collections.CursorBookmark; import mx.collections.HierarchicalCollectionView; import mx.collections.HierarchicalCollectionViewCursor; import mx.collections.HierarchicalData; import mx.collections.ICollectionView; import mx.collections.IGroupingCollection; import mx.collections.IGroupingCollection2; import mx.collections.IHierarchicalCollectionView; import mx.collections.IHierarchicalData; import mx.collections.IViewCursor; import mx.collections.ItemResponder; import mx.collections.Sort; import mx.collections.SortField; import mx.collections.SummaryObject; import mx.collections.errors.ItemPendingError; import mx.controls.advancedDataGridClasses.AdvancedDataGridBaseSelectionData; import mx.controls.advancedDataGridClasses.AdvancedDataGridBaseSelectionPending; import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn; import mx.controls.advancedDataGridClasses.AdvancedDataGridColumnGroup; import mx.controls.advancedDataGridClasses.AdvancedDataGridGroupItemRenderer; import mx.controls.advancedDataGridClasses.AdvancedDataGridHeaderInfo; import mx.controls.advancedDataGridClasses.AdvancedDataGridListData; import mx.controls.advancedDataGridClasses.AdvancedDataGridRendererDescription; import mx.controls.advancedDataGridClasses.IAdvancedDataGridRendererProvider; import mx.controls.advancedDataGridClasses.SortInfo; import mx.controls.listClasses.BaseListData; import mx.controls.listClasses.IDropInListItemRenderer; import mx.controls.listClasses.IListItemRenderer; import mx.controls.listClasses.ListBaseSeekPending; import mx.controls.listClasses.ListRowInfo; import mx.core.ClassFactory; import mx.core.EdgeMetrics; import mx.core.EventPriority; import mx.core.FlexShape; import mx.core.FlexSprite; import mx.core.IDataRenderer; import mx.core.IFactory; import mx.core.IFlexDisplayObject; import mx.core.IInvalidating; import mx.core.IUITextField; import mx.core.ScrollPolicy; import mx.core.SpriteAsset; import mx.core.UIComponent; import mx.core.UIComponentGlobals; import mx.core.mx_internal; import mx.effects.Tween; import mx.events.AdvancedDataGridEvent; import mx.events.AdvancedDataGridEventReason; import mx.events.CollectionEvent; import mx.events.CollectionEventKind; import mx.events.DragEvent; import mx.events.FlexEvent; import mx.events.IndexChangedEvent; import mx.events.ListEvent; import mx.events.ListEventReason; import mx.events.ScrollEvent; import mx.events.ScrollEventDetail; import mx.events.ScrollEventDirection; import mx.events.TweenEvent; import mx.managers.DragManager; import mx.resources.IResourceManager; import mx.resources.ResourceManager; import mx.styles.ISimpleStyleClient; import mx.styles.IStyleClient; import mx.utils.UIDUtil; use namespace mx_internal; //-------------------------------------- // Events //-------------------------------------- /** * Dispatched when a branch of the navigation tree is closed or collapsed. * * @eventType mx.events.AdvancedDataGridEvent.ITEM_CLOSE * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Event(name="itemClose", type="mx.events.AdvancedDataGridEvent")] /** * Dispatched when a branch of the navigation tree is opened or expanded. * * @eventType mx.events.AdvancedDataGridEvent.ITEM_OPEN * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Event(name="itemOpen", type="mx.events.AdvancedDataGridEvent")] /** * Dispatched when a tree branch open or close operation is initiated. * * @eventType mx.events.AdvancedDataGridEvent.ITEM_OPENING * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Event(name="itemOpening", type="mx.events.AdvancedDataGridEvent")] /** * Dispatched when the user drags a column outside of its column group. * TheAdvancedDataGrid control does not provide a default event handler for this event. * * @eventType mx.events.AdvancedDataGridEvent.HEADER_DRAG_OUTSIDE * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Event(name="headerDragOutside", type="mx.events.AdvancedDataGridEvent")] /** * Dispatched when the user drops a column outside of its column group. * TheAdvancedDataGrid control doesn't provide a default handler for this event. * * @eventType mx.events.AdvancedDataGridEvent.HEADER_DROP_OUTSIDE * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Event(name="headerDropOutside", type="mx.events.AdvancedDataGridEvent")] //-------------------------------------- // Styles //-------------------------------------- include "../styles/metadata/PaddingStyles.as"; /** * Colors for rows in an alternating pattern. * Value can be an Array of two of more colors. * For the AdvancedDataGrid controls, * all items in a row have the same background color, * and each row's background color is determined from the Array of colors. * Used only if the backgroundColor property is not specified. * * @default undefined * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="alternatingItemColors", type="Array", arrayType="uint", format="Color", inherit="yes")] /** * Array of colors used for the rows of each level of the navigation tree * of the AdvancedDataGrid control, in descending order. * * @default undefined * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="depthColors", type="Array", arrayType="uint", format="Color", inherit="yes")] /** * The default icon for a leaf node of the navigation tree. * * The default value is TreeNodeIcon in the assets.swf file. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="defaultLeafIcon", type="Class", format="EmbeddedFile", inherit="no")] /** * The icon that is displayed next to an open branch node of the navigation tree. * * The default value is TreeDisclosureOpen in the assets.swf file. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="disclosureOpenIcon", type="Class", format="EmbeddedFile", inherit="no")] /** * The icon that is displayed next to a closed branch node of the navigation tree. * * The default value is TreeDisclosureClosed in the assets.swf file. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="disclosureClosedIcon", type="Class", format="EmbeddedFile", inherit="no")] /** * Specifies the folder open icon for a branch node of the navigation tree. * * The default value is TreeFolderOpen in the assets.swf file. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="folderOpenIcon", type="Class", format="EmbeddedFile", inherit="no")] /** * Specifies the folder closed icon for a branch node of the navigation tree. * * The default value is TreeFolderClosed in the assets.swf file. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="folderClosedIcon", type="Class", format="EmbeddedFile", inherit="no")] /** * The indentation for each node of the navigation tree, in pixels. * * @default 17 * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="indentation", type="Number", inherit="no")] /** * Length of an open or close transition for the navigation tree, in milliseconds. * * @default 250 * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="openDuration", type="Number", format="Time", inherit="no")] /** * Easing function to control component tweening. * *

The default value is undefined.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="openEasingFunction", type="Function", inherit="no")] /** * The skin that defines the appearance of the separators between the text * and icon parts of a header in a AdvancedDataGrid control. * * @default undefined * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="headerSortSeparatorSkin", type="Class", inherit="no")] /** * The class to use as the skin that defines the appearance of the * separators between column headers of different depth in a AdvancedDataGrid control. * * @default undefined * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="headerHorizontalSeparatorSkin", type="Class", inherit="no")] /** * The disabled color of a list item. * * @default 0xDDDDDD * * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="selectionDisabledColor", type="uint", format="Color", inherit="yes")] /** * Reference to an easingFunction function used for controlling programmatic tweening. * *

The default value is undefined.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="selectionEasingFunction", type="Function", inherit="no")] /** * Color of the text when the user rolls over a row. * * @default 0x2B333C * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="textRollOverColor", type="uint", format="Color", inherit="yes")] /** * Color of the text when the user selects a row. * * @default 0x2B333C * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="textSelectedColor", type="uint", format="Color", inherit="yes")] /** * The font family used by the AdvancedDataGridSortItemRenderer class * to render the sort icon in the column header. * * @default Verdana * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="sortFontFamily", type="String", inherit="yes")] /** * The font size used by the AdvancedDataGridSortItemRenderer class * to render the sort icon in the column header. * * @default 10 * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="sortFontSize", type="Number", format="Length", inherit="yes")] /** * The font style used by the AdvancedDataGridSortItemRenderer class * to render the sort icon in the column header. * * @default normal * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="sortFontStyle", type="String", enumeration="normal,italic", inherit="yes")] /** * The font weight used by the AdvancedDataGridSortItemRenderer class * to render the sort icon in the column header. * * @default normal * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="sortFontWeight", type="String", enumeration="normal,bold", inherit="yes")] /************************************************************************* * * End of Tree Events, Styles and Metadata * *************************************************************************/ [AccessibilityClass(implementation="mx.accessibility.AdvancedDataGridAccImpl")] [ResourceBundle("datamanagement")] /** * The AdvancedDataGrid control expands on the functionality of the standard DataGrid control * to add data visualization features to your Adobe Flex application. * These features provide greater control of data display, data aggregation, and data formatting. * * The AdvancedDataGrid control is like a List control except that it can show * more than one column of data, * making it suited for showing objects with multiple properties. *

* The AdvancedDataGrid control provides the following features: *

*

* The AdvancedDataGrid control is intended for viewing data, and not as a * layout tool like an HTML table. * The mx.containers package provides those layout tools. * * @mxml *

* The <mx:AdvancedDataGrid> tag inherits all of the tag attributes * of its superclass, except for labelField, iconField, * and iconFunction, and adds the following tag attributes: *

*
 *  <mx:AdvancedDataGrid
 *    Properties
 *    displayDisclosureIcon="true|false"
 *    displayItemsExpanded="false|true"
 *    groupedColumns="[]"
 *    groupIconFunction="null"
 *    groupItemRenderer="AdvancedDataGridGroupItemRenderer"
 *    groupLabelFunction="null"
 *    groupRowHeight="-1"
 *    itemIcons="undefined"
 *    lockedColumnCount="0"
 *    lockedRowCount="0"
 *    rendererProviders="[]"
 *    selectedCells="null"
 *    treeColumn="null"
 *     
 *    Styles
 *    alternatingItemColors="undefined"
 *    defaultLeafIcon="TreeNodeIcon"
 *    depthColors="undefined"
 *    disclosureClosedIcon="TreeDisclosureClosed"
 *    disclosureOpenIcon="TreeDisclosureOpen"
 *    folderClosedIcon="TreeFolderClosed"
 *    folderOpenIcon="TreeFolderOpen"
 *    headerHorizontalSeparatorSkin="undefined"
 *    indentation="17"
 *    openDuration="250"
 *    openEasingFunction="undefined"
 *    paddingLeft="2"
 *    paddingRight="0"
 *    selectionDisabledColor="#DDDDDD"
 *    selectionEasingFunction="undefined"
 *    sortFontFamily="Verdana"
 *    sortFontSize="10"
 *    sortFontStyle="normal"
 *    sortFontWeight="normal"
 *    textRollOverColor="#2B333C"
 *    textSelectedColor="#2B333C"
 *     
 *    Events
 *    headerDragOutside="No default"
 *    headerDropOutside="No default"
 *    itemClose="No default"
 *    itemOpen="No default"
 *    itemOpening="No default" 
 *  />
 *   
 *  The following AdvancedDataGrid code sample specifies the column order:
 *  <mx:AdvancedDataGrid>
 *    <mx:dataProvider>
 *        <mx:Object Artist="Pavement" Price="11.99"
 *          Album="Slanted and Enchanted"/>
 *        <mx:Object Artist="Pavement"
 *          Album="Brighten the Corners" Price="11.99"/>
 *    </mx:dataProvider>
 *    <mx:columns>
 *        <mx:AdvancedDataGridColumn dataField="Album"/>
 *        <mx:AdvancedDataGridColumn dataField="Price"/>
 *    </mx:columns>
 *  </mx:AdvancedDataGrid>
 *  
*

* * @see mx.controls.advancedDataGridClasses.AdvancedDataGridItemRenderer * @see mx.controls.advancedDataGridClasses.AdvancedDataGridColumn * @see mx.controls.advancedDataGridClasses.AdvancedDataGridDragProxy * @see mx.events.AdvancedDataGridEvent * @see mx.controls.DataGrid * * @includeExample examples/AdvancedDataGridExample.mxml * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public class AdvancedDataGrid extends AdvancedDataGridBaseEx { include "../core/Version.as"; //-------------------------------------------------------------------------- // // Class mixins // //-------------------------------------------------------------------------- /** * @private * Placeholder for mixin by AdvancedDataGridAccImpl. */ mx_internal static var createAccessibilityImplementation:Function; //-------------------------------------------------------------------------- // // Class constants // //-------------------------------------------------------------------------- /** * Indicates mouse is over the text part of the header. * Used as a return value by mouseEventToHeaderPart. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public static const HEADER_TEXT_PART:String = "headerTextPart"; /** * Indicates that the mouse is over the header part of the header. * Used as a return value by * the AdvancedDataGridHeaderRenderer.mouseEventToHeaderPart method. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public static const HEADER_ICON_PART:String = "headerIconPart"; /** * @private */ private static var resourceManager:IResourceManager = ResourceManager.getInstance(); //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * Constructor. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function AdvancedDataGrid() { super(); rendererDescriptionMap = new Dictionary(true); groupItemRenderer = new ClassFactory(AdvancedDataGridGroupItemRenderer); // Added Listener from Tree addEventListener(AdvancedDataGridEvent.ITEM_OPENING, expandItemHandler, false, EventPriority.DEFAULT_HANDLER); addEventListener(FlexEvent.UPDATE_COMPLETE, updateCompleteHandler); } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- /** * @private * Maps items to AdvancedDataGridDescription */ private var rendererDescriptionMap:Dictionary; /** * @private */ private var isMeasuringHeader:Boolean; /** * @private * Keep track of styles of the individual item renderers when custom * row/column formatting i.e. grid and column styleFunctions are applied. */ private var oldStyles:Dictionary; // Cell Selection variables /** * @private * The first AdvancedDataGridBaseSelectionData in a link list of * AdvancedDataGridBaseSelectionData. This represents the item that was * most recently selected. AdvancedDataGridBaseSelectionData instances are * linked together and keep track of the order the user selects an item. * This order is reflected in selectedCells. */ mx_internal var firstCellSelectionData:AdvancedDataGridBaseSelectionData; /** * A hash table of AdvancedDataGridBaseSelectionData objects that track * which items are currently selected. The table is indexed by the UID of * the items and then the column number. * * @see mx.controls.advancedDataGridClasses.AdvancedDataGridBaseSelectionData * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ mx_internal var cellSelectionData:Object = {}; /** * Map of selections keyes by x,y and referred to by modified isCellAlreadySelected() * and addToSelectedCells() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 4 */ mx_internal var cellSelections:Object = {}; /** * A hash table of selection indicators. This table allows the component * to quickly find and remove the indicators when the set of selected * items is cleared. The table is indexed by the item's UID and column number. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ mx_internal var cellSelectionIndicators:Object = {}; /** * A hash table of data provider item renderers currently in view. The * table is indexed by the data provider item's UID and column number and is * used to quickly get the renderer used to display a particular item. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ // Parallel data structure to visibleData in listItems. protected var visibleCellRenderers:Object = {}; /** * The column of the selected cell. * * Used in conjunction with selectedIndex property to determine * the column and row indices of the selected cell. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected var selectedColumnIndex:int = -1; /** * The column index of the item that is currently rolled over or under the cursor. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected var highlightColumnIndex:int = -1; /** * The column name of the item under the caret. * * Used in conjunction with the caretIndex property to determine * the column/row indices of the cell where the caret is located. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected var caretColumnIndex:int = -1; /** * The column index of the current anchor. * * Use this property in conjunction with the ListBase.anchorIndex property * to determine the column and row * indices of the cell where the anchor is located. * * @see mx.controls.listClasses.ListBase#anchorIndex * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected var anchorColumnIndex:int = -1; /** * A hash table of selection tweens. This allows the component to * quickly find and clean up any tweens in progress if the set * of selected items is cleared. The table is indexed by the item's UID * and column number. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected var cellSelectionTweens:Object = {}; /** * Flag to indicate that selectedCells has changed * * @private */ private var selectedCellsChanged:Boolean = false; /** * Temporary storage for selectedCells * * @private */ private var _temporary_selectedCells:Array = null; /** * Storage of sortInfos, used for the multi column sorting UI. * * @private */ private var visualSortInfo:Dictionary = new Dictionary(); /** * Flag to check if design view data is set, so that openNodes can also be changed. * * @private */ private var designViewDataFlag:Boolean = false; /** * @private */ private var lastColumnWidth:Number = NaN; /** * @private */ protected var movingSelectionLayer:Sprite; /** * @private */ private var headerSelected:Boolean = true; /** * @private */ private var _lockedColumnCountVal:int; /** * @private */ private var lockedColumnCountChanged:Boolean = false; /** * @private */ private var headerItemsList:Array; /** * @private */ private var headerDraggedOutside:Boolean = false; /** * @private */ private var dropInfo:AdvancedDataGridEvent; /** * @private */ private var lastHeaderInfos:Array; /** * @private */ private var horizontalSeparators:Array; /** * @private */ private var horizontalLockedSeparators:Array; /** * @private */ private var numSeparators:int = 0; private var groupedColumnsChanged:Boolean=false; private var columnsChanged:Boolean = false; private var designViewDataFlatType:String = "flat"; private var designViewDataTreeType:String = "tree"; private var designViewDataTypeChanged:Boolean = false; mx_internal var columnsToInfo:Dictionary = new Dictionary(); /** * @private * Needed for Accessibility to know if column grouping is present or not. */ mx_internal var columnGrouping:Boolean = false; /** * @private * Item is currently in the process of opening */ private var opening:Boolean; /** * The tween object that animates rows * * Users can add event listeners to this Object to get * notified when the tween starts, updates and ends. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected var tween:Object; /** * @private */ private var maskList:Array; /** * @private */ private var _userMaxHorizontalScrollPosition:Number = 0; /** * @private */ private var eventAfterTween:Object; /** * Storage for the Group item renderer in case of item opening/closing events * @private */ private var eventItemRenderer:IListItemRenderer; /** * @private */ private var oldLength:int = -1; /** * @private */ private var expandedItem:Object; /** * @private */ private var bSelectedItemRemoved:Boolean = false; /** * @private * Storage for the groupLabelField property. */ private var groupLabelField:String; /** * @private */ private var lastUserInteraction:Event; /** * @private * automation delegate access */ mx_internal var _dropData:Object; /** * @private */ mx_internal var isOpening:Boolean = false; /** * @private * used by opening tween * rowIndex is the row below the row that was picked * and is the first one that will actually change */ private var rowIndex:int; /** * @private * Number of rows that are or will be tweened */ private var rowsTweened:int; /** * @private */ private var rowList:Array; /** * @private * variable to hold the IHierarchicalCollectionView instance provided by the user. */ private var hierarchicalCollectionInstance:IHierarchicalCollectionView; /** * @private */ mx_internal var collectionLength:int; /** * @private */ private var dataProviderChanged:Boolean = false; /** * @private * Storage variable for the original dataProvider */ mx_internal var _rootModel:IHierarchicalData; /** * @private * Storage variable for changes to displayItemsExpanded. */ private var displayItemsExpandedChanged:Boolean = false; //-------------------------------------------------------------------------- // // Overridden properties // //-------------------------------------------------------------------------- //---------------------------------- // dataProvider //---------------------------------- [Bindable("collectionChange")] [Inspectable(category="Data", defaultValue="null")] /** * @private */ override public function get dataProvider():Object { if(super.dataProvider) return super.dataProvider; return null; } /** * * @private */ override public function set dataProvider(value:Object):void { if (itemEditorInstance) endEdit(AdvancedDataGridEventReason.OTHER); if (isCellSelectionMode()) { clearCellSelectionData(); } if (_rootModel) _rootModel.removeEventListener( CollectionEvent.COLLECTION_CHANGE, collectionChangeHandler); if (value is IHierarchicalData) { _rootModel = value as IHierarchicalData; } else if (value is IHierarchicalCollectionView) { // if the dataProvider is IHierarchicalCollectionView, // set it to super.dataProvider in commitProperties() _rootModel = IHierarchicalCollectionView(value).source; hierarchicalCollectionInstance = IHierarchicalCollectionView(value); } else { _rootModel = null; hierarchicalCollectionInstance = null; super.dataProvider = value; } if (_rootModel) _rootModel.addEventListener( CollectionEvent.COLLECTION_CHANGE, collectionChangeHandler); //flag for processing in commitProps dataProviderChanged = true; invalidateProperties(); } //---------------------------------- // lockedRowCount //---------------------------------- /** * The index of the first row in the control that scrolls. * Rows above this one remain fixed in view. * * @default 0 * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ override public function get lockedRowCount():int { // if dataProvider is hierarchical, return 0 // because lockedRowCount has no effect in a hierarchical display if (_rootModel is IHierarchicalData) return 0; return super.lockedRowCount; } //---------------------------------- // lockedColumnCount //---------------------------------- /** * The index of the first column in the control that scrolls. * Columns to the left of this one remain fixed in view. * *

When using column groups, a column group is considered to be a single column. * For example, if you set this property to 2, and the left-most two column groups * contain two and three children, respectively, then you have effectively locked the * first five columns of the control.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ override public function set lockedColumnCount(value:int):void { _lockedColumnCountVal = value; lockedColumnCountChanged = true; invalidateProperties(); } //---------------------------------- // columns //---------------------------------- /** * @private */ private var _columnsValue:Array; [Inspectable(category="General", arrayType="mx.controls.advancedDataGridClasses.AdvancedDataGridColumn")] /** * @private */ override public function set columns(value:Array):void { _columnsValue = value; columnsChanged = true; generatedColumns = false; invalidateProperties(); } //---------------------------------- // verticalScrollPosition //---------------------------------- /** * @private * Sets verticalScrollPosition and draw vertical lines again * if rendererProviders are used. */ override public function set verticalScrollPosition(value:Number):void { var oldAnchorColumnIndex:int = anchorColumnIndex; var oldCaretColumnIndex:int = caretColumnIndex; var oldSelectedColumnIndex:int = selectedColumnIndex; super.verticalScrollPosition = value; anchorColumnIndex = oldAnchorColumnIndex; caretColumnIndex = oldCaretColumnIndex; selectedColumnIndex = oldSelectedColumnIndex; // draw the vertically lines afresh if Custom Row Renderers are used // i.e., update the vertical lines if items are spanning columns if (rendererProviders.length != 0) drawVerticalSeparators(); // draw the horizontal lines afresh if groupRowHeight is not -1 // and its not equal to rowHeight if (!variableRowHeight && groupRowHeight != -1 && groupRowHeight != rowHeight) drawHorizontalSeparators(); // Display cells set in selectionMode which are not yet visible if (cellsWaitingToBeDisplayed) processCellsWaitingToBeDisplayed(); } //---------------------------------- // horizontalScrollPosition //---------------------------------- /** * @private */ override public function set horizontalScrollPosition(value:Number):void { var oldAnchorColumnIndex:int = anchorColumnIndex; var oldCaretColumnIndex:int = caretColumnIndex; var oldSelectedColumnIndex:int = selectedColumnIndex; super.horizontalScrollPosition = value; anchorColumnIndex = oldAnchorColumnIndex; caretColumnIndex = oldCaretColumnIndex; selectedColumnIndex = oldSelectedColumnIndex; // Display cells set in selectionMode which are not yet visible if (cellsWaitingToBeDisplayed) processCellsWaitingToBeDisplayed(); } //-------------------------------------------------------------------------- // // Properties // //-------------------------------------------------------------------------- //---------------------------------- // groupedColumns //---------------------------------- /** * @private */ private var _groupedColumns:Array; /** * An Array that defines the hierarchy of AdvancedDataGridColumn instances when performing column grouping. * If you specify both the columns and groupedColumns properties, * the control uses the groupedColumns property and ignores * the columns property. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get groupedColumns():Array { return _groupedColumns; } public function set groupedColumns(value:Array):void { //Validate if(validateGroupedColumns(value)) { if(_groupedColumns) { var n:int = _groupedColumns.length; for ( var i:int = 0; i < n; i++) { if(_groupedColumns[i] is AdvancedDataGridColumnGroup) columnGroupRendererChanged(AdvancedDataGridColumnGroup(_groupedColumns[i])); } } _groupedColumns = value; groupedColumnsChanged = true; invalidateProperties(); } } // // Cell Selection properties // //---------------------------------- // selectedCells //---------------------------------- /** * @private */ protected var _selectedCells:Array = []; [Inspectable(category="Data", arrayType="Object")] /** * Contains an Array of cell locations as row and column indices. * Changing the value of the selectionMode property * sets this property to null. * *

Note that these row and column indices are absolute values, * irrespective of the display. This is explained by a typical * selectedCells property setting as follows:

* *
     *  selectedCells = [ { rowIndex : r1, columnIndex : c1 },
     *                    { rowIndex : r2, columnIndex : c2 },
     *                    ... ]
* *

Then, dataProvider[r1], columns[c1], dataProvider[r2], columns[c2], * etc. will always be valid.

* *

If you want to programmatically change the set of selected cells, you * must get this Array, make modifications to the cells and order of * cells in the Array, and then assign the new array to the selectedCells * property. This is because the AdvancedDataGrid control returns a new * copy of the Array of selectedCells and therefore does not notice the * changes.

* *

The value of the selectionMode property determines the data in * the rowIndex and columnIndex properties, as the following table describes:

* * * * * * * * * * * * * * * * * * * * * * * * * * *
selectionModeValue of rowIndex and columnIndex properties
noneNo selection allowed in the control, and selectedCells is null.
singleRow Click any cell in the row to select the row. After the selection, * selectedCells contains a single Object: *

[{rowIndex:selectedRowIndex, columnIndex: -1}]

multipleRowsClick any cell in the row to select the row. * While holding down the Control key, click any cell in another row * to select the row for discontiguous selection. * While holding down the Shift key, click any cell in another row to select multiple, contiguous rows. * After the selection, selectedCells contains one Object for each selected row: *

[ {rowIndex: selectedRowIndex1, columnIndex: -1}, * {rowIndex: selectedRowIndex2, columnIndex: -1}, * ... * {rowIndex: selectedRowIndexN, columnIndex: -1} * ]

singleCellClick any cell to select the cell. * After the selection, selectedCells contains a single Object: *

[{rowIndex: selectedRowIndex, columnIndex:selectedColIndex}]

multipleCellsClick any cell to select the cell. * While holding down the Control key, click any cell to select the cell multiple discontiguous selection. * While holding down the Shift key, click any cell to select multiple, contiguous cells. * After the selection, selectedCells contains one Object for each selected cell: *

[ {rowIndex: selectedRowIndex1, columnIndex: selectedColIndex1}, * {rowIndex: selectedRowIndex2, columnIndex: selectedColIndex2}, * ... * {rowIndex: selectedRowIndexN, columnIndex: selectedColIndexN} * ]

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get selectedCells():Array { if (_selectedCells) return _selectedCells.slice(); // make a copy else return null; } /** * @private */ public function set selectedCells(value:Array):void { // clear selectedCells clearSelectedCells(); _temporary_selectedCells = value; syncCellSelections(value); clearPendingCells(); selectedCellsChanged = true; invalidateProperties(); invalidateDisplayList(); } //-------------------------------------------------------------------------- // Flex Builder design view functionality //-------------------------------------------------------------------------- //---------------------------------- // designViewDataType //---------------------------------- private var _designViewDataType:String = designViewDataTreeType; [Inspectable(enumeration="flat,tree", defaultValue="tree")] /** * Flag from Flex Builder on what kind of data is needed to display * in Design View. * * Unfortunately, this can't be mx_internal because we need to set it * from the AdvancedDataGridInserter.java class * * @private */ public function get designViewDataType():String { return _designViewDataType; } /** * @private */ public function set designViewDataType(value:String):void { _designViewDataType = value; designViewDataTypeChanged = true; } //---------------------------------- // dataType //---------------------------------- /** * What kind of data is being displayed? "tree" or "flat"? * * @private */ mx_internal function get dataType():String { if (dataProvider) return (dataProvider is IHierarchicalCollectionView) ? designViewDataTreeType : designViewDataFlatType; else return designViewDataTreeType; } //---------------------------------- // groupIconFunction //---------------------------------- /** * @private * Storage for groupIconFunction property. */ private var _groupIconFunction:Function; [Bindable("groupIconFunctionChanged")] [Inspectable(category="Data")] /** * A user-supplied callback function to run on each group item * to determine its branch icon in the navigation tree. * You can specify icons by using the itemIcons or setItemIcon properties * if you have predetermined icons for data items. * Use this callback function to determine the icon dynamically after examining the data. * *

The groupIconFunction takes the item in the data provider * and its depth and returns a Class defining the icon, or null property * to use the default icon. * The callback function must have the following signature:

* *
     *    groupIconFunction(item:Object,depth:int):Class
* * @default null * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get groupIconFunction():Function { return _groupIconFunction; } /** * @private */ public function set groupIconFunction(value:Function):void { _groupIconFunction = value; itemsSizeChanged = true; invalidateDisplayList(); dispatchEvent(new Event("groupIconFunctionChanged")); } //---------------------------------- // groupItemRenderer //---------------------------------- /** * @private * Storage for the groupItemRenderer property. */ private var _groupItemRenderer:IFactory; [Inspectable(category="Data")] /** * Specifies the item renderer used to display the branch nodes * in the navigation tree that correspond to groups. * By default, it is an instance of the AdvancedDataGridGroupItemRenderer class. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get groupItemRenderer():IFactory { return _groupItemRenderer; } /** * @private */ public function set groupItemRenderer(value:IFactory):void { _groupItemRenderer = value; invalidateSize(); invalidateDisplayList(); itemsSizeChanged = true; rendererChanged = true; dispatchEvent(new Event("itemRendererChanged")); } //---------------------------------- // groupLabelFunction //---------------------------------- /** * @private * Storage for groupLabelFunction property. */ private var _groupLabelFunction:Function; [Bindable("groupLabelFunctionChanged")] [Inspectable(category="Data")] /** * A callback function to run on each item to determine its label * in the navigation tree. * By default, the control looks for a property named label * on each data provider item and displays it. * However, some data sets do not have a label property, * or have another property that can be used for displaying. * An example is a data set that has lastName and firstName fields * but you want to display full names. * *

You can supply a groupLabelFunction that finds the * appropriate fields and returns a displayable string. The * groupLabelFunction is also good for handling formatting and * localization.

* *

The method signature for the AdvancedDataGrid * and AdvancedDataGridColumn classes is:

*
     *  myGroupLabelFunction(item:Object, column:AdvancedDataGridColumn):String
* *

Where item contains the AdvancedDataGrid item object, and * column specifies the AdvancedDataGrid column. * The function returns a String containing the label.

* * @default null * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get groupLabelFunction():Function { return _groupLabelFunction; } /** * @private */ public function set groupLabelFunction(value:Function):void { _groupLabelFunction = value; itemsSizeChanged = true; invalidateDisplayList(); dispatchEvent(new Event("groupLabelFunctionChanged")); } //---------------------------------- // groupRowHeight //---------------------------------- /** * @private * Storage for groupNodeHeight property. */ private var _groupRowHeight:Number = -1; [Inspectable(category="Data")] /** * The height of the grouped row, in pixels. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get groupRowHeight():Number { return _groupRowHeight; } /** * @private */ public function set groupRowHeight(value:Number):void { _groupRowHeight = value; invalidateSize(); invalidateDisplayList(); itemsSizeChanged = true; } //---------------------------------- // rendererProviders //---------------------------------- /** * @private * Storage for rendererProvider property. */ private var _rendererProviders:Array = []; /* of AdvancedDataGridRendererProvider */ /** * Array of AdvancedDataGridRendererProvider instances. * You can use several renderer providers to specify custom item renderers * used for particular data, at a particular depth of the navigation tree, * or with column spanning or row spanning. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get rendererProviders():Array { return _rendererProviders; } /** * @private */ public function set rendererProviders(value:Array):void { _rendererProviders = value; invalidateSize(); invalidateDisplayList(); itemsSizeChanged = true; rendererChanged = true; } //---------------------------------- // itemIcons //---------------------------------- [Inspectable(category="Data")] /** * An object that specifies the icons for the items. * Each entry in the object has a field name that is the item UID * and a value that is an object with the following format: *
     *  {iconID: Class, iconID2: Class}
     *  
* The iconID field value is the class of the icon for * a closed or leaf item and the iconID2 is the class * of the icon for an open item. * *

This property is intended to allow initialization of item icons. * Changes to this array after initialization are not detected * automatically. * Use the setItemIcon() method to change icons dynamically.

* * @see #setItemIcon() * @default undefined * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public var itemIcons:Object; //---------------------------------- // firstVisibleItem //---------------------------------- [Bindable("firstVisibleItemChanged")] /** * The data provider element that corresponds to the * item that is currently displayed in the top row of the AdvancedDataGrid control. * For example, based on how the branches have been opened, closed, and scrolled, * the top row might be the ninth item in the list of * currently viewable items, which represents * some great-grandchild of the root node. * Setting this property is analogous to setting the verticalScrollPosition of the List control. * *

If the item is not currently viewable (for example, because it * is under a nonexpanded item), setting this property has no effect.

* *

The default value is the first item in the AdvancedDataGrid control.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get firstVisibleItem():Object { return listItems[0][0].data; } /** * @private */ public function set firstVisibleItem(value:Object):void { var pos:int = getItemIndex(value); if (pos < 0) return; verticalScrollPosition = Math.min(maxVerticalScrollPosition, pos); dispatchEvent(new Event("firstVisibleItemChanged")); } //---------------------------------- // hierarchicalCollectionView //---------------------------------- /** * @private */ private var _hierarchicalCollectionView:IHierarchicalCollectionView = new HierarchicalCollectionView(); [Inspectable(category="Data")] /** * The IHierarchicalCollectionView instance used by the control. * *

The default value is an internal instance of the * HierarchicalCollectionView class.

* * @see mx.collections.IHierarchicalCollectionView * * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function set hierarchicalCollectionView(value:IHierarchicalCollectionView):void { _hierarchicalCollectionView = value; // set the dataProvider using the new IHierarchicalCollectionView dataProviderChanged = true; invalidateProperties(); } /** * @private */ public function get hierarchicalCollectionView():IHierarchicalCollectionView { return IHierarchicalCollectionView(_hierarchicalCollectionView); } //---------------------------------- // displayDisclosureIcon //---------------------------------- /** * @private * Indicates that disclosure icons will be shown or not. */ private var _displayDisclosureIcon:Boolean = true; [Inspectable(category="General")] /** * Controls the creation and visibility of disclosure icons * in the navigation tree. * If false, disclosure icons are not displayed. * * @default true * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get displayDisclosureIcon():Boolean { return _displayDisclosureIcon; } public function set displayDisclosureIcon(value:Boolean):void { if (value != _displayDisclosureIcon) { _displayDisclosureIcon = value; itemsSizeChanged = true; invalidateDisplayList(); } } //---------------------------------- // displayItemsExpanded //---------------------------------- /** * @private * Indicates that items will be shown expanded or not. */ private var _displayItemsExpanded:Boolean = false; [Inspectable(category="General", enumeration="true,false", defaultValue="false")] /** * If true, expand the navigation tree to show all items. * If a new branch is added, it will be shown expanded. * * @default false * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get displayItemsExpanded():Boolean { return _displayItemsExpanded; } public function set displayItemsExpanded(value:Boolean):void { if (value != _displayItemsExpanded) { _displayItemsExpanded = value; if (_displayItemsExpanded) { displayItemsExpandedChanged = true; invalidateProperties(); } } } //---------------------------------- // treeColumn //---------------------------------- /** * @private * Storage for the treeColumn property. */ private var _treeColumn:AdvancedDataGridColumn = null; [Inspectable(category="General")] /** * The column in which the tree is displayed. * Set this property to the value of the id property of an * AdvancedDataGridColumn instance, as the following example shows: * *
     *    <mx:AdvancedDataGrid 
     *       width="100%" height="100%"
     *       treeColumn="{rep}"> 
     *       <mx:dataProvider>
     *          <mx:HierarchicalData source="{dpHierarchy}" 
     *              childrenField="categories"/>
     *       </mx:dataProvider>
     *       <mx:columns>
     *          <mx:AdvancedDataGridColumn dataField="Region"/>
     *          <mx:AdvancedDataGridColumn id="rep" 
     *              dataField="Territory_Rep"
     *              headerText="Territory Rep"/>
     *          <mx:AdvancedDataGridColumn dataField="Actual"/>
     *          <mx:AdvancedDataGridColumn dataField="Estimate"/>
     *       </mx:columns>
     *    </mx:AdvancedDataGrid>
* * @default null * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get treeColumn():AdvancedDataGridColumn { if (_treeColumn) return _treeColumn; return null; } /** * @private */ public function set treeColumn(value:AdvancedDataGridColumn):void { if (value != _treeColumn) { _treeColumn = value; purgeItemRenderers(); freeItemRenderersTable = new Dictionary(false); itemsSizeChanged = true; invalidateDisplayList(); } } //---------------------------------- // treeColumnIndex //---------------------------------- /** * The tree column number. * * @default 0 * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function get treeColumnIndex():int { if (_treeColumn) return _treeColumn.colNum; return 0; } //---------------------------------- // movingColumnIndex //---------------------------------- private var _movingColumnIndex:int; /** * For Automation * returns the index of the moving column, in the orderedHeadersList array. * Essentialy the order in which column comes if traversed in a DFS manner * @private */ mx_internal function get movingColumnIndex():int { return _movingColumnIndex; } /** * Given the index, finds the corresponding column and set it as movingColumn * @private */ mx_internal function set movingColumnIndex(index:int):void { _movingColumnIndex = index; if (index >= 0 && orderedHeadersList && index < orderedHeadersList.length && orderedHeadersList[index]) movingColumn = orderedHeadersList[index].column; } //-------------------------------------------------------------------------- // // Overridden Methods // //-------------------------------------------------------------------------- /** * @private */ override protected function initializeAccessibility():void { if (AdvancedDataGrid.createAccessibilityImplementation != null) AdvancedDataGrid.createAccessibilityImplementation(this); } /** * @private */ override protected function makeRowsAndColumns(left:Number, top:Number, right:Number, bottom:Number, firstCol:int, firstRow:int, byCount:Boolean = false, rowsNeeded:uint = 0):Point { if (!visibleColumns || visibleColumns.length == 0) visibleCellRenderers = {}; return super.makeRowsAndColumns(left, top, right, bottom, firstCol, firstRow, byCount, rowsNeeded); } /** * @inheritDoc * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ override protected function finishKeySelection():void { if (isCellSelectionMode()) finishCellKeySelection(); else super.finishKeySelection(); } /** * Updates the set of selected items given that the item renderer provided * was clicked by the mouse and the keyboard modifiers are in the given * state. This method also updates the display of the item renderers based * on their updated selected state. * * @param item The item renderer that was clicked. * * @param shiftKey true if the Shift key was held down when * the mouse was clicked. * * @param ctrlKey true if the Control key was held down when * the mouse was clicked. * * @param transition true if the graphics for the selected * state should be faded in using an effect. * * @return true if the set of selected items changed. * Clicking an already selected item doesn't always change the set * of selected items. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ override protected function selectItem(item:IListItemRenderer, shiftKey:Boolean, ctrlKey:Boolean, transition:Boolean = true):Boolean { if (isCellSelectionMode() && !selectedHeaderInfo) return selectCellItem(item, shiftKey, ctrlKey, transition); return super.selectItem(item, shiftKey, ctrlKey, transition); } /** * @private */ override protected function drawVisibleItem(uid:String, selected:Boolean = false, highlighted:Boolean = false, caret:Boolean = false, transition:Boolean = false):void { if (isCellSelectionMode()) { if (visibleCellRenderers[uid]) { var n:int = _columns.length; for (var i:int = 0; i < n; i++) { var bSelectCell:Boolean = cellSelectionData && cellSelectionData[uid] && cellSelectionData[uid][i.toString()]; var bHighlightCell:Boolean = highlightUID == uid && highlightColumnIndex == i; var bCaretCell:Boolean = caretUID == uid && caretColumnIndex == i; drawCellItem(visibleCellRenderers[uid][i.toString()], bSelectCell, bHighlightCell, bCaretCell); } } } super.drawVisibleItem(uid, selected, highlighted, caret, transition); } /** * @private */ override protected function setVisibleDataItem(uid:String, item:IListItemRenderer):void { if (isCellSelectionMode()) { if (uid) { if (!visibleCellRenderers[uid]) visibleCellRenderers[uid] = {}; visibleCellRenderers[uid] [indexToColNum(currentColNum).toString()] = item; } } super.setVisibleDataItem(uid, item); } /** * @private * */ override protected function addRendererToContentArea(item:IListItemRenderer, column:AdvancedDataGridColumn):void { //Creating a header if(columnGrouping && item.data is AdvancedDataGridColumn) { var headerInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(item.data as AdvancedDataGridColumn); if(headerInfo && (headerInfo.columnSpan + headerInfo.actualColNum) <= lockedColumnCount) listContent.addChild(DisplayObject(item)); else listSubContent.addChild(DisplayObject(item)); } else { super.addRendererToContentArea(item, column); } } /** * @private * */ override protected function mouseEventToItemRenderer(event:MouseEvent):IListItemRenderer { if(!columnGrouping) { return super.mouseEventToItemRenderer(event); } else { var r:IListItemRenderer; if (event.target == highlightIndicator || event.target == listContent) { var pt:Point = new Point(event.stageX, event.stageY); pt = listContent.globalToLocal(pt); var ww:Number = 0; // For headerItems r = findHeaderRenderer(pt); } if (!r) r = super.mouseEventToItemRenderer(event); // To handle case when mouse is at the border of the header item // renderer. Ignoring prevents flickering of the highlight of the // header because of infinite loop of mouseOver and mouseOut events. if (event.target == listContent && isHeaderItemRenderer(r)) return null; return r == itemEditorInstance ? null : r; } } /** * @private * */ override protected function hasHeaderItemsCreated(index:int=-1):Boolean { if(!columnGrouping) return super.hasHeaderItemsCreated(index); if(index == -1) return (visibleHeaderInfos && visibleHeaderInfos[0].headerItem); var headerInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(getOptimumColumns()[index]); return (headerInfo && headerInfo.headerItem); } /** * @private * */ override protected function purgeHeaderRenderers():void { if(!columnGrouping) { super.purgeHeaderRenderers(); } else { if(headerItemsList && headerItemsList.length) { while (headerItemsList.length) { var item:IListItemRenderer = headerItemsList.pop(); addHeaderToFreeItemRenderers(item, item.data as AdvancedDataGridColumn); } } } } /** * @private */ override protected function createHeaders(left:Number, top:Number):void { if(!columnGrouping) { var creatingHeaders:Boolean = false; if(horizontalScrollPolicy != ScrollPolicy.OFF && getOptimumColumns()!= visibleColumns) { if(!headerItems[0] || !headerItems[0][0] || (top < headerItems[0][0].y + headerItems[0][0].height)) creatingHeaders = true; } super.createHeaders(left, top); if (creatingHeaders) { creatingHeaders = false; var residualWidth:Number = getLastColumnResidualWidth(); var lastColIndex:int = displayableColumns.length - 1; var lastCol:AdvancedDataGridColumn = displayableColumns[lastColIndex]; var w:Number = (isNaN(lastCol.explicitWidth) ? lastCol.preferredWidth : lastCol.explicitWidth); var item:IListItemRenderer = headerItems[0][lastColIndex]; if (item) { item.explicitWidth = w + residualWidth; UIComponentGlobals.layoutManager.validateClient(item, true); item.setActualSize(w + residualWidth, item.height); } } } else { if(visibleHeaderInfos && (!visibleHeaderInfos[0].headerItem || (top < visibleHeaderInfos[0].headerItem.y + visibleHeaderInfos[0].headerItem.height))) { //Creating new header items..need to purge old ones. purgeHeaderRenderers(); headerItemsList = []; lastColumnWidth = NaN; //Just create the headerItems!! no measuring, Nothing createHeaderItems(visibleHeaderInfos); headerRowInfo = []; //Calculate the heights of each header rows measureHeaderHeights(visibleHeaderInfos,0); //Set heights of all headerItems var visibleHeaderInfo:AdvancedDataGridHeaderInfo; var padding:int = cachedPaddingTop + cachedPaddingBottom; var currentItemHeight:Number=0; var n:int = orderedHeadersList.length; for (var i:int = 0; i < n; i++) { visibleHeaderInfo = orderedHeadersList[i]; if(visibleHeaderInfo.visible) { currentItemHeight = headerRowInfo[visibleHeaderInfo.depth].height - padding; if(visibleHeaderInfo.visibleChildren) currentItemHeight -= headerRowInfo[visibleHeaderInfo.depth+1].height; if(horizontalScrollPolicy != ScrollPolicy.OFF && !isNaN(lastColumnWidth) && visibleHeaderInfo.column == displayableColumns[displayableColumns.length-1]) visibleHeaderInfo.headerItem.setActualSize(lastColumnWidth, currentItemHeight); else visibleHeaderInfo.headerItem.setActualSize(visibleHeaderInfo.column.width, currentItemHeight); visibleHeaderInfo.headerItem.visible = headerVisible; } } lastColumnWidth = NaN; //Done with measuring, we know the dimensions for each header now. Just layout them at their proper place layoutHeaders(visibleHeaderInfos, left, top,0); currentItemTop = headerVisible ? headerRowInfo[0].height : 0; _headerHeight = !_explicitHeaderHeight ? headerRowInfo[0].height : _headerHeight; } } } /** * Returns the item renderer for a specific item in a column. * * @param c The AdvancedDataGridColumn instance for the column. * * @param itemData The item in the column. * * @return The IListItemRenderer defining the item renderer. * * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ override mx_internal function getRenderer(c:AdvancedDataGridColumn, itemData:Object, forDragProxy:Boolean = false):IListItemRenderer { var renderer:IListItemRenderer; var factory:IFactory; var adgDescription:AdvancedDataGridRendererDescription; if (rendererProviders.length != 0) adgDescription = getRendererDescription(itemData,c,forDragProxy); if (adgDescription && adgDescription.renderer) { if (freeItemRenderersTable[c] && freeItemRenderersTable[c][adgDescription.renderer] && freeItemRenderersTable[c][adgDescription.renderer].length) { renderer = freeItemRenderersTable[c][adgDescription.renderer].pop(); } else { renderer = adgDescription.renderer.newInstance(); itemRendererToFactoryMap[renderer] = adgDescription.renderer; } rendererDescriptionMap[renderer] = adgDescription; return renderer; } return super.getRenderer(c, itemData, forDragProxy); } /** * @private * This grid just returns the column size, * but could handle column spanning. */ override mx_internal function getWidthOfItem(item:IListItemRenderer, col:AdvancedDataGridColumn, visibleColumnIndex:int):Number { var ww:Number = 0; var adgDescription:AdvancedDataGridRendererDescription = rendererDescriptionMap[item] as AdvancedDataGridRendererDescription; if (adgDescription && adgDescription.renderer) { var i:int; var end:int; // set the width of the item according to the number of columns it is spaning if (adgDescription.columnSpan == 0) { end = _columns.length; if (visibleColumnIndex < lockedColumnCount) end = lockedColumnCount; var tmpWidth:Number = 0; for (i = visibleColumnIndex; i < end ;i++) { tmpWidth += AdvancedDataGridColumn(_columns[i]).width; } ww = tmpWidth; } else { ww = 0; end = visibleColumnIndex+adgDescription.columnSpan; if (visibleColumnIndex < lockedColumnCount && end > lockedColumnCount) end = lockedColumnCount; if (end > displayableColumns.length) end = displayableColumns.length; var optimumColumns:Array = getOptimumColumns(); for (i = visibleColumnIndex; i < end ; i++) { ww += super.getWidthOfItem(item,optimumColumns[i], visibleColumnIndex); } } var lastColIndex:int = displayableColumns.length - 1; if(horizontalScrollPolicy != ScrollPolicy.OFF && lastColIndex >= visibleColumnIndex && lastColIndex < end) { // Find the extra width we need to add.. var residualWidth:Number = getLastColumnResidualWidth(); var lastCol:AdvancedDataGridColumn = displayableColumns[lastColIndex]; // subtract the width which got added above in the loop ww -= lastCol.width; // Add the corrected width ww += (isNaN(lastCol.explicitWidth) ? lastCol.preferredWidth : lastCol.explicitWidth) + getLastColumnResidualWidth(); } return ww ; } else if(horizontalScrollPolicy != ScrollPolicy.OFF && col == displayableColumns[displayableColumns.length-1]) { return (isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth) + getLastColumnResidualWidth(); } else { return super.getWidthOfItem(item, col, visibleColumnIndex); } } /** * @private * Creates the header seperators taking into * account the different depths of the two headers * on the two sides of the seperator */ override protected function createHeaderSeparators(n:int, separators:Array, headerLines:UIComponent):void { if(!columnGrouping) { super.createHeaderSeparators(n, separators, headerLines); } else { var separatorAffordance:Number = 3; var optimumColumns:Array = getOptimumColumns(); var offset:int = (lockedColumnCount > 0 && separators != lockedSeparators) ? lockedColumnCount - 1 : 0; for (var i:int = 0; i < n; i++) { var headerItemIndex:int = i + offset; var sep:UIComponent = getSeparator(i, separators, headerLines); var sepSkin:IFlexDisplayObject = IFlexDisplayObject(sep.getChildAt(0)); var leftHeaderInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(optimumColumns[headerItemIndex]); var leftHeaderItem:IListItemRenderer = leftHeaderInfo.headerItem; if(!leftHeaderInfo || !leftHeaderInfo.visible || !(leftHeaderItem )) { sep.visible = false; continue; } sep.visible = true; var posX:Number; posX = leftHeaderItem.x + optimumColumns[headerItemIndex].width - Math.round(sep.measuredWidth / 2 + 0.5); if (i > 0) { posX = Math.max(posX, separators[i - 1].x + Math.round(sep.measuredWidth / 2 + 0.5)); } sep.x = posX; //Column on the other side of the seperator var rightHeaderInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(optimumColumns[headerItemIndex+1]); var lineHeight:Number; //Based on what is on left and right of the seperator //We need to find out the height of the separator var leftParent:AdvancedDataGridHeaderInfo = leftHeaderInfo; var rightParent:AdvancedDataGridHeaderInfo = rightHeaderInfo; var padding:int = cachedPaddingBottom+cachedPaddingTop; while(leftParent!=rightParent && leftParent.parent!=null && rightParent.parent!=null) { if(leftParent.depth > rightParent.depth) { leftParent = leftParent.parent; } else if(leftParent.depth < rightParent.depth) { rightParent = rightParent.parent; } else { leftParent = leftParent.parent; rightParent = rightParent.parent; } } sep.y = 0; lineHeight = leftHeaderItem.y + leftHeaderItem.height + padding; //Two columns have the same ancestor, may not be parent!! if(leftParent == rightParent && leftParent) { sep.y = leftParent.headerItem.y + leftParent.headerItem.height + cachedPaddingBottom; lineHeight = leftHeaderItem.y + leftHeaderItem.height - leftParent.headerItem.y - leftParent.headerItem.height; } sepSkin.setActualSize(sepSkin.measuredWidth,lineHeight); // Draw invisible background for separator affordance sep.graphics.clear(); sep.graphics.beginFill(0xFFFFFF, 0); sep.graphics.drawRect(-separatorAffordance, 0, sepSkin.measuredWidth + separatorAffordance , lineHeight); sep.graphics.endFill(); } } } /** * Setup Renderers * Make renderer and populate listItems with them. * @private */ override protected function setupRenderer(itemData:Object,uid:String,insertItems:Boolean = false):void { var savedColNum:int = currentColNum; super.setupRenderer(itemData, uid, insertItems); if (rendererProviders.length == 0) return; var itemCount:int = listItems[currentRowNum].length; // count of the items which will be invisible due to the previous item's column span var count:int = 0; var optimumColumns:Array = getOptimumColumns(); for(var i:int = 0; i < itemCount; ++i) { var item:IListItemRenderer = listItems[currentRowNum][i]; var adgDescription:AdvancedDataGridRendererDescription = rendererDescriptionMap[item]; if (adgDescription && adgDescription.renderer) { // set count to the max of the column span of the previous renderer and the present renderer if (adgDescription.columnSpan == 0) count = Math.max(count-1, optimumColumns.length - i); else count = Math.max(count-1, adgDescription.columnSpan - 1); } else if (count > 0) { // make the item invisible if there is already an item that is spaning onto it item.visible = false; count--; } } } /** * @private */ override protected function updateDisplayOfItemRenderer(r:IListItemRenderer):void { applyUserStylesForItemRenderer(r); super.updateDisplayOfItemRenderer(r); } /** * Draws a vertical line between columns. * This implementation draws a line * directly into the given Sprite. The Sprite has been cleared * before lines are drawn into it. * * @param s A Sprite that contains a DisplayObject * that contains the graphics for that row. * * @param columnIndex The column's index in the set of displayed columns. * The left most visible column has a column index of 0. * * @param color The color for the indicator. * * @param x The x position for the background * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ override protected function drawVerticalLine(s:Sprite, colIndex:int, color:uint, x:Number):void { //draw our vertical lines var g:Graphics = s.graphics; if (lockedColumnCount > 0 && colIndex == lockedColumnCount - 1) g.lineStyle(1, 0, 100); else g.lineStyle(1, color, 100); var tempY:Number = 0; if(headerVisible) { tempY = headerRowInfo[0].height; } // check if custom renderers are used if (rendererProviders.length != 0) { var item:IListItemRenderer; var n:int = listItems.length; for (var i:int = 0; i < n; i++) { // if listItem has any item, update tempY if (listItems[i].length != 0) { item = listItems[i][colIndex] as IListItemRenderer; tempY = rowInfo[i].y + rowInfo[i].height; // check for item and its width if (item.visible) { if ((item.x +item.width) <= x) { g.moveTo(x, rowInfo[i].y); g.lineTo(x, tempY); } } else { // check for the item renderers in the previous columns in the same row for (var j:int= colIndex - 1; j >= 0; j--) { item = listItems[i][j] as IListItemRenderer; if (item.visible) { if ((item.x +item.width) <= x) { g.moveTo(x, rowInfo[i].y); g.lineTo(x, tempY); break; } else { break; } } } } } } } // draw line from tempY to listContent's height g.moveTo(x, tempY); g.lineTo(x, listContent.height); } /** * Get the appropriate renderer factory for a column, * using the default renderer if none specified * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ override mx_internal function columnItemRendererFactory(c:AdvancedDataGridColumn, forHeader:Boolean, itemData:Object):IFactory { var factory:IFactory; if (!forHeader) { if (_rootModel is IHierarchicalData && c.colNum == treeColumnIndex) { if (!isBranch(itemData) && c.itemRenderer) { factory = c.itemRenderer; } else if (groupItemRenderer) { factory = groupItemRenderer; } } } if (!factory) { factory = super.columnItemRendererFactory(c, forHeader, itemData); } return factory; } /** * @private */ override protected function getNumColumns():int { if(columnGrouping) { var optimumColumns:Array = getOptimumColumns(); if(!optimumColumns) return 0; return optimumColumns.length; } else { return super.getNumColumns(); } } /** * @private */ override protected function getPossibleDropPositions(c:AdvancedDataGridColumn):Array { if(columnGrouping && c!=null) { var headerInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(c); if(headerInfo && headerInfo.parent && headerInfo.parent.visibleChildren && headerInfo.parent.visibleChildren.length > 0) return headerInfo.parent.visibleChildren; else return visibleHeaderInfos; } else { return super.getPossibleDropPositions(c); } } /** * @private */ override mx_internal function getHeaderInfo(col:AdvancedDataGridColumn):AdvancedDataGridHeaderInfo { if(columnGrouping) return columnsToInfo[itemToUID(col)]; else return super.getHeaderInfo(col); } /** * @private */ override mx_internal function getHeaderInfoAt(colIndex:int):AdvancedDataGridHeaderInfo { if(columnGrouping) return columnsToInfo[itemToUID(columns[colIndex])]; else return super.getHeaderInfoAt(colIndex); } /** * @private */ override protected function createChildren():void { super.createChildren(); // This invisible layer, which is a child of listSubContent // catches mouse events for all items // and is where we put selection highlighting by default. if (!movingSelectionLayer) { movingSelectionLayer = new FlexSprite(); movingSelectionLayer.name = "movingSelectionLayer"; movingSelectionLayer.mouseEnabled = false; listSubContent.addChild(movingSelectionLayer); // trace("movingSelectionLayer parent set to " + movingSelectionLayer.parent); var g:Graphics = movingSelectionLayer.graphics; g.beginFill(0, 0); // 0 alpha means transparent g.drawRect(0, 0, 10, 10); g.endFill(); } } /** * @private */ override protected function commitProperties():void { if (dataProviderChanged) { var tmpCollection:Object; //reset flags dataProviderChanged = false; if (hierarchicalCollectionInstance) { super.dataProvider = hierarchicalCollectionInstance; } // at this point _rootModel may be null so we dont need to continue else if (_rootModel is IHierarchicalData) { //wrap userdata in a HierarchicalCollection and pass that collection to the List hierarchicalCollectionView.source = _rootModel; super.dataProvider = hierarchicalCollectionView; } } // if the data is grouped, get the groupLabelField. if (_rootModel is IGroupingCollection && IGroupingCollection(_rootModel).grouping) groupLabelField = IGroupingCollection(_rootModel).grouping.label; if (_rootModel is IGroupingCollection2 && IGroupingCollection2(_rootModel).grouping) groupLabelField = IGroupingCollection2(_rootModel).grouping.label; if (groupedColumnsChanged || columnsChanged) { anchorColumnIndex = -1; highlightColumnIndex = -1; selectedColumnIndex = -1; } // This code should always come after the hierarchicalData code above // because if no columns array has been defined, columns are generated // based on the dataProvider. Thus setting of columns by dataProvider // falls in the same cycle of commitProperties and if moved above will // not get picked unless a second call to commitProperties come if(groupedColumnsChanged) { columnGrouping = true; columnsChanged = false; removeOldHeaders(); columnsToInfo = new Dictionary(); headerInfos = initializeHeaderInfo(_groupedColumns); headerInfoInitialized = true; // In case a new set of groupedColumns have been specified // its possible the effective value of lockedColumnCount // changes if(lockedColumnCount > 0) lockedColumnCountChanged = true; super.columns = getLeafColumns(_groupedColumns.slice(0)); } else if(columnsChanged) { columnGrouping = false; columnsChanged = false; removeOldHeaders(); super.columns = _columnsValue; } if (displayItemsExpandedChanged) { displayItemsExpandedChanged = false; // if displayItemsExpanded is set to true, then expand all the items. if (displayItemsExpanded) expandAll(); } super.commitProperties(); // lockedColumnCount set code should always come after groupedColumns set code // and super.commitProperties because in case of column grouping the actual value // of lockedColumnCount is dependent on groupedColumns and visibleHeaderInfos // So if both are set in the same cycle then lockedColumnCount may miss the // actual value if(lockedColumnCountChanged) { if(columnGrouping) super.lockedColumnCount = getAdjustedLockedCount(_lockedColumnCountVal); else super.lockedColumnCount = _lockedColumnCountVal; } if(groupedColumnsChanged) { groupedColumnsChanged = false; headerInfoInitialized = false; } if (selectedCellsChanged) { setSelectedCells(_temporary_selectedCells); selectedCellsChanged = false; _temporary_selectedCells = null; } // If column becomes hidden, invalidate anchor, highlight and selected indices. { if (anchorColumnIndex != -1 && columns[anchorColumnIndex].visible == false) anchorColumnIndex = -1; if (highlightColumnIndex != -1 && columns[highlightColumnIndex].visible == false) highlightColumnIndex = -1; if (selectedColumnIndex != -1 && columns[selectedColumnIndex].visible == false) selectedColumnIndex = -1; } } /** * @private */ override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { // Kill any animation before resizing; // tween is null if there is no Tween underway. if (tween) tween.endTween(); // Display cells set in selectionMode which are not yet visible if (cellsWaitingToBeDisplayed) processCellsWaitingToBeDisplayed(); super.updateDisplayList(unscaledWidth, unscaledHeight); if (headerVisible && columnGrouping) drawHeaderHorizontalSeparators(); else clearHeaderHorizontalSeparators(); //update collection length if (collection) collectionLength = collection.length; // Keyboard access to header if (columnGrouping && selectedHeaderInfo) { selectColumnGroupHeader(selectedHeaderInfo); } else if(!columnGrouping && headerIndex != -1) { selectedHeaderInfo = getHeaderInfo(columns[headerIndex]); selectColumnHeader(headerIndex); } } /** * @private */ override public function calculateDropIndex(event:DragEvent = null):int { if (event) { if (_rootModel is IHierarchicalData) { if (event) updateDropData(event); return _dropData.rowIndex; } } return super.calculateDropIndex(event); } /** * @private */ override protected function drawRowBackgrounds():void { var colors:Array; colors = getStyle("depthColors"); if (!(_rootModel is IHierarchicalData) || !colors) { super.drawRowBackgrounds(); return; } var rowBGs:Sprite = Sprite(listContent.getChildByName("rowBGs")); if (!rowBGs) { rowBGs = new FlexSprite(); rowBGs.mouseEnabled = false; rowBGs.name = "rowBGs"; listContent.addChildAt(rowBGs, 0); } var color:*; var depthColors:Boolean = false; var n:int = listItems.length; if (colors) depthColors = true; else { var colorsStyle:Object = getStyle("alternatingItemColors"); if (colorsStyle) colors = (colorsStyle is Array) ? (colorsStyle as Array) : [colorsStyle]; } color = getStyle("backgroundColor"); if (color === undefined) color = 0xFFFFFF; if (!colors || colors.length == 0) { while (rowBGs.numChildren > n) { rowBGs.removeChildAt(rowBGs.numChildren - 1); } return; } styleManager.getColorNames(colors); var curRow:int = 0; var i:int = 0; var actualRow:int = verticalScrollPosition; while (curRow < n) { if (depthColors) { try { if (listItems[curRow][0]) { var d:int = getItemDepth(listItems[curRow][0].data, curRow); drawRowBackground(rowBGs, i++, rowInfo[curRow].y, rowInfo[curRow].height, colors[d - 1], curRow); } else { drawRowBackground(rowBGs, i++, rowInfo[curRow].y, rowInfo[curRow].height, uint(color), curRow); } } catch (e:Error) { //trace("[Tree] caught exception in drawRowBackground"); } } else { drawRowBackground(rowBGs, i++, rowInfo[curRow].y, rowInfo[curRow].height, colors[actualRow % colors.length], actualRow); } curRow++; actualRow++; } while (rowBGs.numChildren > i) { rowBGs.removeChildAt(rowBGs.numChildren - 1); } } /** * @private */ override protected function isDraggingAllowed(draggedColumn:AdvancedDataGridColumn):Boolean { if (!draggedColumn.draggable) return false; if(columnGrouping) { var parentInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(draggedColumn).parent; //Parent doesn't allow its children to be draggable if(parentInfo && (parentInfo.column as AdvancedDataGridColumnGroup).childrenDragEnabled == false) return false; } return true; } /** * @private */ override protected function columnDraggingMouseUpHandler(event:Event):void { if(!columnGrouping) { super.columnDraggingMouseUpHandler(event); return; } if (!movingColumn) return; var headerInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(movingColumn); var origIndex:int = headerInfo.index;//movingColumn.colNum; var dropPositions:Array = getPossibleDropPositions(movingColumn); var n:int = dropPositions.length; if (dropColumnIndex >= 0) { if (dropColumnIndex >= n) dropColumnIndex = n - 1; else if (origIndex < dropColumnIndex) dropColumnIndex--; //dropColumnIndex is actually the index into the group w.r.t to the displayableColumns, //We need to get the corresponding actual index dropColumnIndex = dropPositions[dropColumnIndex].index; } // Shift columns. shiftColumns(origIndex, dropColumnIndex, event); unsetColumnDragParameters(); } /** * @private */ override protected function dragEnterHandler(event:DragEvent):void { // Drag-and-drop not supported for cells if (isCellSelectionMode()) return; if (!(_rootModel is IHierarchicalData) && (event.dragSource.hasFormat("items") || event.dragSource.hasFormat("itemsByIndex"))) { super.dragEnterHandler(event); return; } if (event.isDefaultPrevented()) return; lastDragEvent = event; if ((!_rootModel || _rootModel is IHierarchicalData) && event.dragSource.hasFormat("treeDataGridItems")) { DragManager.acceptDragDrop(this); DragManager.showFeedback(event.ctrlKey ? DragManager.COPY : DragManager.MOVE); showDropFeedback(event); return; } hideDropFeedback(event); DragManager.showFeedback(DragManager.NONE); } /** * @private */ override protected function dragOverHandler(event:DragEvent):void { // Drag-and-drop not supported for cells if (isCellSelectionMode()) return; if (!(_rootModel is IHierarchicalData) && (event.dragSource.hasFormat("items") || event.dragSource.hasFormat("itemsByIndex"))) { super.dragOverHandler(event); return; } if (event.isDefaultPrevented()) return; lastDragEvent = event; if ((!_rootModel || _rootModel is IHierarchicalData) && event.dragSource.hasFormat("treeDataGridItems")) { DragManager.showFeedback(event.ctrlKey ? DragManager.COPY : DragManager.MOVE); showDropFeedback(event); return; } hideDropFeedback(event); DragManager.showFeedback(DragManager.NONE); } /** * Handler for the DragEvent.DRAG_DROP event. * This method hides * the drop feedback by calling the hideDropFeedback() method. * * By default, only the DragManager.MOVE drag action is supported. * To support the DragManager.COPY * drag action, you must write an event handler for the * DragEvent.DRAG_DROP event that * implements the copy of the AdvancedDataGrid data based on its structure. * * @param event The DragEvent object. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ override protected function dragDropHandler(event:DragEvent):void { // Drag-and-drop not supported for cells if (isCellSelectionMode()) return; if (!(_rootModel is IHierarchicalData) && (event.dragSource.hasFormat("items") || event.dragSource.hasFormat("itemsByIndex"))) { super.dragDropHandler(event); return; } if (event.isDefaultPrevented()) return; hideDropFeedback(event); if ((!_rootModel || _rootModel is IHierarchicalData) && event.dragSource.hasFormat("treeDataGridItems")) { //we only support MOVE by default if (event.action == DragManager.MOVE && dragMoveEnabled) { var items:Array = event.dragSource.dataForFormat("treeDataGridItems") as Array; //Are we dropping on ourselves? if (event.dragInitiator == this) { // If we're dropping onto ourselves or a child of a descendant then dont actually drop calculateDropIndex(event); // If we did start this drag op then we need to remove first var index:int; var parent:*; var parentItem:*; //get ancestors of the drop target item var dropParentStack:Array = getParentStack(_dropData.parent); dropParentStack.unshift(_dropData.parent); //optimize stack method var n:int = items.length; for (var i:int = 0; i < n; i++) { parent = getParentItem(items[i]); index = getChildIndexInParent(parent, items[i]); //check ancestors of the dropTarget if the item matches, we're invalid for each (parentItem in dropParentStack) { //we dont want to drop into one of our own sets of children if (items[i] == parentItem) return; } //we remove before we add due to the behavior //of structures with parent pointers like e4x removeChildItem(parent, items[i], index); //is the removed item before the drop location? if (parent == _dropData.parent && index < _dropData.index) { addChildItem(_dropData.parent, items[i], (_dropData.index - i - 1)); } else { addChildItem(_dropData.parent, items[i], _dropData.index); } } } } } lastDragEvent = null; } /** * Handler for the DragEvent.DRAG_COMPLETE event. * * By default, only the DragManager.MOVE drag action is supported. * To support the DragManager.COPY * drag action, you must write an event handler for the * DragEvent.DRAG_DROP event that * implements the copy of the AdvancedDataGrid data based on its structure. * * @param event The DragEvent object. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ override protected function dragCompleteHandler(event:DragEvent):void { // Drag-and-drop not supported for cells if (isCellSelectionMode()) return; if (!(_rootModel is IHierarchicalData) && (event.dragSource.hasFormat("items") || event.dragSource.hasFormat("itemsByIndex"))) { super.dragCompleteHandler(event); return; } isPressed = false; if (event.isDefaultPrevented()) return; resetDragScrolling(); if ((!_rootModel || _rootModel is IHierarchicalData) && event.dragSource.hasFormat("treeDataGridItems")) { var items:Array; //we only support MOVE by default if (event.action == DragManager.MOVE && dragMoveEnabled) { if (event.relatedObject != this) { //if we dropped on another component //then we need to remove from ourself first items = event.dragSource.dataForFormat("treeDataGridItems") as Array; var parent:*; var index:int; //do the remove var n:int = items.length; for (var i:int=0; i < n; i++) { parent = getParentItem(items[i]); index = getChildIndexInParent(parent, items[i]); removeChildItem(parent, items[i], index); } addItemsToTarget(event, items); } clearSelected(false); } else if (event.action == DragManager.COPY) { if (event.relatedObject != this) { items = event.dragSource.dataForFormat("treeDataGridItems") as Array; addItemsToTarget(event, items); } clearSelected(false); } } lastDragEvent = null; } /** * @private * Initialized the headerInfos structure w.r.t to the groupedColumns * configuration */ override protected function initializeHeaderInfo(groupedColumns:Array):Array { if(!columnGrouping) return super.initializeHeaderInfo(groupedColumns); else return initializeGroupedHeaderInfo(groupedColumns, null,0,null); } /** * @private */ override protected function createDisplayableColumns():void { if(!columnGrouping) { super.createDisplayableColumns(); } else { var i:int; var n:int; displayableColumns = []; n = _columns.length; for (i = 0; i < n; i++) { if(getHeaderInfo(_columns[i]).visible) displayableColumns.push(_columns[i]); } } } /** * @private */ override protected function updateVisibleHeaders():Array { if(!columnGrouping) return super.updateVisibleHeaders(); else return updateVisibleHeaderInfos(headerInfos).infos; } /** * @private */ override protected function updateHeaderSearchList():void { orderedHeadersList = []; updateHeadersList(visibleHeaderInfos); } /** * @private */ override protected function calculateHeaderHeight():Number { if (!columnGrouping) return super.calculateHeaderHeight(); // first measure the header isMeasuringHeader = true; createHeaders(0, 0); isMeasuringHeader = false; return headerHeight; } /** * @private * Position indicator bar that shows where an item will be placed in the list. */ override public function showDropFeedback(event:DragEvent):void { if (!(_rootModel is IHierarchicalData)) { super.showDropFeedback(event); return; } super.showDropFeedback(event); // Adjust for indent var vm:EdgeMetrics = viewMetrics; var offset:int = 0; updateDropData(event); var indent:int = 0; var depth:int; if (_dropData.parent) { offset = getItemIndex(iterator.current); depth = getItemDepth(_dropData.parent, Math.abs(offset - getItemIndex(_dropData.parent))); indent = (depth + 1) * getStyle("indentation"); } else { indent = getStyle("indentation"); } if (indent < 0) indent = 0; //position drop indicator dropIndicator.width = listContent.width - indent; dropIndicator.x = indent + vm.left + 2; if (_dropData.emptyFolder) { dropIndicator.y += _dropData.rowHeight / 2; } } /** * @private */ override protected function addDragData(ds:Object):void // actually a DragSource { if (_rootModel is IHierarchicalData) ds.addHandler(collapseSelectedItems, "treeDataGridItems"); else super.addDragData(ds); } /** * @private * Set the itemEditor instance position according to the indentation of the item it is representing. */ override protected function layoutItemEditor():void { if (_rootModel is IHierarchicalData) { var indent:int = rowMap[editedItemRenderer.name].indent; itemEditorInstance.move(itemEditorInstance.x + indent, itemEditorInstance.y); itemEditorInstance.setActualSize(itemEditorInstance.width - indent, itemEditorInstance.height); } } /** * @private */ override protected function selectColumnHeader(columnNumber:int):void { if(!columnGrouping || selectedHeaderInfo == null) super.selectColumnHeader(columnNumber); else selectColumnGroupHeader(selectedHeaderInfo); } /** * @private */ override public function createItemEditor(colIndex:int, rowIndex:int):void { // TODO - Check for the item renderer instead of the column number if (_rootModel is IHierarchicalData && colIndex == treeColumnIndex) { var col:AdvancedDataGridColumn = displayableColumns[colIndex]; if (col.editorXOffset == 0 || col.editorWidthOffset == 0) { col.editorXOffset = 12; col.editorWidthOffset = -12; } } super.createItemEditor(colIndex, rowIndex); } /** * @private */ override protected function adjustAfterRemove(items:Array, location:int, emitEvent:Boolean):Boolean { var requiresValueCommit:Boolean = emitEvent; if (isCellSelectionMode()) { var n:int = items.length; for (var i:int = 0; i < n; i++) { var uid:String = UIDUtil.getUID(items[i]); if (cellSelectionData[uid]) { for (var p:String in cellSelectionData[uid]) { removeCellIndicators(uid, int(p)); removeCellSelectionData(uid, int(p)); requiresValueCommit = true; } } } } else // if (isRowSelectionMode()) { var indicesLength:int = selectedItems.length; var length:int = items.length; if (_selectedIndex > location) { _selectedIndex -= length; requiresValueCommit = true; } if (bSelectedItemRemoved && indicesLength < 1) { _selectedIndex = getItemIndex(expandedItem); requiresValueCommit = true; bSelectionChanged = true; bSelectedIndexChanged = true; invalidateDisplayList(); } } return requiresValueCommit; } /** * @private */ override protected function makeListData(data:Object, uid:String, rowNum:int, columnNum:int, column:AdvancedDataGridColumn):BaseListData { var label:String ; var advancedDataGridListData:AdvancedDataGridListData; if(data is AdvancedDataGridColumnGroup) { return new AdvancedDataGridListData((column.headerText != null) ? column.headerText : "", data.dataField, -1, uid, this, rowNum); } else if (!(data is AdvancedDataGridColumn)) { if (groupItemRenderer && isBranch(data) && column.colNum == treeColumnIndex) { // Checking for a groupLabelFunction or a groupLabelField property to be present if (groupLabelFunction != null) label = groupLabelFunction(data, column); else if (groupLabelField != null && data.hasOwnProperty(groupLabelField)) label = data[groupLabelField]; if (label) advancedDataGridListData = new AdvancedDataGridListData(label, column.dataField, columnNum, uid, this, rowNum); } } if (!advancedDataGridListData) advancedDataGridListData = super.makeListData(data, uid, rowNum,columnNum,column) as AdvancedDataGridListData; if (iterator && iterator is HierarchicalCollectionViewCursor && columnNum == treeColumnIndex && !(data is AdvancedDataGridColumn)) initListData(data, advancedDataGridListData); else //item needs to be set, otherwise it will flicker the display when a node is expanded or collapsed advancedDataGridListData.item = data; return advancedDataGridListData; } /** * @private */ override public function itemToIcon(item:Object):Class { if (item == null) { return null; } var icon:*; var open:Boolean = isItemOpen(item); var branch:Boolean = isBranch(item); var uid:String = itemToUID(item); //first lets check the component var iconClass:Class = itemIcons && itemIcons[uid] ? itemIcons[uid][open ? "iconID2" : "iconID"] : null; if (iconClass) { return iconClass; } else if (iconFunction != null) { return iconFunction(item); } else if (branch) { return getStyle(open ? "folderOpenIcon" : "folderClosedIcon"); } else //let's check the item itself { if (item is XML) { try { if (item[iconField].length() != 0) icon = String(item[iconField]); } catch(e:Error) { } } else if (item is Object) { try { if (iconField && item[iconField]) icon = item[iconField]; else if (item.icon) icon = item.icon; } catch (e:Error) { } } } //set default leaf icon if nothing else was found if (icon == null) icon = getStyle("defaultLeafIcon"); //convert to the correct type and class if (icon is Class) { return icon; } else if (icon is String) { iconClass = Class(systemManager.getDefinitionByName(String(icon))); if (iconClass) return iconClass; return document[icon]; } else { return Class(icon); } } /** * Checks to see if item is visible in the list * @private */ override public function isItemVisible(item:Object):Boolean { //first check visible data if (visibleData[itemToUID(item)]) return true; //then check parent items var parentItem:Object = getParentItem(item); if (parentItem) { var uid:String = itemToUID(parentItem); if (visibleData[uid] && IHierarchicalCollectionView(collection).openNodes[uid]) { return true; } } return false; } /** * @private */ override protected function isDataEditable(data:Object):Boolean { if (_rootModel && (_rootModel is IGroupingCollection || _rootModel is IGroupingCollection2)) { // group header if (_rootModel.hasChildren(data)) return editable.indexOf("group") != -1; // summary data if (data is SummaryObject) return editable.indexOf("summary") != -1; } // any item renderer return editable.indexOf("item") != -1; } /** * @private * * see ListBase.as */ override mx_internal function addClipMask(layoutChanged:Boolean):void { var vm:EdgeMetrics = viewMetrics; var bHScrollBar:Boolean if (horizontalScrollBar && horizontalScrollBar.visible) vm.bottom -= horizontalScrollBar.minHeight; if (verticalScrollBar && verticalScrollBar.visible) vm.right -= verticalScrollBar.minWidth; if(verticalScrollBar && verticalScrollBar.visible && horizontalScrollBar && horizontalScrollBar.visible) vm.right += verticalScrollBar.minWidth; listContent.scrollRect = new Rectangle( 0, 0, unscaledWidth - vm.left - vm.right, listContent.height); } override mx_internal function shiftColumns(oldIndex:int, newIndex:int, trigger:Event = null):void { if (newIndex >= 0 && oldIndex != newIndex) { if(!columnGrouping) { super.shiftColumns(oldIndex, newIndex, trigger); } else { var headerInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(movingColumn); //needed for automation, which uses movingColumnIndex updateMovingColumnIndex(); var groupInfos:Array = headerInfo.parent ? headerInfo.parent.children : headerInfos; var groupColumns:Array = headerInfo.parent == null ? groupedColumns : (headerInfo.parent.column as AdvancedDataGridColumnGroup).children; var k:int; var queue:Array = []; var pointer:int = 0; var i:int; var increment:int; if (newIndex >= 0 && oldIndex != newIndex) { var incr:int = oldIndex < newIndex ? 1 : -1; for (i = oldIndex; i != newIndex; i += incr) { var j:int = i + incr; var c:AdvancedDataGridColumn = groupColumns[i]; groupColumns[i] = groupColumns[j]; groupColumns[j] = c; groupInfos[i].actualColNum += groupInfos[j].columnSpan * incr; groupInfos[j].actualColNum -= groupInfos[i].columnSpan * incr; //If the columns being dragged or shifted is a Column Group then actualColNum //need to be modified for all the descendants of the column group. if (groupInfos[i].children && groupInfos[i].children.length > 0) { increment = groupInfos[j].columnSpan * incr; for( k = 0; k < groupInfos[i].children.length; k++) { queue.push(groupInfos[i].children[k]); } while (queue[pointer]) { headerInfo = queue[pointer++]; headerInfo.actualColNum += increment; if(headerInfo && headerInfo.children) { for( k = 0; k < headerInfo.children.length; k++) { queue.push(headerInfo.children[k]); } } } } if (groupInfos[j].children && groupInfos[j].children.length > 0) { queue = []; pointer = 0; increment = groupInfos[i].columnSpan * incr; for( k = 0; k < groupInfos[j].children.length; k++) { queue.push(groupInfos[j].children[k]); } while (queue[pointer]) { headerInfo = queue[pointer++] headerInfo.actualColNum -= increment; if(headerInfo && headerInfo.children) for( k = 0; k < headerInfo.children.length; k++) queue.push(headerInfo.children[k]); } } var cInfo:AdvancedDataGridHeaderInfo = groupInfos[i]; groupInfos[i] = groupInfos[j]; groupInfos[j] = cInfo; groupInfos[i].index -=incr; groupInfos[j].index += incr; } var newColumns:Array = getLeafColumns(groupedColumns); _columns = []; var n:int = newColumns.length; for (i = 0; i < n; i++) { _columns[i] = newColumns[i]; _columns[i].colNum = i; } visibleHeaderInfos = updateVisibleHeaders(); updateHeaderSearchList(); createDisplayableColumns(); //In case of column grouping a shifting of columns may lead to //change in the effective layout owing to a bigger or //smaller group shifted in from locked area to unlocked area //or vice versa var newLockedColCount:int = getAdjustedLockedCount(_lockedColumnCountVal); if (lockedColumnCount != newLockedColCount) { lockedColumnCountChanged = true; super.lockedColumnCount = newLockedColCount; } columnsInvalid = true; itemsSizeChanged = true; invalidateDisplayList(); var icEvent:IndexChangedEvent = new IndexChangedEvent(IndexChangedEvent.HEADER_SHIFT); icEvent.oldIndex = oldIndex; icEvent.newIndex = newIndex; icEvent.triggerEvent = trigger; dispatchEvent(icEvent); } } if (isCellSelectionMode()) clearSelectedCells(); } } /** * @private * Fetch sort information for the given column. */ override public function getFieldSortInfo(column:AdvancedDataGridColumn):SortInfo { if (!sortExpertMode && visualSortInfo[column.colNum]) return visualSortInfo[column.colNum]; var headerInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(column); if (columnGrouping && column && collection && collection.sort && headerInfo.internalLabelFunction != null) { var n:int = collection.sort.fields.length; for (var i:int = 0; i < n; i++) { var uid:String = itemToUID(column); if( uid && collection.sort.fields[i].name == uid) return new SortInfo(i + 1, collection.sort.fields[i].descending); } } else { return super.getFieldSortInfo(column); } return null; } /** * @private */ override protected function unselectColumnHeader(columnNumber:int, completely:Boolean=false):void { var s:Sprite = Sprite( selectionLayer.getChildByName("headerKeyboardSelection") ); //If not found, then see if there is a child in the movingSelectionLayer if(!s) s = Sprite(movingSelectionLayer.getChildByName("headerKeyboardSelection")); if (s) s.parent.removeChild(s); selectedHeaderInfo = null; if (completely) { caretIndex = 0; isPressed = false; if (isCellSelectionMode()) { caretColumnIndex = columnNumber; selectItem(listItems[caretIndex][absoluteToVisibleColumnIndex(columnNumber)], false, false); } else { selectItem(listItems[caretIndex][0], false, false); } } } /** * @private */ override public function styleChanged(styleProp:String):void { super.styleChanged(styleProp); if (styleProp == "sortFontFamily" || styleProp == "sortFontSize" || styleProp == "sortFontStyle" || styleProp == "sortFontWeight") { itemsSizeChanged = true; rendererChanged = true; invalidateProperties(); invalidateDisplayList(); } } /** * @private */ override protected function removeIndicators(uid:String):void { if (isCellSelectionMode()) { for (var p:String in cellSelectionIndicators[uid]) { removeCellIndicators(uid, int(p)); } for (p in visibleCellRenderers[uid]) { delete visibleCellRenderers[uid][p]; } delete visibleCellRenderers[uid]; } super.removeIndicators(uid); } /** * Moves the cell and row selection indicators up or down by the given offset * as the control scrolls its display. * This assumes all the selection indicators in this row are at * the same y position. * * @param uid The UID of the row. * * @param The scroll offset. * * @param absolute true if offset contains the new scroll position, * and false if it contains a value relative to the current scroll position. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ override protected function moveIndicators(uid:String, offset:int, absolute:Boolean):void { if (!uid) return; if (!absolute && offset == 0) return; if (isCellSelectionMode()) { if (cellSelectionIndicators[uid]) { for (var p:String in cellSelectionIndicators[uid]) { if (absolute) cellSelectionIndicators[uid][p].y = offset; else cellSelectionIndicators[uid][p].y += offset; } } } else { // Functionality in AdvancedListBase, so we can't assume that it // checks for isRowSelectionMode as in ADGBase, otherwise usually // we would call the super version directly, and not in an 'else' // clause. super.moveIndicators(uid, offset, absolute); } } /** * @private */ override mx_internal function clearHighlight(item:IListItemRenderer):void { if (isCellSelectionMode()) { var uid:String = itemToUID(item.data); var c:AdvancedDataGridColumn = columnMap[item.name]; if (c) { var columnIndex:int = c.colNum; drawCellItem(item, cellSelectionData[uid] && cellSelectionData[uid][columnIndex], false, caretUID == uid && caretColumnIndex == columnIndex); } } super.clearHighlight(item); } /** * @private */ override protected function selectData(uid:String, data:Object, index:int, selectedBookmark:CursorBookmark):void { if (isCellSelectionMode()) { var columnIndex:int = selectedColumnIndex; if (!cellSelectionData[uid] || !cellSelectionData[uid][columnIndex]) { if (cellSelectionIndicators[uid] && cellSelectionIndicators[uid][columnIndex]) { selectCellItem(cellSelectionData[uid][columnIndex], false, false); } else { clearSelectedCells(); addCellSelectionData(uid, columnIndex, new AdvancedDataGridBaseSelectionData(data, index, columnIndex, false) ); selectedColumnIndex = caretColumnIndex = anchorColumnIndex = columnIndex; _selectedIndex = caretIndex = anchorIndex = index; caretBookmark = anchorBookmark = selectedBookmark; } } } else { super.selectData(uid, data, index, selectedBookmark); } } /** * @private * Clear all the selected data. */ override protected function clearAllSelection():void { if (isCellSelectionMode()) { clearSelectedCells(); clearCellIndicators(); } super.clearAllSelection(); } /** * @private */ override protected function itemRendererToIndices(item:IListItemRenderer):Point { // We do not support this method for headers as headerItems are separate from the // listItems. On the other hand, DataGrid supports this fine since headers are // listItems[0]. if (isHeaderItemRenderer(item)) return null; var pt:Point = super.itemRendererToIndices(item); // When optimumColumns is displayableColumns, then pt.x already points // to the correct column number, so we don't need to add // horizontalScrollPosition, so undo what the 'super' version does. if (pt && getOptimumColumns() != visibleColumns && pt.x >= lockedColumnCount) pt.x -= horizontalScrollPosition; return pt; } /** * @private */ override protected function getOptimumColumns():Array { if (rendererProviders.length > 0 && horizontalScrollPolicy != ScrollPolicy.OFF || columnGrouping) return displayableColumns; return super.getOptimumColumns(); } /** * @private */ override protected function getRowHeight(itemData:Object = null):Number { if (groupRowHeight > 0 && dataProvider is IHierarchicalCollectionView && _rootModel.hasChildren(itemData)) return groupRowHeight; else return super.getRowHeight(); } /** * @private */ override mx_internal function getMeasuringRenderer(c:AdvancedDataGridColumn, forHeader:Boolean, data:Object):IListItemRenderer { if (!forHeader) { var adgDescription:AdvancedDataGridRendererDescription; if (rendererProviders.length != 0) { adgDescription = getRendererDescription(data,c,false); if (adgDescription && adgDescription.renderer) { var factory:IFactory = adgDescription.renderer; var item:IListItemRenderer = measuringObjects[factory]; if (!item) { item = adgDescription.renderer.newInstance(); item.visible = false; item.styleName = c; listContent.addChild(DisplayObject(item)); measuringObjects[factory] = item; } return item; } } } return super.getMeasuringRenderer(c, forHeader, data); } //-------------------------------------------------------------------------- // // Methods // //-------------------------------------------------------------------------- /** * @private * Returns true if the specified item is a branch item. The AdvancedDataGrid * control delegates to the IDataDescriptor to determine if an item is a branch. * @param item Item to inspect. * @return True if a branch, false if not. * */ protected function isBranch(item:Object):Boolean { if (_rootModel && item != null) return _rootModel.canHaveChildren(item); return false; } /** * @private * wraps calls to the descriptor * mx_internal for automation delegate access */ mx_internal function getChildren(item:Object, view:Object):ICollectionView { //get the collection of children var children:ICollectionView = IHierarchicalCollectionView(collection).getChildren(item); return children; } /** * Determines the number of parents from root to the specified item. * Method starts with the Cursor.current item and will seek forward * to a specific offset, returning the cursor to its original position. * * @private */ mx_internal function getItemDepth(item:Object, offset:int):int { //first test for a match (most cases) if (!collection) return 0; if (!iterator) iterator = collection.createCursor(); return getDepth(item); } /** * @private * Utility method to get the depth of the item. */ protected function getDepth(item:Object):int //private { // if data is hierarchical, calculate its depth if (_rootModel is IHierarchicalData && collection is IHierarchicalCollectionView) return IHierarchicalCollectionView(dataProvider).getNodeDepth(item); return -1; } /** * Sets the associated icon in the navigation tree for the item. * Calling this method overrides the * iconField and iconFunction properties for * this item if it is a leaf item. Branch items don't use the * iconField and iconFunction properties. * They use the folderOpenIcon and folderClosedIcon properties. * * @param item An Object defining the item in the navigation tree. * This Object contains the data provider element for the item. * * @param iconID The closed (or leaf) icon. * * @param iconID2 The open icon. * * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function setItemIcon(item:Object, iconID:Class, iconID2:Class):void { if (!itemIcons) itemIcons = {}; if (!iconID2) iconID2 = iconID; itemIcons[itemToUID(item)] = { iconID: iconID, iconID2: iconID2 }; itemsSizeChanged = true; invalidateDisplayList(); } /** * @private * Gets the number of visible items from a starting item. */ private function getVisibleChildrenCount(item:Object):int { var count:int = 0; if (item == null) return count; var uid:String = itemToUID(item); var children:Object; if (IHierarchicalCollectionView(collection).openNodes[uid] && _rootModel.canHaveChildren(item) && _rootModel.hasChildren(item)) { children = getChildren(item, iterator.view); } if (children != null) { var n:int = children.length; for (var i:int = 0; i < n; i++) { count++; uid = itemToUID(children[i]); if (IHierarchicalCollectionView(collection).openNodes[uid]) count += getVisibleChildrenCount(children[i]); } } return count; } /** * Returns true if the specified branch node is open. * * @param item Branch node to inspect. * This Object contains the data provider element for the branch node. * * @return true if open, and false if not. * * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function isItemOpen(item:Object):Boolean { var uid:String = itemToUID(item); return IHierarchicalCollectionView(collection).openNodes[uid] != null; } /** * @private */ private function makeMask():DisplayObject { var tmpMask:Shape = new FlexShape(); tmpMask.name = "mask"; var g:Graphics = tmpMask.graphics; g.beginFill(0xFFFFFF); g.moveTo(0,0); g.lineTo(0,10); g.lineTo(10,10); g.lineTo(10,0); g.lineTo(0,0); g.endFill(); listContent.addChild(tmpMask); return tmpMask; } /** * Opens or closes a branch node of the navigation tree. * When a branch item opens, it restores the open and closed states * of its child branches if they were already opened. * *

If you set the dataProvider property and then immediately call * the expandItem() method, you may not see the correct behavior. * You should either wait for the component to validate * or call validateNow().

* * @param item An Object defining the branch node. This Object contains the * data provider element for the branch node. * * @param open Specify true to open the branch node, * and false to close it. * * @param animate Specify true to animate the transition. (Note: * If a branch has over 20 children, to improve performance * it does not animate the first time it opens.) * * @param dispatchEvent Specifies whether the control dispatches an open event * after the open animation is complete (true), or not (false). * * @param cause The event, if any, that initiated the item action. * * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function expandItem(item:Object, open:Boolean, animate:Boolean = false, dispatchEvent:Boolean = false, cause:Event = null):void { //if the iterator is null, that indicates we have not been //validated yet, so we will not continue. if (iterator == null) return; if (cause) lastUserInteraction = cause; expandedItem = item; listContent.allowItemSizeChangeNotification = false; listSubContent.allowItemSizeChangeNotification = false; var i:int; var n:int; var bSelected:Boolean = false; var bHighlight:Boolean = false; var bCaret:Boolean = false; var newRowIndex:int; var rowData:BaseListData; var tmpMask:DisplayObject; var uid:String = itemToUID(item); // if this can't be opened, or shouldn't be, don't! if (!isBranch(item) || (isItemOpen(item)==open) || isOpening) return; if (itemEditorInstance) endEdit(ListEventReason.OTHER); //we'll use the last recorded length not necessarily the current one oldLength = collectionLength; var bookmark:CursorBookmark = iterator.bookmark; // sent to update the length in the collection var event:CollectionEvent = new CollectionEvent( CollectionEvent.COLLECTION_CHANGE, false, true, CollectionEventKind.mx_internal::EXPAND); event.items = [item]; // update the list of openNodes if (open) { IHierarchicalCollectionView(collection).openNode(item); rowsTweened = Math.abs(oldLength - collection.length); } else { IHierarchicalCollectionView(collection).closeNode(item); //how many rows to move? rowsTweened = Math.abs(oldLength - collection.length); } // will it affect the displayList? if (isItemVisible(item)) { // is the item on screen? if (visibleData[uid]) { //find the rowindex of the row after the open thats opening/closing n = listItems.length; for (rowIndex = 0; rowIndex < n; rowIndex++) { if (rowInfo[rowIndex].uid == uid) { rowIndex++; // rowIndex is set to the row after the one opening/closing // because that is the first row to change break; } } } } //if we're opening or closing a node that is not visible, //we still need to dispatch the correct collectionChange events //so that scroll position and selection properties update correctly. else { var eventArr:Array = open ? buildUpCollectionEvents(true) : buildUpCollectionEvents(false); n = eventArr.length; for (i = 0; i < n; i++) { collection.dispatchEvent(eventArr[i]); } // This item is not visible but we need to dispatch the appropiate event if (dispatchEvent) { dispatchAdvancedDataGridEvent(open ? AdvancedDataGridEvent.ITEM_OPEN : AdvancedDataGridEvent.ITEM_CLOSE, item, visibleData[itemToUID(item)], lastUserInteraction); lastUserInteraction = null; } return; } var rC:int = listItems.length; var tmpRowInfo:Object; var col:int = treeColumnIndex; // we will cap this with as many rows as can be displayed later var rowsToMove:int = rowsTweened; var dur:Number = getStyle("openDuration"); if (animate && rowIndex < rC && rowsToMove > 0 && rowsToMove < 20 && dur!=0) { // Kill any previous animation. tween is undefined if there is no Tween underway. if (tween) tween.endTween(); var renderer:IListItemRenderer = listItems[rowIndex - 1][col]; if (renderer is IDropInListItemRenderer) { // this is done to refresh the items with their disclosure/folder icons state updated with the new one // If this is removed then the disclosure/folder icons will change their state after the // tween is over. var di:IDropInListItemRenderer = IDropInListItemRenderer(renderer); var advancedDataGridListData:AdvancedDataGridListData = AdvancedDataGridListData(di.listData); advancedDataGridListData = AdvancedDataGridListData( makeListData(advancedDataGridListData.item, advancedDataGridListData.uid, advancedDataGridListData.rowIndex,col,visibleColumns[col])); di.listData = advancedDataGridListData; renderer.data = renderer.data; // this forces eval of new listData } // animate the opening opening = open; isOpening = true; maskList = []; rowList = []; // ignoring padding left //var paddingLeft:Number = getStyle("paddingLeft"); var paddingLeft:Number = 0; var xx:Number = paddingLeft; var ww:Number = 0; var yy:Number = 0; var hh:Number = 0; // don't tween anymore than the amount of space we have var delta:int = rowIndex; var maxDist:Number = 0; var c:AdvancedDataGridColumn; var optimumColumns:Array = getOptimumColumns(); if (open) { newRowIndex = rowIndex; // don't tween anymore than the amount of space we have maxDist = listContent.height - rowInfo[rowIndex].y; iterator.seek(CursorBookmark.CURRENT, delta); var data:Object; // create the rows now so we know how much to move for (i = 0; i < rowsToMove && yy < maxDist; i++) { data = iterator.current; xx = paddingLeft; currentRowNum = rowIndex + i; hh = 0; currentColNum = 0; uid = itemToUID(data); // create renderers setupRenderer(data,uid,true); bSelected = (selectedData[uid] != null) || (cellSelectionData[uid] && cellSelectionData[uid][currentColNum.toString()]); bHighlight = highlightUID == uid; bCaret = caretUID == uid; // layout renderers created above currentItemTop = yy; hh = layoutRow(true,xx,hh); tmpRowInfo = new ListRowInfo(yy, hh, uid, data); yy += hh; rowInfo.splice(rowIndex + i, 0, tmpRowInfo); iterator.moveNext(); } rowsTweened = i; // position the new rows; var referenceRowInfo:ListRowInfo = rowInfo[rowIndex + rowsTweened]; for (i = 0; i < rowsTweened; i++) { xx = paddingLeft; rowInfo[rowIndex + i].y -= yy - referenceRowInfo.y; for (col = 0; col < optimumColumns.length; ++col) { renderer = listItems[rowIndex + i][col]; renderer.move(renderer.x, renderer.y - (yy - referenceRowInfo.y)); tmpMask = makeMask(); if (col < lockedColumnCount) tmpMask.x = xx; else tmpMask.x = getAdjustedXPos(xx); tmpMask.y = referenceRowInfo.y; tmpMask.width = renderer.width; tmpMask.height = yy; listItems[rowIndex + i][col].mask = tmpMask; xx += optimumColumns[col].width; } } } else // closing up rows { var more:Boolean = true; var valid:Boolean = true; var startY:Number = yy = rowInfo[listItems.length - 1].y + rowInfo[listItems.length - 1].height; // figure out how much space was consumed by the rows that are going away for (i = rowIndex; i < rowIndex + rowsToMove && i < rC; i++) { maxDist += rowInfo[i].height; xx = paddingLeft; var tempArray:Array = []; for (col = 0; col < optimumColumns.length; ++col) { // retain a reference to the rows going away tempArray.push({item: listItems[i][col]}); tmpMask = makeMask(); if (col < lockedColumnCount) tmpMask.x = xx; else tmpMask.x = getAdjustedXPos(xx); tmpMask.y = listItems[rowIndex][col].y; // changed rowIndex to i because we want to create the mask for the entire renderer's width //tmpMask.width = listItems[rowIndex][col].width; tmpMask.width = listItems[i][col].width; tmpMask.height = maxDist; listItems[i][col].mask = tmpMask; xx += optimumColumns[col].width; } if (tempArray.length != 0) rowList.push(tempArray); } rowsToMove = i - rowIndex; // remove the rows going away rowInfo.splice(rowIndex, rowsToMove); listItems.splice(rowIndex, rowsToMove); iterator.seek(CursorBookmark.CURRENT, listItems.length); more = (iterator != null && !iterator.afterLast && iteratorValid); maxDist += yy; // create the rows now so we know how much to move for (i = 0; i < rowsToMove && yy < maxDist; i++) { //reset item specific values uid = null; data = null; renderer = null; valid = more; data = more ? iterator.current : null; xx = paddingLeft; if(valid) { currentRowNum = rC - rowsToMove + i; currentColNum = 0; uid = itemToUID(data); setupRenderer(data,uid,true); bSelected = selectedData[uid] != null; bHighlight = highlightUID == uid; bCaret = caretUID == uid; currentItemTop = yy; hh = layoutRow(true,xx,hh); } else { // if we've run out of data, we dont make renderers // and we inherit the previous row's height or rowHeight // if it is the first row. // EXCEPT when variable row height is on since the row // above us might be bigger then we are. So we'll get // this row out of the rowList and check it. /* if (!variableRowHeight) { hh = rowIndex + i > 0 ? rowInfo[rowIndex + i - 1].height : rowHeight; } else { if (rowList[i]) { hh = Math.ceil(rowList[i].item.getExplicitOrMeasuredHeight() + cachedPaddingTop + cachedPaddingBottom); } else { //default hh = rowHeight; } } */ hh = rowHeight; } tmpRowInfo = new ListRowInfo(yy, hh, uid, data); rowInfo.push(tmpRowInfo); yy += hh; if (more) more = iterator.moveNext(); } //make indicator masks for rows var maskY:Number = rowList[0][0].item.y - getStyle("paddingTop"); var maskX:Number = rowList[0][0].item.x - getStyle("paddingLeft"); for (i = 0; i < rowList.length; i++) { var lastColumn:int = rowList[i].length -1; var indicator:Object = selectionIndicators[itemToUID(rowList[i][lastColumn].item.data)]; if (indicator) { tmpMask = makeMask(); tmpMask.x = maskX; tmpMask.y = maskY; tmpMask.width = rowList[i][lastColumn].item.x + rowList[i][lastColumn].item.width + getStyle("paddingLeft") + getStyle("paddingRight"); tmpMask.height = rowList[i][lastColumn].item.y + rowList[i][lastColumn].item.height + getStyle("paddingTop") + getStyle("paddingBottom") - maskY; selectionIndicators[itemToUID(rowList[i][lastColumn].item.data)].mask = tmpMask; } } //make indicator masks for cells var cellMaskY:Number = rowList[0][0].item.y - getStyle("paddingTop"); var cellMaskX:Number = rowList[0][0].item.x - getStyle("paddingLeft"); for (var cellRow:int = 0; cellRow < rowList.length; cellRow++) for (var cellColumn:int = 0; cellColumn < rowList[cellRow].length; cellColumn++) { var cellLastColumn:int = rowList[cellRow].length -1; var cellUID:String = itemToUID(rowList[cellRow][cellColumn].item.data); if (cellUID in cellSelectionIndicators) { var cellIndicator:Object = cellSelectionIndicators[cellUID] [cellColumn]; if (cellIndicator) { tmpMask = makeMask(); tmpMask.x = maskX; tmpMask.y = maskY; tmpMask.width = rowList[cellRow][cellLastColumn].item.x + rowList[cellRow][cellLastColumn].item.width + getStyle("paddingLeft") + getStyle("paddingRight"); tmpMask.height = rowList[cellRow][cellLastColumn].item.y + rowList[cellRow][cellLastColumn].item.height + getStyle("paddingTop") + getStyle("paddingBottom") - maskY; cellIndicator.mask = tmpMask; } } } } // restore the iterator iterator.seek(bookmark, 0); rC = rowList.length; for (i = 0; i < rC; i++) { for (col = 0; col < rowList[i].length; col++) { rowList[i][col].itemOldY = rowList[i][col].item.y; } } rC = listItems.length; for (i = rowIndex; i < rC; i++) { if (listItems[i].length) { rowInfo[i].itemOldY = listItems[i][0].y; } rowInfo[i].oldY = rowInfo[i].y; } // slow down the tween if there's lots of rows to tween dur = dur * Math.max(rowsToMove / 5, 1); if (dispatchEvent) eventAfterTween = item; tween = new Tween(this, 0, (open) ? yy : startY - yy, dur, 5); var oE:Function = getStyle("openEasingFunction") as Function; if (oE != null) tween.easingFunction = oE; // Block all layout, responses from web service, and other background // processing until the tween finishes executing. UIComponent.suspendBackgroundProcessing(); // force drawing in case there's new rows UIComponentGlobals.layoutManager.validateNow(); } else { // not to be animated if (dispatchEvent) { // in case when eventItemRenderer is null, // instead of taking item renderer from visibleData, we should take it from listItems for // the correct value. var itemRenderer:IListItemRenderer = eventItemRenderer ? eventItemRenderer : visibleData[itemToUID(item)]; dispatchAdvancedDataGridEvent(open ? AdvancedDataGridEvent.ITEM_OPEN : AdvancedDataGridEvent.ITEM_CLOSE, item, itemRenderer, lastUserInteraction); lastUserInteraction = null; eventItemRenderer = null; } itemsSizeChanged = true; invalidateDisplayList(); } // If we're wordwrapping, no need to adjust maxHorizontalScrollPosition. // Also check if _userMaxHorizontalScrollPosition is greater than 0. if (!wordWrap && initialized) { super.maxHorizontalScrollPosition = _userMaxHorizontalScrollPosition > 0 ? _userMaxHorizontalScrollPosition + getIndent() : super.maxHorizontalScrollPosition; } //restore ItemSizeChangeNotification flag listContent.allowItemSizeChangeNotification = variableRowHeight; listSubContent.allowItemSizeChangeNotification = variableRowHeight; } /** * @private */ mx_internal function onTweenUpdate(value:Object):void { var renderer:IFlexDisplayObject; var n:int; var m:int; var i:int; var j:int; var deltaY:Number; var lastY:Number; var columnIndexString:String; var cellSelectionUID:String; n = listItems.length; var s:Sprite; for (i = rowIndex; i < n; i++) { //move items that are staying if (listItems[i].length) { m = listItems[i].length; for (j = 0; j < m; ++j) { renderer = IFlexDisplayObject(listItems[i][j]); lastY = renderer.y; renderer.move(renderer.x, rowInfo[i].itemOldY + value); deltaY = renderer.y - lastY; } } //move selection graphics of the items that are staying visible rowInfo[i].y += deltaY; s = selectionIndicators[rowInfo[i].uid]; if (s) s.y += deltaY; // Cell movement if (isCellSelectionMode()) { cellSelectionUID = rowInfo[i].uid; if (cellSelectionIndicators[cellSelectionUID]) { for (columnIndexString in cellSelectionIndicators[cellSelectionUID]) { cellSelectionIndicators[cellSelectionUID][columnIndexString].y += deltaY; } } } } //move the items that are going away. n = rowList.length; for (i = 0; i < n; i++) { var doOnce:Boolean = true ; m = rowList[i].length; for (j = 0; j < m; j++) { s = null; renderer = IFlexDisplayObject(rowList[i][j].item); // Row movement if (rowMap[renderer.name] != null) { s = selectionIndicators[BaseListData(rowMap[renderer.name]).uid]; } lastY = renderer.y; renderer.move(renderer.x, rowList[i][j].itemOldY + value); deltaY = renderer.y - lastY; //move selection graphic for items that are going away if (doOnce && s) { s.y += deltaY; doOnce = false; } } // Cell movement if (isCellSelectionMode()) { cellSelectionUID = BaseListData(rowMap[renderer.name]).uid; if (cellSelectionIndicators[cellSelectionUID]) { for (columnIndexString in cellSelectionIndicators[cellSelectionUID]) { cellSelectionIndicators[cellSelectionUID][columnIndexString].y += deltaY; } } } } } /** * @private */ mx_internal function onTweenEnd(value:Object):void { UIComponent.resumeBackgroundProcessing(); onTweenUpdate(value); var i:int; var j:int; var n:int; var m:int; var renderer:*; var dilir:IDropInListItemRenderer; var rC:int = listItems.length; var itemUID:*; var indicator:Object; var cellColumnString:String; isOpening = false; //dispatch collectionChange ADD or REMOVE events that correlate //to the nodes that were expanded or collapsed if (collection) { var eventArr:Array = opening ? buildUpCollectionEvents(true) : buildUpCollectionEvents(false); n = eventArr.length; for (i = 0; i < n; i++) { collection.dispatchEvent(eventArr[i]); } } if (opening) { var firstDeletedRow:int = -1; for (i = rowIndex; i < rC; i++) { if (listItems[i].length) { for (j =0; j < listItems[i].length; ++j) { renderer = listItems[i][j]; var mask:DisplayObject = renderer.mask; if (mask) { listContent.removeChild(mask); renderer.mask = null; } rowMap[renderer.name].rowIndex = i; if (renderer is IDropInListItemRenderer) { dilir = IDropInListItemRenderer(renderer); if (dilir.listData) { dilir.listData.rowIndex = i; dilir.listData = dilir.listData; // call the setter } } if (renderer.y > listContent.height) { addToFreeItemRenderers(renderer); itemUID = itemToUID(renderer.data); // row mask if (selectionIndicators[itemUID]) { //remove indicators mask indicator = selectionIndicators[itemUID]; if (indicator) { mask = indicator.mask; if (mask) { listContent.removeChild(mask); indicator.mask = null; } } removeIndicators(itemUID); } // cell mask if (isCellSelectionMode()) { if (cellSelectionIndicators[itemUID]) { for (cellColumnString in cellSelectionIndicators[itemUID]) { //remove indicators mask indicator = cellSelectionIndicators[itemUID][cellColumnString]; if (indicator) { mask = indicator.mask; if (mask) { listContent.removeChild(mask); indicator.mask = null; } } } removeCellIndicators(itemUID, int(cellColumnString)); } } delete rowMap[renderer.name]; if (firstDeletedRow < 0) firstDeletedRow = i; } } } else { if (rowInfo[i].y >= listContent.height) { if (firstDeletedRow < 0) firstDeletedRow = i; } } } if (firstDeletedRow >= 0) { rowInfo.splice(firstDeletedRow); listItems.splice(firstDeletedRow); } } else //closing { n = rowList.length; for (i = 0; i < n; i++) { m = rowList[i].length; for (j = 0; j < m; j++) { mask = rowList[i][j].item.mask; if (mask) { listContent.removeChild(mask); rowList[i][j].item.mask = null; } addToFreeItemRenderers(rowList[i][j].item); //kill graphic and graphic mask if necessary itemUID = itemToUID(rowList[i][j].item.data); // row mask if (selectionIndicators[itemUID]) { //remove indicators mask indicator = selectionIndicators[itemUID]; if (indicator) { mask = indicator.mask; if (mask) { listContent.removeChild(mask); indicator.mask = null; } } removeIndicators(itemUID); } // cell mask if (isCellSelectionMode()) { if (cellSelectionIndicators[itemUID]) { for (cellColumnString in cellSelectionIndicators[itemUID]) { //remove indicators mask indicator = cellSelectionIndicators[itemUID][cellColumnString]; if (indicator) { mask = indicator.mask; if (mask) { listContent.removeChild(mask); indicator.mask = null; } } removeCellIndicators(itemUID, int(cellColumnString)); } } } delete rowMap[rowList[i][j].item.name]; } } for (i = rowIndex; i < rC; i++) { if (listItems[i].length) { m = listItems[i].length; for (j =0; j < m; ++j) { renderer = listItems[i][j]; rowMap[renderer.name].rowIndex = i; if (renderer is IDropInListItemRenderer) { dilir = IDropInListItemRenderer(renderer); if (dilir.listData) { dilir.listData.rowIndex = i; dilir.listData = dilir.listData; // call the setter } } } } } } //should we dispatch an AdvancedDataGrid event? if (eventAfterTween) { // in case when eventItemRenderer is null, // instead of taking item renderer from visibleData, we should take it from listItems for // the correct value. var itemRenderer:IListItemRenderer = eventItemRenderer ? eventItemRenderer : visibleData[itemToUID(eventAfterTween)]; dispatchAdvancedDataGridEvent((isItemOpen(eventAfterTween) ? AdvancedDataGridEvent.ITEM_OPEN : AdvancedDataGridEvent.ITEM_CLOSE), eventAfterTween, itemRenderer, lastUserInteraction); lastUserInteraction = null; eventAfterTween = false; itemRenderer = null; } //invalidate itemsSizeChanged = true; invalidateDisplayList(); // Get rid of the tween, so this onTweenEnd doesn't get called more than once. tween = null; // check for scrollbars configureScrollBars(); } /** * @private * * Helper function that builds up the collectionChange ADD or * REMOVE events that correlate to the nodes that were expanded * or collapsed. */ private function buildUpCollectionEvents(open:Boolean):Array { var ce:CollectionEvent; var item:Object; var parentArray:Array; var rowsAdded:Array = []; var rowsRemoved:Array = []; var retVal:Array = []; var itemIndex:int = getItemIndex(expandedItem); if (open) { var children:ICollectionView = getChildren(expandedItem, iterator.view); if (!children) return []; var cursor:IViewCursor = children.createCursor(); var push:Boolean = true; while (!cursor.afterLast) { rowsAdded.push(item); try { cursor.moveNext(); } catch (e:ItemPendingError) { // should not come here break; } } } else { var stack:Array = []; var i:int; stack = getOpenChildrenStack(expandedItem, stack); var n:int = stack.length; while (i < n) { var m:int = selectedItems.length; for (var j:int = 0; j < m; j++) { if (selectedItems[j] == stack[i]) { bSelectedItemRemoved = true; } } rowsRemoved.push(stack[i]); i++; } } if (rowsAdded.length > 0) { ce = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); ce.kind = CollectionEventKind.ADD; ce.location = itemIndex + 1; ce.items = rowsAdded; retVal.push(ce); } if (rowsRemoved.length > 0) { ce = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE); ce.kind = CollectionEventKind.REMOVE; ce.location = itemIndex; ce.items = rowsRemoved; retVal.push(ce); } return retVal; } /** * @private * Go through the open items and figure out which is deepest. */ private function getIndent():Number { var depth:Number = 0; for (var p:String in IHierarchicalCollectionView(collection).openNodes) { // add one since its children are actually indented depth = Math.max(getItemDepth(IHierarchicalCollectionView(collection).openNodes[p], 0), depth); } return depth * getStyle("indentation"); } /** * @private */ private function getItemIndex(item:Object):int { var cursor:IViewCursor = collection.createCursor(); var i:int = 0; do { if (cursor.current === item) break; i++; } while (cursor.moveNext()); return i; } /** * @private */ private function getIndexItem(index:int):Object { var cursor:IViewCursor = collection.createCursor(); var i:int = index; while (cursor.moveNext()) { if (i == 0) return cursor.current; i--; } return null; } /** * Opens or closes all the nodes of the navigation tree below the specified item. * *

If you set the dataProvider property and then immediately call * the expandChildrenOf() method, you may not see the correct behavior. * You should either wait for the component to validate, * or call the validateNow() method before calling expandChildrenOf().

* * @param item An Object defining the branch node. This Object contains the * data provider element for the branch node. * * @param open Specify true to open the items, * and false to close them. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function expandChildrenOf(item:Object, open:Boolean):void { //if the iterator is null, that indicates we have not been //validated yet, so we will not continue. if (iterator == null) return; // if it is not a branch item there's nothing to do if (isBranch(item)) { dispatchAdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_OPENING, item, //item null, //renderer null, //trigger open, //opening false, //animate true); //dispatch var i:int = 0; var childItems:Object if (item != null && _rootModel.canHaveChildren(item) && _rootModel.hasChildren(item)) { childItems = getChildren(item, iterator.view); } if (childItems) { var cursor:IViewCursor = childItems.createCursor(); while (!cursor.afterLast) { if (isBranch(cursor.current)) expandChildrenOf(cursor.current, open); cursor.moveNext(); } } } } /** * Returns the parent of a child item. This method returns a value * only if the item was or is currently visible. Top-level items have a * parent with the value null. * * @param item An Object defining the child item. This Object contains the * data provider element for the child. * * @return The parent of the item. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function getParentItem(item:Object):* { if (item == null) return null; if (item && collection) return IHierarchicalCollectionView(collection).getParentItem(item); return null; } /** * @private * Returns the stack of parents from a child item. */ private function getParentStack(item:Object):Array { var stack:Array = []; if (item == null) return stack; var parent:* = getParentItem(item); while (parent) { stack.push(parent); parent = getParentItem(parent); } return stack; } /** * @private * Returns a stack of all open descendants of an item. */ private function getOpenChildrenStack(item:Object, stack:Array):Array { var curr:Object; if (item == null) return stack; var children:ICollectionView = getChildren(item, iterator.view); if (!children) return []; var cursor:IViewCursor = children.createCursor(); while (!cursor.afterLast) { curr = cursor.current; stack.push(curr); if (isBranch(curr) && isItemOpen(curr)) { getOpenChildrenStack(curr, stack); } try { cursor.moveNext(); } catch(e:ItemPendingError) { break; } } return stack; } /** * @private * Finds the index distance between a parent and child */ private function getChildIndexInParent(parent:Object, child:Object):int { var index:int = 0; if (!parent) { var cursor:IViewCursor = ICollectionView(iterator.view).createCursor(); while (!cursor.afterLast) { if (child === cursor.current) break; index++; cursor.moveNext(); } } else { if (parent != null && _rootModel.canHaveChildren(parent) && _rootModel.hasChildren(parent)) { var children:ICollectionView = getChildren(parent, iterator.view); if (children.contains(child)) { var n:int = children.length; for (; index < n; index++) { if (child === children[index]) break; } } else { //throw new Error("Parent item does not contain specified child: " + itemToUID(child)); } } } return index; } /** * @private * Collapses those items in the selected items array that have * parent nodes already selected. */ private function collapseSelectedItems():Array { var collection:ArrayCollection = new ArrayCollection(selectedItems); for (var i:int = 0; i < selectedItems.length; i++) { var item:Object = selectedItems[i]; var stack:Array = getParentStack(item); for (var j:int = 0; j < stack.length; j++) { if (collection.contains(stack[j])) { //item's parent is included in the selected item set var index:int = collection.getItemIndex(item); var removed:Object = collection.removeItemAt(index); break; } } } return collection.source; } /** * Initializes an AdvancedDataGridListData object that is used by the AdvancedDataGrid item renderer. * * @param item The item to be rendered. * This Object contains the data provider element for the item. * * @param adgListData The AdvancedDataGridListDataItem to use in rendering the item. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function initListData(item:Object, adgListData:AdvancedDataGridListData):void { if (item == null) return; var open:Boolean = isItemOpen(item); var branch:Boolean = isBranch(item); var uid:String = itemToUID(item); // this is hidden by non-branches but kept so we know how wide it is so things align if (displayDisclosureIcon) adgListData.disclosureIcon = getStyle(open ? "disclosureOpenIcon" : "disclosureClosedIcon"); adgListData.open = open; adgListData.hasChildren = _rootModel.hasChildren(item); // set the depth as 1 if the item is pending if (itemPending) adgListData.depth = 1; else adgListData.depth = getItemDepth(item, adgListData.rowIndex); adgListData.indent = (adgListData.depth - 1) * getStyle("indentation"); adgListData.item = item; if (groupIconFunction != null && isBranch(item)) { if (groupIconFunction(item, adgListData.depth) != null) adgListData.icon = groupIconFunction(item, adgListData.depth); else adgListData.icon = itemToIcon(item); } else adgListData.icon = itemToIcon(item); } /** * @private * Delegates to the Descriptor to add a child to a parent */ mx_internal function addChildItem(parent:Object, child:Object, index:Number):Boolean { return IHierarchicalCollectionView(dataProvider).addChildAt(parent, child, index); } /** * @private * Delegates to the Descriptor to remove a child from a parent */ mx_internal function removeChildItem(parent:Object, child:Object, index:Number):Boolean { return IHierarchicalCollectionView(dataProvider).removeChildAt(parent, index); } /** * @private */ mx_internal function dispatchAdvancedDataGridEvent(type:String, item:Object, renderer:IListItemRenderer, trigger:Event = null, opening:Boolean = true, animate:Boolean = true, dispatch:Boolean = true):void { var event:AdvancedDataGridEvent; // Create expanding event. if (type == AdvancedDataGridEvent.ITEM_OPENING) { event = new AdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_OPENING, false, true); event.opening = opening; event.animate = animate; event.dispatchEvent = dispatch; } // Create all other events. if (!event) event = new AdvancedDataGridEvent(type); // Common values. event.item = item; event.itemRenderer = renderer; event.triggerEvent = trigger; // Send it off. dispatchEvent(event); } /** * Generate sample data based on number of columns and their dataFields, * so that the designer can see how the data will look like. * * @private */ private function setDesignViewData():void { designViewDataFlag = true; var sampleDataProvider:Array = []; // The sample data is going to be just integers in ascending order. var counter:int = 1; // Number of rows to display. const numberOfRows:int = 10; if (!columns || columns.length == 0) return; var i:int; var sampleDataRow:Object; var j:int; var m:int; var col:AdvancedDataGridColumn; if (designViewDataType == designViewDataFlatType) { for (i = 0; i < numberOfRows; i++) { sampleDataRow = {}; m = columns.length; for (j = 0; j < m; j++) { col = columns[j]; if (col.dataField != null) { sampleDataRow[col.dataField] = counter; } counter++; } sampleDataProvider.push(sampleDataRow); } dataProvider = sampleDataProvider; } else if (designViewDataType == designViewDataTreeType) { var branchName:String = columns[0].dataField || ""; var branchCounter:int = 1; for (i = 0; i < numberOfRows; i++) { var childrenString:String = "children"; sampleDataRow = {}; sampleDataRow[childrenString] = [{}]; sampleDataRow[branchName] = resourceManager.getString("datamanagement", "Branch", [branchCounter]); m = columns.length; for (j = 0; j < m; j++) { col = columns[j]; if (col.dataField != null) { sampleDataRow[childrenString][0][col.dataField] = counter; } counter++; } sampleDataProvider.push(sampleDataRow); branchCounter++; } dataProvider = new HierarchicalData(sampleDataProvider); } } /** * Set sort in design view to showcase multi column sorting. * * @private */ private function setDesignViewSort():void { if (collection == null) return; var sort:Sort = new Sort(); sort.fields = []; var oddDescending:Boolean = false; var n:int = columns.length; for (var i:int = 0; i < n; i++) { sort.fields.push( new SortField(columns[i].dataField, false, oddDescending, false) ); oddDescending = !oddDescending; } if (collection) collection.sort = sort; } /** * @private */ public function setDesignView():void { designViewDataType = dataType; designViewDataTypeChanged = true; invalidateProperties(); } /** * Process cells (set in selectedCells) which are not yet displayed * in case we have scrolled to the view where it can be displayed. * * @private */ protected function processCellsWaitingToBeDisplayed():void { var pendingCellsDisplayed:Array = []; var n:int = pendingCellSelection.length; var i:int; for (i = 0; i < n; i++) { var visibleCoords:Object = absoluteToVisibleIndices( pendingCellSelection[i].rowIndex, pendingCellSelection[i].columnIndex); var visibleRowIndex:int = visibleCoords.rowIndex; var visibleColIndex:int = visibleCoords.columnIndex; var listItem:IListItemRenderer; if (listItems[visibleRowIndex] && listItems[visibleRowIndex][visibleColIndex]) { listItem = listItems[visibleRowIndex][visibleColIndex]; selectCellItem(listItem, false, true); pendingCellsDisplayed.push(i); } } // Make sure we remove in reverse order, from things at the end to // the start so that we remove correctly. For example, if we have to // remove the first and third entries, then we have to make sure we // remove third and then first to maintain correctness. pendingCellsDisplayed.sort(Array.NUMERIC | Array.DESCENDING); n = pendingCellsDisplayed.length; for (i = 0; i < n; i++) { pendingCellSelection.removeItemAt(pendingCellsDisplayed[i]); } if (pendingCellSelection.length == 0) cellsWaitingToBeDisplayed = false; } /** * Expands all the nodes of the navigation tree in the control. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function expandAll():void { if (dataProvider is IHierarchicalCollectionView && iterator) { // move to the first item iterator.seek(CursorBookmark.FIRST); while(!iterator.afterLast) { var item:Object = iterator.current; // open the item if its a branch item and its not already open if (isBranch(item) && !isItemOpen(item)) { IHierarchicalCollectionView(collection).openNode(item); // open node // dispatch ITEM_OPEN event var itemRenderer:IListItemRenderer = visibleData[itemToUID(item)]; dispatchAdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_OPEN, item, itemRenderer, null); } iterator.moveNext(); } itemsSizeChanged = true; invalidateDisplayList(); // seek to the correct position iterator.seek(CursorBookmark.FIRST, verticalScrollPosition); } } /** * Collapses all the nodes of the navigation tree. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function collapseAll():void { if (dataProvider is IHierarchicalCollectionView && iterator) { // clear the selected items clearSelected(); //dispatch events for each (var item:* in IHierarchicalCollectionView(collection).openNodes) { dispatchAdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_CLOSE, item, visibleData[itemToUID(item)]); } var oldValue:int = verticalScrollPosition; verticalScrollPosition = 0; IHierarchicalCollectionView(collection).openNodes = {}; // set the verticalScrollPosition to a valid value and seek to that position verticalScrollPosition = oldValue >= collection.length ? collection.length - 1 : oldValue; iterator.seek(CursorBookmark.FIRST, verticalScrollPosition); } } /** * @private * * Get the renderer and column span for custom renderer * */ mx_internal function getRendererDescription(itemData:Object,c:AdvancedDataGridColumn, forDragProxy:Boolean = false):AdvancedDataGridRendererDescription { var adgDescription:AdvancedDataGridRendererDescription = new AdvancedDataGridRendererDescription(); var index:int = 0; var depth:int = 0; if (rendererProviders.length !=0) { if (forDragProxy) index = getItemIndex(itemData); depth = getItemDepth(itemData,index); } var n:int = rendererProviders.length; for (var i:int = 0; i < n; i++) { IAdvancedDataGridRendererProvider(rendererProviders[i]).describeRendererForItem(itemData,depth,c,adgDescription); if (adgDescription.renderer) return adgDescription; } return adgDescription; } /** * @private * */ private function findHeaderRenderer(pt:Point):IListItemRenderer { var headerItem:IListItemRenderer; var xMatched:Boolean=false; var array:Array = visibleHeaderInfos; var x:Number; while(!xMatched) { var n:int = array ? array.length : 0; for(var i:int=0; i < n; i++) { headerItem = array[i]["headerItem"]; x = headerItem.x; if(array[i].actualColNum >= lockedColumnCount) x = getAdjustedXPos(headerItem.x); if(pt.x >= x && pt.x < (x + headerItem.getExplicitOrMeasuredWidth())) { xMatched = true; if(pt.y >headerItem.y - cachedPaddingTop && pt.y <= headerItem.y + headerItem.height + cachedPaddingBottom) return headerItem; else if(array[i].visibleChildren && array[i].visibleChildren.length > 0) array = array[i].visibleChildren; else return null; break; } } if(!xMatched)//There is no point of keep looping over if x is not within range return null; xMatched = false; } return null; } /** * @private */ private function getLastColumnResidualWidth():Number { var n:int = displayableColumns.length-1; var displayWidth:int = unscaledWidth - viewMetrics.right - viewMetrics.left; var totalWidth:Number = 0; var i:int; var numLockCols:int = Math.max(0, lockedColumnCount); //Find the scrollable width i.e displayWidth - { sum of width of locked columns} if (numLockCols > 0 && numLockCols < visibleColumns.length) { for (i = 0; i < numLockCols; i++) { displayWidth -= displayableColumns[i].width; } } // Starting from the right most column in the displayableColumns array // find out how many columns we will be able to // accumulate and how much width they will take // when horizontal scroll bar is at the rightmost end if (n>=0) { totalWidth = (isNaN(displayableColumns[n].explicitWidth) ? displayableColumns[n].preferredWidth : displayableColumns[n].explicitWidth); } for (i = n-1; i >= numLockCols; i--) { if (totalWidth + displayableColumns[i].width <= displayWidth) totalWidth += displayableColumns[i].width; else break; } // The residual width is the width which should be added to // the last column apriori, so that in future when we have horizontally // scrolled to the right most position we don't need to create larger // items for that column return displayWidth - totalWidth; } /** * @private */ private function createHeaderItems(subHeaderInfos:Array):Number { var i:int; var c:AdvancedDataGridColumn; var rowData:AdvancedDataGridListData; var item:IListItemRenderer; var childrenWidth:Number; var w:Number = 0; var n:int = subHeaderInfos.length; for (i = 0; i < n; i++) { if(subHeaderInfos[i].visibleChildren) childrenWidth = createHeaderItems(subHeaderInfos[i].visibleChildren); c = subHeaderInfos[i].column; //When we have reached the last column little extra work //to see the future i.e when display will be scrolled horizontally // at extreme right position if (horizontalScrollPolicy != ScrollPolicy.OFF && c == displayableColumns[displayableColumns.length-1]) lastColumnWidth = (isNaN(c.explicitWidth) ? c.preferredWidth : c.explicitWidth) + getLastColumnResidualWidth(); item = getHeaderRenderer(c); // passing rowNum as -1 for headers rowData = AdvancedDataGridListData(makeListData(c, uid, -1, c.colNum, c)); rowMap[item.name] = rowData; if (item is IDropInListItemRenderer) IDropInListItemRenderer(item).listData = rowData; item.data = c; item.styleName = c; if (!isMeasuringHeader) addRendererToContentArea(item, c); headerItemsList.push(item); subHeaderInfos[i].headerItem = item; // set prefW so we can compute prefH if (subHeaderInfos[i].visibleChildren) { item.explicitWidth = childrenWidth; c.width = childrenWidth; } else if (!isNaN(lastColumnWidth)&& c == displayableColumns[displayableColumns.length-1]) { item.explicitWidth = lastColumnWidth; } else { item.explicitWidth = c.width; } UIComponentGlobals.layoutManager.validateClient(item, true); // but size it regardless of what prefW is if (!isNaN(lastColumnWidth) && c == displayableColumns[displayableColumns.length-1]) w += lastColumnWidth; else w += c.width; } return w; } /** * @private */ private function measureHeaderHeights(subHeaderInfos:Array, depth:uint):Number { var i:int; var maxRowHeight:Number=0; var padding:int = cachedPaddingTop + cachedPaddingBottom; var n:int = subHeaderInfos.length; for ( i = 0; i < n; i++) { var currentGroupHeight:Number = _explicitHeaderHeight ? _headerHeight : subHeaderInfos[i].headerItem.getExplicitOrMeasuredHeight()+padding; if (subHeaderInfos[i].visibleChildren) currentGroupHeight += measureHeaderHeights(subHeaderInfos[i].visibleChildren, depth+1); maxRowHeight = Math.max(maxRowHeight, currentGroupHeight); } if (!headerRowInfo[depth]) headerRowInfo[depth] = new ListRowInfo(0,0, uid); headerRowInfo[depth].height = Math.max(headerRowInfo[depth].height, maxRowHeight); return headerRowInfo[depth].height; } /** * @private */ private function layoutHeaders(subHeaderInfos:Array, rowX:Number, rowY:Number, depth:int):void { var i:int; var w:Number = rowX; var padding:int = cachedPaddingTop + cachedPaddingBottom; headerRowInfo[depth].y = rowY; var n:int = subHeaderInfos.length; for (i = 0; i < n; i++) { subHeaderInfos[i].headerItem.move(w, rowY+cachedPaddingTop); var currentItemHeight:Number = subHeaderInfos[i].headerItem.height; if (subHeaderInfos[i].visibleChildren) layoutHeaders(subHeaderInfos[i].visibleChildren, w, rowY + currentItemHeight + padding, depth+1); w += subHeaderInfos[i].headerItem.getExplicitOrMeasuredWidth(); } } /** * Applies styles from the AdvancedDatagrid control to an item renderer. * The item renderer should implement the IStyleClient and IDataRenderer interfaces, * and be a subclass of the DisplayObject class. * * @param givenItemRenderer The item renderer. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function applyUserStylesForItemRenderer(givenItemRenderer:IListItemRenderer):void { if (!givenItemRenderer) return; if (! ( // It should support all the following interfaces/inheritances givenItemRenderer is IStyleClient && givenItemRenderer is IDataRenderer && givenItemRenderer is DisplayObject ) ) { return; } var itemRenderer:IStyleClient = givenItemRenderer as IStyleClient; var column:AdvancedDataGridColumn = columnMap[DisplayObject(itemRenderer).name]; // 0. Make sure we have a dictionary if (!oldStyles) { oldStyles = new Dictionary(true); // use weakKeys } // 1. Reset to the default i.e. "old" styles var styleName:String; if (oldStyles[itemRenderer]) { for (styleName in oldStyles[itemRenderer]) { itemRenderer.setStyle(styleName, oldStyles[itemRenderer][styleName]); } delete oldStyles[itemRenderer]; } // 2. Call the grid's styleFunction var newStyles:Object; if (styleFunction != null) { newStyles = styleFunction(IDataRenderer(itemRenderer).data, column); if (newStyles) { for (styleName in newStyles) { if (!oldStyles[itemRenderer]) { oldStyles[itemRenderer] = {}; } oldStyles[itemRenderer][styleName] = itemRenderer.getStyle(styleName); itemRenderer.setStyle(styleName, newStyles[styleName]); } } } // 3. Call the column's styleFunction if (column && column.styleFunction != null) { newStyles = column.styleFunction(IDataRenderer(itemRenderer).data, column); if (newStyles) { for (styleName in newStyles) { // Don't store the "old" value for this styleName if grid.styleFunction has // already stored it if (!oldStyles[itemRenderer]) { oldStyles[itemRenderer] = {}; } if (!oldStyles[itemRenderer][styleName]) { oldStyles[itemRenderer][styleName] = itemRenderer.getStyle(styleName); } itemRenderer.setStyle(styleName, newStyles[styleName]); } } } } /** * @private */ private function removeOldHeaders():void { var item:IListItemRenderer; if (columnGrouping) { //In case column grouping was not there previously then there will be items in headerItems // We need to remove them if (headerItems && headerItems.length) { while (headerItems.length) { var headerRow:Array = headerItems.pop(); while (headerRow.length) { item = IListItemRenderer(headerRow.pop()); item.parent.removeChild(DisplayObject(item)); } } } } else { //In case column grouping was there previously then there will be items in headerItemsList // We need to remove them if (headerItemsList && headerItemsList.length) { while (headerItemsList.length) { item = headerItemsList.pop(); item.parent.removeChild(DisplayObject(item)); } } //there may be items in freeItemRenderersTable also. We need to remove them as well if (_groupedColumns) { var n:int = _groupedColumns.length; for ( var i:int = 0; i < n; i++) { if (_groupedColumns[i] is AdvancedDataGridColumnGroup) columnGroupRendererChanged(AdvancedDataGridColumnGroup(_groupedColumns[i])); } } } } /** * @private */ private function columnGroupRendererChanged(c:AdvancedDataGridColumnGroup):void { var n:int = c.children ? c.children.length : 0; if (n > 0) { columnRendererChanged(c); for( var i:int = 0; i < n; i++) { if (c.children[i] is AdvancedDataGridColumnGroup) columnGroupRendererChanged(c.children[i]); } } } /** * @private */ private function addItemsToTarget(event:DragEvent, items:Array):void { // add items only if the target component is AdvancedDataGrid /* if (!(event.relatedObject is AdvancedDataGrid)) return; */ var className:String = getQualifiedClassName(event.relatedObject); var classType:Class = getDefinitionByName(className) as Class; var o:Object = new classType(); if (!(o is AdvancedDataGrid)) return; //then do the add on the target control var targetADG:Object = event.relatedObject; if (!targetADG.collection) { // Empty tree. Use the first item to create a dataprovider var item:Object = items.shift(); targetADG.dataProvider = new HierarchicalData(item); targetADG.validateNow(); } else if (targetADG.collection.length == 0) { //Empty dataProvider. Add the first item in. var obj:Object = items.shift(); targetADG.addChildItem(targetADG._dropData.parent, obj, targetADG._dropData.index); } var n:int = items.length; for (var i:int = n - 1; i >= 0; i--) { targetADG.addChildItem(targetADG._dropData.parent, items[i], targetADG._dropData.index); } } /** * @private * * validate the groupedColumns and verify that same column object is not * used twice */ private function validateGroupedColumns(groupedColumns:Array):Boolean { var dict:Dictionary = new Dictionary(); var queue:Array = []; var valid:Boolean = true; var n:int = groupedColumns.length; for (var i:int = 0; i < n; i++) { queue.push(groupedColumns[i]); } while (queue.length > 0 && valid) { var c:AdvancedDataGridColumn = queue.shift();; var uid:String = itemToUID(c); if (dict[uid]!=null) { valid = false; throw new Error(resourceManager.getString( "datamanagement", "repeatColumnsNotAllowed")); } else { dict[uid] = c; if (c is AdvancedDataGridColumnGroup) { var cGroup:AdvancedDataGridColumnGroup = AdvancedDataGridColumnGroup(c); if (cGroup.children) { n = cGroup.children.length; for (i = 0; i < n; i++) { queue.push(cGroup.children[i]); } } } } } return valid; } /** * @private */ private function getLeafColumns(groupedColumns:Array):Array { var i:int=0; while (i < groupedColumns.length) { if (groupedColumns[i] is AdvancedDataGridColumnGroup && groupedColumns[i].children && groupedColumns[i].children.length >0) { var prefix:Array = groupedColumns.slice(0,i); var postfix:Array = groupedColumns.slice(i+1); prefix = prefix.concat(getLeafColumns(groupedColumns[i].children)); groupedColumns = prefix.concat(postfix); i = prefix.length; } else { i++; } } return groupedColumns; } /** * @private */ private function initializeGroupedHeaderInfo(groupedColumns:Array, parent:AdvancedDataGridHeaderInfo, depth:int, parentInternalLabelFunction:Function=null):Array { var headerInfos:Array = []; var n:int = groupedColumns.length; for (var i:int = 0; i < n; i++ ) { var headerInfo:AdvancedDataGridHeaderInfo = new AdvancedDataGridHeaderInfo(groupedColumns[i], parent, i, depth) ; var uid:String = itemToUID(groupedColumns[i]); columnsToInfo[uid] = headerInfo; groupedColumns[i].owner = this; //Parent has a dataField/labelFunction already, child can extract data via this internalLabelFunction only headerInfo.internalLabelFunction = parentInternalLabelFunction; //In case this header was selected through keyboard navigation if (selectedHeaderInfo && selectedHeaderInfo.column == groupedColumns[i]) selectedHeaderInfo = headerInfo; if (groupedColumns[i] is AdvancedDataGridColumnGroup && groupedColumns[i].children && groupedColumns[i].children.length > 0) { //If this column has a dataField/labelFunction, children columns will get data through an internalLabelFunction if (groupedColumns[i].dataField || groupedColumns[i].labelFunction || parentInternalLabelFunction != null) { var childInternalLabelFunction:Function; if (parentInternalLabelFunction == null) childInternalLabelFunction = (function(data:Object, c:AdvancedDataGridColumn):* { var parentInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(c).parent; var parentColumn:AdvancedDataGridColumnGroup = parentInfo.column as AdvancedDataGridColumnGroup; return parentColumn.itemToData(data); }); else childInternalLabelFunction = ( function(data:Object, c:AdvancedDataGridColumn):* { var parentInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(c).parent; var parentColumn:AdvancedDataGridColumnGroup = parentInfo.column as AdvancedDataGridColumnGroup; var dataFromParent:* = parentInfo.internalLabelFunction(data, parentInfo.column); return parentColumn.itemToData(dataFromParent); }); } headerInfo.children = initializeGroupedHeaderInfo(groupedColumns[i].children, headerInfo, depth+1, childInternalLabelFunction); } headerInfos.push(headerInfo); } return headerInfos; } /** * @private */ protected function updateVisibleHeaderInfos(headerInfos:Array = null, parentVisible:Boolean = true, actualColNum:int = 0):Object { var n:int = headerInfos ? headerInfos.length : 0; var i:int; var k:int = 0; var visibleHeaderInfos:Array; var totalColumnSpan:int = 0; for (i = 0; i < n; i++) { var headerInfo:AdvancedDataGridHeaderInfo = headerInfos[i]; //If parent is not visible we don't want any of the child to be visible if(!parentVisible) headerInfo.visible = false; else headerInfo.visible = headerInfo.column.visible; if(headerInfos[i].visible == false) { headerInfo.visibleIndex = NaN; headerInfo.actualColNum = NaN; headerInfo.columnSpan = 0; //Need to tell set visible = false to all the children updateVisibleHeaderInfos(headerInfo.children, false); } else { if (headerInfos[i].children && headerInfos[i].children.length > 0) { var children:Object = updateVisibleHeaderInfos(headerInfos[i].children, true, actualColNum); headerInfo.visibleChildren = children.infos; //If there aren't any visibleChildren then we don't want parent either if (headerInfo.visibleChildren == null) { headerInfo.visible = false; headerInfo.actualColNum = NaN; headerInfo.columnSpan = 0; } else { if (!visibleHeaderInfos) visibleHeaderInfos = []; visibleHeaderInfos.push(headerInfo); headerInfo.visibleIndex = k++; headerInfo.actualColNum = actualColNum; headerInfo.columnSpan = children.colSpan; } } else { //No children and..it is visible we want this!! if (!visibleHeaderInfos) visibleHeaderInfos = []; headerInfo.visibleIndex = k++; visibleHeaderInfos.push(headerInfo); headerInfo.actualColNum = actualColNum; headerInfo.columnSpan = 1; } } totalColumnSpan += headerInfo.columnSpan; actualColNum += headerInfo.columnSpan; } return {infos:visibleHeaderInfos, colSpan:totalColumnSpan}; } /** * @private */ private function updateHeadersList(headerInfos:Array):void { var n:int = headerInfos ? headerInfos.length : 0; var totalColumnSpan:int = 0; for( var i:int = 0; i < n; i++) { var headerInfo:AdvancedDataGridHeaderInfo = headerInfos[i]; orderedHeadersList.push(headerInfo); if(headerInfo.visibleChildren && headerInfo.visibleChildren.length > 0) updateHeadersList(headerInfo.visibleChildren); } } /** * @private */ private function updateDropData(event:DragEvent):void { var rowCount:int = rowInfo.length; var rowNum:int = 0; // we need to take care of headerHeight var yy:int = rowInfo[rowNum].height + (headerVisible ? headerHeight :0); var pt:Point = globalToLocal(new Point(event.stageX, event.stageY)); while (rowInfo[rowNum] && pt.y >= yy) { if (rowNum != rowInfo.length-1) { rowNum++; yy += rowInfo[rowNum].height; } else { // now we're past all rows. adding a pixel or two should be enough. // at this point yOffset doesn't really matter b/c we're past all elements // but might as well try to keep it somewhat correct yy += rowInfo[rowNum].height; rowNum++; } } var lastRowY:Number = rowNum < rowInfo.length ? rowInfo[rowNum].y : (rowInfo[rowNum-1].y + rowInfo[rowNum-1].height); var yOffset:Number = pt.y - lastRowY; var rowHeight:Number = rowNum < rowInfo.length ? rowInfo[rowNum].height : rowInfo[rowNum-1].height; //rowInfo[rowNum].height; rowNum += verticalScrollPosition; var parent:Object; var index:int; var emptyFolder:Boolean = false; var numItems:int = collection ? collection.length : 0; var topItem:Object = (rowNum > _verticalScrollPosition && rowNum <= numItems) ? listItems[rowNum - _verticalScrollPosition - 1][0].data : null; var bottomItem:Object = (rowNum - verticalScrollPosition < rowInfo.length && rowNum < numItems) ? listItems[rowNum - _verticalScrollPosition][0].data : null; var topParent:Object = collection ? getParentItem(topItem) : null; var bottomParent:Object = collection ? getParentItem(bottomItem) : null; // check their relationship if (yOffset > rowHeight * .5 && isItemOpen(bottomItem) && _rootModel.canHaveChildren(bottomItem) && !_rootModel.hasChildren(bottomItem)) { // we'll get here if we're dropping into an empty folder. // we have to be in the lower 50% of the row, otherwise // we're "between" rows. parent = bottomItem; index = 0; emptyFolder = true; } else if (!topItem && !rowNum == rowCount) { parent = collection ? getParentItem(bottomItem) : null; index = bottomItem ? getChildIndexInParent(parent, bottomItem) : 0; rowNum = 0; } else if (bottomItem && bottomParent == topItem) { // we're dropping in the first item of a folder, that's an easy one parent = topItem; index = 0; } else if (topItem && bottomItem && topParent == bottomParent) { parent = collection ? getParentItem(topItem) : null; index = iterator ? getChildIndexInParent(parent, bottomItem) : 0; } else { //we're dropping at the end of a folder. Pay attention to the position. if (topItem && (yOffset < (rowHeight * .5))) { // ok, we're on the top half of the bottomItem. parent = topParent; index = getChildIndexInParent(parent, topItem) + 1; // insert after } else if (!bottomItem) { parent = null; if ((rowNum - verticalScrollPosition) == 0) index = 0; else if (collection) index = collection.length; else index = 0; } else { parent = bottomParent; index = getChildIndexInParent(parent, bottomItem); } } _dropData = { parent: parent, index: index, localX: event.localX, localY: event.localY, emptyFolder: emptyFolder, rowHeight: rowHeight, rowIndex: rowNum }; } /** * @private */ protected function selectColumnGroupHeader(headerInfo:AdvancedDataGridHeaderInfo):void { var r:IListItemRenderer = headerInfo.headerItem; var parentLayer:Sprite; var otherLayer:Sprite; var s:Sprite; if (headerInfo.actualColNum < lockedColumnCount) { parentLayer = selectionLayer; otherLayer = movingSelectionLayer; } else { parentLayer = movingSelectionLayer; otherLayer = selectionLayer; } s = Sprite(parentLayer.getChildByName("headerKeyboardSelection")); if (!s) s = Sprite(otherLayer.getChildByName("headerKeyboardSelection")); if (! s) { s = new FlexSprite(); s.name = "headerKeyboardSelection"; } if (s.parent != parentLayer) parentLayer.addChild(s); var g:Graphics = s.graphics; g.clear(); g.beginFill( (isPressed || isKeyPressed) ? getStyle("selectionColor") : getStyle("rollOverColor") ); g.drawRect(0, 0, r.width, r.height+cachedPaddingTop+cachedPaddingBottom - 0.5); g.endFill(); s.x = r.x; s.y = r.y - cachedPaddingTop; // Make sure other selection is removed caretIndex = -1; isPressed = false; selectItem(headerInfo.headerItem, false, false); } /** * @private */ private function scrollColumnGroupIfNeeded(h1:AdvancedDataGridHeaderInfo, h2:AdvancedDataGridHeaderInfo):void { if (!isColumnFullyVisible(displayableColumns[h2.actualColNum].colNum)) scrollToViewColumn(displayableColumns[h2.actualColNum].colNum, displayableColumns[h1.actualColNum].colNum); } /** * @private */ protected function clearHeaderHorizontalSeparators():void { if (!horizontalSeparators) return; var lines:Sprite = Sprite(listSubContent.getChildByName("lines")); var headerHorizontalLines:Sprite = Sprite(lines.getChildByName("headerHorizontalLines")); while (headerHorizontalLines.numChildren) { headerHorizontalLines.removeChildAt(headerHorizontalLines.numChildren - 1); horizontalSeparators.pop(); } var lockedContent:Sprite = getLockedContent(); var lockedHeaderHorizontalLines:Sprite = Sprite(lockedContent.getChildByName("lockedHeaderheaderHorizontalLines")); if (lockedHeaderHorizontalLines) { while (lockedHeaderHorizontalLines.numChildren) { lockedHeaderHorizontalLines.removeChildAt(lockedHeaderHorizontalLines.numChildren - 1); horizontalLockedSeparators.pop(); } } } /** * @private */ protected function drawHeaderHorizontalSeparators():void { if (!horizontalSeparators) { horizontalSeparators = []; horizontalLockedSeparators = []; } var childIndex:int; var lines:Sprite = Sprite(listSubContent.getChildByName("lines")); var headerHorizontalLines:UIComponent = UIComponent(lines.getChildByName("headerHorizontalLines")); if (!headerHorizontalLines) { headerHorizontalLines = new UIComponent(); headerHorizontalLines.name = "headerHorizontalLines"; lines.addChild(headerHorizontalLines); } if (lines.numChildren > 1) { var vHeaderLines:UIComponent = UIComponent(lines.getChildByName("header")); //Push the horizontal header lines behind vertical lines // Otherwise they look little dirty if (vHeaderLines) { childIndex = lines.getChildIndex(vHeaderLines); if (childIndex+1 < lines.numChildren) lines.setChildIndex(headerHorizontalLines, childIndex+1); } } numSeparators = 0; var unlockedHeaderInfos:Array; var lockedHeaderInfos:Array if(visibleHeaderInfos) { if(!isNaN(_lockedColumnCountVal) && _lockedColumnCountVal > 0) { lockedHeaderInfos = visibleHeaderInfos.slice(0, _lockedColumnCountVal); unlockedHeaderInfos = visibleHeaderInfos.slice(_lockedColumnCountVal); } else { unlockedHeaderInfos = visibleHeaderInfos; } //Draw horizontal header separators in the unlocked area createHeaderHorizontalSeparators(unlockedHeaderInfos, horizontalSeparators, headerHorizontalLines); } //Remove extra separators created from the unlocked area while (headerHorizontalLines.numChildren > numSeparators) { headerHorizontalLines.removeChildAt(headerHorizontalLines.numChildren - 1); horizontalSeparators.pop(); } var lockedHeaderHorizontalLines:UIComponent ; numSeparators = 0; if (lockedColumnCount > 0) { var lockedContent:Sprite = getLockedContent(); lockedHeaderHorizontalLines = UIComponent(lockedContent.getChildByName("lockedHeaderHorizontalLines")); if (!lockedHeaderHorizontalLines) { lockedHeaderHorizontalLines = new UIComponent(); lockedHeaderHorizontalLines.name = "lockedHeaderHorizontalLines"; lockedContent.addChild(lockedHeaderHorizontalLines); } if (lockedContent.numChildren > 1) { var lockedHeaderLines:UIComponent = UIComponent(lockedContent.getChildByName("lockedHeaderLines")); //Push the horizontal header lines behind vertical lines // Otherwise they look little dirty if (lockedHeaderLines) { childIndex = lockedContent.getChildIndex(lockedHeaderLines); if (childIndex+1 < lines.numChildren) lockedContent.setChildIndex(lockedHeaderHorizontalLines, childIndex+1); } } createHeaderHorizontalSeparators(lockedHeaderInfos, horizontalLockedSeparators, lockedHeaderHorizontalLines); } //Remove extra horizontal separators from the locked area if (lockedHeaderHorizontalLines) while (lockedHeaderHorizontalLines.numChildren > numSeparators) { lockedHeaderHorizontalLines.removeChildAt(lockedHeaderHorizontalLines.numChildren - 1); horizontalLockedSeparators.pop(); } } /** * @private */ private function getLockedContent():Sprite { var locked:Sprite = Sprite(listContent.getChildByName("lockedContent")); if (!locked) { locked = new UIComponent(); locked.name = "lockedContent"; locked.cacheAsBitmap = true; locked.mouseEnabled = false; listContent.addChild(locked); } listContent.setChildIndex(locked, listContent.numChildren - 1); return locked; } /** * @private */ protected function createHeaderHorizontalSeparators(subHeaderInfos:Array, seperators:Array, headerLines:UIComponent):void { var n:int = subHeaderInfos.length; for (var i:int = 0; i < n; i++) { if(subHeaderInfos[i].visible && subHeaderInfos[i].visibleChildren && subHeaderInfos[i].visibleChildren.length > 0) { var sep:UIComponent = getHorizontalSeparator(numSeparators, seperators, headerLines); var sepSkin:IFlexDisplayObject = IFlexDisplayObject(sep.getChildAt(0)); if (!subHeaderInfos[i].headerItem) sep.visible = false; numSeparators++; sep.visible = true; var w:Number = subHeaderInfos[i].column.width; sepSkin.setActualSize(w, 2); sep.x = subHeaderInfos[i].headerItem.x; var depth:int = subHeaderInfos[i].depth; sep.y = headerRowInfo[depth].y + (headerRowInfo[depth].height - headerRowInfo[depth+1].height) - 2; createHeaderHorizontalSeparators(subHeaderInfos[i].children, seperators, headerLines); } } } /** * @private */ protected function getHorizontalSeparator(i:int, seperators:Array, headerLines:UIComponent):UIComponent { var sep:UIComponent; var sepSkin:IFlexDisplayObject; var headerSeparatorClass:Class = getStyle("headerHorizontalSeparatorSkin"); if (i < headerLines.numChildren) { sep = UIComponent(headerLines.getChildAt(i)); sepSkin = IFlexDisplayObject(sep.getChildAt(0)); } else { sep = new UIComponent(); headerLines.addChild(sep); } if (sepSkin) { if (sepSkin is headerSeparatorClass) { sepSkin = IFlexDisplayObject(sep.getChildAt(0)); } else { //Remove the skin, because headerHorizontalSeparatorSkin style has //changed now and this skin is of no use sep.removeChildAt(0); sepSkin = null; } } if (!sepSkin) { sepSkin = new headerSeparatorClass(); if (sepSkin is ISimpleStyleClient) ISimpleStyleClient(sepSkin).styleName = this; sep.addChild(DisplayObject(sepSkin)); } seperators.push(sep); return sep; } /** * @private */ private function updateMovingColumnIndex():void { movingColumnIndex = -1; if(movingColumn) { var n:int = orderedHeadersList ? orderedHeadersList.length : 0; for ( var i:int = 0; i < n; i++) { if(orderedHeadersList[i].column == movingColumn) { movingColumnIndex = i; break; } } } } /** * @private */ protected function finishCellKeySelection():void { var uid:String; var rowCount:int = listItems.length; var partialRow:int = (rowInfo[rowCount-1].y + rowInfo[rowCount-1].height > listContent.height) ? 1 : 0; var listItem:IListItemRenderer; var bSelChanged:Boolean = false; var columnIndex:int = caretColumnIndex; // Move selection if (bSelectItem && caretIndex - verticalScrollPosition >= 0) { if (caretIndex - verticalScrollPosition > listItems.length-1) caretIndex = listItems.length - 1 + verticalScrollPosition; var visibleCoords:Object = absoluteToVisibleIndices(caretIndex, caretColumnIndex); var visibleRowIndex:int = visibleCoords.rowIndex; var visibleColIndex:int = visibleCoords.columnIndex; if (visibleRowIndex >= 0 && visibleColIndex >= 0) listItem = listItems[visibleRowIndex][visibleColIndex]; // if column spanning is used and the item is invisible, // then find the item which is spanning across this column while (listItem && !listItem.visible) { if (listItems[visibleRowIndex] && listItems[visibleRowIndex][visibleColIndex - 1]) { listItem = listItems[visibleRowIndex][visibleColIndex]; visibleColIndex--; columnIndex--; } else { break; } } if (listItem) { uid = itemToUID(listItem.data); if (visibleCellRenderers[uid]) listItem = visibleCellRenderers[uid][columnIndex.toString()]; if (bShiftKey) { selectCellItem(listItem, bShiftKey, bCtrlKey); bSelChanged = true; } else if (bCtrlKey) { if (lastKey == Keyboard.SPACE) { selectCellItem(listItem, bShiftKey, bCtrlKey, true); } else { drawCellItem(listItem, cellSelectionData[uid] && cellSelectionData[uid][columnIndex.toString()] != null, uid == highlightUID && caretColumnIndex == highlightColumnIndex, true); bSelChanged = true; } } else { var oldCaretIndex:int = caretIndex; clearSelectedCells(); caretIndex = oldCaretIndex; anchorIndex = caretIndex; _selectedIndex = caretIndex; selectedColumnIndex = caretColumnIndex; anchorColumnIndex = caretColumnIndex; selectCellItem(listItem, bShiftKey, bCtrlKey, false); bSelChanged = true; } } } // Move caret if (!bSelectItem && caretColumnIndex - horizontalScrollPosition >= 0) { if (caretColumnIndex - horizontalScrollPosition > _columns.length - 1) caretColumnIndex = _columns.length - 1 + horizontalScrollPosition; if (caretIndex - verticalScrollPosition >= 0) { var caretCoords:Object = absoluteToVisibleIndices(caretIndex, caretColumnIndex); listItem = listItems[caretCoords.rowIndex][caretCoords.columnIndex]; } if (listItem) { uid = itemToUID(listItem.data); if (visibleCellRenderers[uid]) listItem = visibleCellRenderers[uid][columnIndex.toString()]; if (bShiftKey) { selectCellItem(listItem, bShiftKey, bCtrlKey); bSelChanged = true; } else if (bCtrlKey) { drawCellItem(listItem, cellSelectionData[uid] && cellSelectionData[uid][columnIndex.toString()] != null, uid == highlightUID && caretColumnIndex == highlightColumnIndex, true); } else { oldCaretIndex = caretIndex; clearSelectedCells(); caretIndex = oldCaretIndex; anchorIndex = caretIndex; _selectedIndex = caretIndex; selectCellItem(listItem, bShiftKey, bCtrlKey, false); bSelChanged = true; } } } if (bSelChanged) { var evt:ListEvent = new ListEvent(ListEvent.CHANGE); evt.itemRenderer = listItem; var pt:Point = itemRendererToIndices(listItem); if (pt) { evt.rowIndex = pt.y; evt.columnIndex = displayToAbsoluteColumnIndex(pt.x); } dispatchEvent(evt); } } /** * Updates the list of selected cells, assuming that the specified item renderer was * clicked by the mouse, and the keyboard modifiers are in the specified state. * *

This method also updates the display of the item renderers based on their updated * selected state.

* * @param item The item renderer for the cell. * * @param shiftKey true to specify that the Shift key is pressed, * and false if not. * * @param ctrlKey true to specify that the Control key is pressed, * and false if not. * * @param transition Specify true to animate the transition. * * @return Returns true if the operation succeeded. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function selectCellItem(item:IListItemRenderer, shiftKey:Boolean, ctrlKey:Boolean, transition:Boolean = true):Boolean { if (!selectable || !item || !itemRendererToIndices(item)) return false; if (isHeaderItemRenderer(item)) return false; // We'll start by assuming that the selection has not changed. var selectionChange:Boolean = false; var placeHolder:CursorBookmark = iterator.bookmark; var pt:Point = itemRendererToIndices(item); if (pt.x < 0) return false; var index:int = pt.y; var columnIndex:int = displayToAbsoluteColumnIndex(pt.x); var uid:String = itemToUID(item.data); var data:Object = item.data; if (selectionMode == SINGLE_CELL || (selectionMode == MULTIPLE_CELLS && !ctrlKey && !shiftKey)) { // We want to know if 0, 1 or more items are selected var numSelected:int = 0; if (selectionMode == MULTIPLE_CELLS) { var curSelectionData:AdvancedDataGridBaseSelectionData = firstCellSelectionData; if (curSelectionData != null) { numSelected++; if (curSelectionData.nextSelectionData) numSelected++; } } // allow unselecting via ctrl-click if (ctrlKey && cellSelectionData[uid] && cellSelectionData[uid][columnIndex]) { selectionChange = true; var oldCaretIndex:int = caretIndex; var oldCaretColumnIndex:int = caretColumnIndex; clearSelectedCells(); caretIndex = oldCaretIndex; caretColumnIndex = oldCaretColumnIndex; } // Plain old click, ignore if same item is selected unless number of // selected items is going to change else if ((_selectedIndex != index || selectedColumnIndex != columnIndex) || (selectionMode == MULTIPLE_CELLS && numSelected != 1) || (selectionMode == SINGLE_CELL)) { selectionChange = true; clearSelectedCells(); // Add new cell to cellSelectionData addCellSelectionData(uid, columnIndex, new AdvancedDataGridBaseSelectionData(item.data, index, columnIndex, false) ); // Insert new cell's location at head position // of outside-facing selectedCells addToSelectedCells(index, columnIndex); // Draw the cell as selected, check if it is already highlighted drawCellItem(item, true, uid == highlightUID && columnIndex == highlightColumnIndex, true, transition); // Update index, cell selectedColumnIndex = columnIndex; _selectedIndex = pt.y; iterator.seek(CursorBookmark.CURRENT, _selectedIndex - verticalScrollPosition); // Update caret and anchor anchorColumnIndex = caretColumnIndex = selectedColumnIndex; anchorIndex = caretIndex = _selectedIndex; anchorBookmark = caretBookmark = iterator.bookmark; // Reset iterator to top of visible rows iterator.seek(placeHolder, 0); } } else if (shiftKey && selectionMode == MULTIPLE_CELLS) { if (anchorBookmark) { var oldAnchorBookmark:CursorBookmark = anchorBookmark; var oldAnchorIndex:int = anchorIndex; var oldAnchorColumnIndex:int = anchorColumnIndex; clearSelectedCells(); caretIndex = index; caretColumnIndex = columnIndex; caretBookmark = iterator.bookmark; anchorIndex = oldAnchorIndex; anchorBookmark = oldAnchorBookmark; anchorColumnIndex = oldAnchorColumnIndex; try { iterator.seek(anchorBookmark, 0); } catch (e:ItemPendingError) { e.addResponder(new ItemResponder( cellSelectionPendingResultHandler, cellSelectionPendingFailureHandler, new AdvancedDataGridBaseSelectionPending( index, anchorIndex, columnIndex, anchorColumnIndex, item.data, transition, placeHolder, CursorBookmark.CURRENT, 0) ) ); iteratorValid = false; } shiftCellSelectionLoop(index, anchorIndex, columnIndex, anchorColumnIndex, item.data, transition, placeHolder); selectionChange = true; } } else if (ctrlKey && selectionMode == MULTIPLE_CELLS) { if (cellSelectionData[uid] && cellSelectionData[uid][columnIndex.toString()]) { removeCellSelectionData(uid, columnIndex); drawCellItem(visibleCellRenderers[uid][columnIndex.toString()], // item false, // selected uid == highlightUID && columnIndex == highlightColumnIndex, // highlighted true, // caret transition); // transition removeFromSelectedCells(index, columnIndex); } else { addCellSelectionData(uid, columnIndex, new AdvancedDataGridBaseSelectionData( item.data, index, columnIndex, false) ); drawCellItem(visibleCellRenderers[uid][columnIndex.toString()], // item true, // selected uid == highlightUID && columnIndex == highlightColumnIndex, // highlighted true, // caret transition); // transition addToSelectedCells(index, columnIndex); _selectedIndex = index; selectedColumnIndex = columnIndex; // Setting this causes unexpected behavior in the UI because // it does a call of updateDisplayList on its own, and we can't // set it directly since it's a private variable in ListBase // selectedItem = item.data; } iterator.seek(CursorBookmark.CURRENT, index - verticalScrollPosition); caretIndex = index; caretColumnIndex = columnIndex; caretBookmark = iterator.bookmark; anchorIndex = index; anchorColumnIndex = columnIndex; anchorBookmark = iterator.bookmark; iterator.seek(placeHolder, 0); selectionChange = true; } return selectionChange; } /** * @private * Handle shift-selection of cells. * */ private function shiftCellSelectionLoop(stopIndex:int, anchorIndex:int, stopColumnIndex:int, anchorColumnIndex:int, stopData:Object, transition:Boolean, placeHolder:CursorBookmark):void { var incr:Boolean = (anchorIndex < stopIndex); var columnIncr:Boolean = (anchorColumnIndex < stopColumnIndex); var data:Object; var uid:String; var index:int = anchorIndex; try { // loop for rows do { data = iterator.current; uid = itemToUID(data); var columnIndex:int = anchorColumnIndex; // loop for columns while (true) { addCellSelectionData(uid, columnIndex, new AdvancedDataGridBaseSelectionData( data, index, columnIndex, false) ); addToSelectedCells(index, columnIndex); if (visibleCellRenderers[uid] && visibleCellRenderers[uid][columnIndex.toString()] && visibleCellRenderers[uid][columnIndex.toString()].visible) // Checking visibility to handle column spanning { drawCellItem(visibleCellRenderers[uid][columnIndex.toString()], true, uid == highlightUID && columnIndex == highlightColumnIndex, false, transition); } if (columnIndex != stopColumnIndex && columnIndex != -1) { if (columnIncr) columnIndex = viewDisplayableColumnAtOffset(columnIndex, +1, -1, false); else columnIndex = viewDisplayableColumnAtOffset(columnIndex, -1, -1, false); } else { break; } } if (data == stopData) { if (visibleCellRenderers[uid] && visibleCellRenderers[uid][columnIndex.toString()]) { drawCellItem(visibleCellRenderers[uid][columnIndex.toString()], true, uid == highlightUID && columnIndex == highlightColumnIndex, true, transition); } break; } if (incr) index++; else index--; } while (incr ? iterator.moveNext() : iterator.movePrevious()); } catch (e:ItemPendingError) { e.addResponder(new ItemResponder( cellSelectionPendingResultHandler, cellSelectionPendingFailureHandler, new AdvancedDataGridBaseSelectionPending( index, anchorIndex, columnIndex, anchorColumnIndex, data, transition, placeHolder, CursorBookmark.CURRENT, 0) ) ); iteratorValid = false; } try { iterator.seek(placeHolder, 0); iteratorValid = true; } catch (e2:ItemPendingError) { lastSeekPending = new ListBaseSeekPending(placeHolder, 0); e2.addResponder(new ItemResponder( seekPendingResultHandler, seekPendingFailureHandler, lastSeekPending)); } } /** * @private */ private function cellSelectionPendingResultHandler(data:Object, info:AdvancedDataGridBaseSelectionPending):void { iterator.seek(info.bookmark, info.offset); shiftCellSelectionLoop(info.index, info.anchorIndex, info.columnIndex, info.anchorColumnIndex, info.stopData, info.transition, info.placeHolder); } /** * @private */ private function cellSelectionPendingFailureHandler(data:Object, info:AdvancedDataGridBaseSelectionPending):void { } /** * @private * Check if a cell is already present in selecedCells. */ private function isCellAlreadySelected(rowIndex:int, columnIndex:int):Boolean { if (!_selectedCells || !_selectedCells.length) return false; return cellSelections[[rowIndex, columnIndex]] != null; } /** * Helper function to add a row/column location to the selectedCells array * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ mx_internal function addToSelectedCells(rowIndex:int, columnIndex:int):void { if (isCellAlreadySelected(rowIndex, columnIndex)) return; var newCell:Object = { rowIndex : rowIndex, columnIndex : columnIndex }; var newSelectedCells:Array = _selectedCells; if (!newSelectedCells) newSelectedCells = []; newSelectedCells.unshift(newCell); _selectedCells = newSelectedCells; cellSelections[[rowIndex,columnIndex]] = newCell; } /** * Helper function to remove a row/column location to the selectedCells array * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ mx_internal function removeFromSelectedCells(rowIndex:int, columnIndex:int):void { var cellToRemove:int = -1; if (_selectedCells) { var n:int = _selectedCells.length; for (var i:int = 0; i < n; i++) { // Skip the cell that we want to remove if (_selectedCells[i].rowIndex == rowIndex && _selectedCells[i].columnIndex == columnIndex) { cellToRemove = i; break; } } if (cellToRemove != -1) _selectedCells.splice(cellToRemove, 1); } } /** * Adds cell selection information to the control, as if you used the mouse to select the cell. * * @param uid The UID of the selected cell. * * @param columnIndex The column index of the selected cell. * * @param selectionData An AdvancedDataGridBaseSelectionData instance defining the * information about the selected cell. * * @see mx.controls.advancedDataGridClasses.AdvancedDataGridBaseSelectionData * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function addCellSelectionData(uid:String, columnIndex:int, selectionData:AdvancedDataGridBaseSelectionData):void { if (firstCellSelectionData != null) firstCellSelectionData.prevSelectionData = selectionData; selectionData.nextSelectionData = firstCellSelectionData; firstCellSelectionData = selectionData; if (!cellSelectionData[uid]) cellSelectionData[uid] = {}; cellSelectionData[uid][columnIndex.toString()] = selectionData; } /** * Removes cell selection information from the control. * * @param uid The UID of the selected cell. * * @param columnIndex The column index of the selected cell. * * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function removeCellSelectionData(uid:String, columnIndex:int):void { if (!cellSelectionData[uid]) return; var curSelectionData:AdvancedDataGridBaseSelectionData = cellSelectionData[uid][columnIndex.toString()]; if (firstCellSelectionData == curSelectionData) firstCellSelectionData = curSelectionData.nextSelectionData; if (curSelectionData.prevSelectionData != null) curSelectionData.prevSelectionData.nextSelectionData = curSelectionData.nextSelectionData; if (curSelectionData.nextSelectionData != null) curSelectionData.nextSelectionData.prevSelectionData = curSelectionData.prevSelectionData; delete cellSelectionData[uid][columnIndex.toString()]; // Remove uid if there are no columns for that uid in cellSelectionData if (!atLeastOneProperty(cellSelectionData[uid])) delete cellSelectionData[uid]; } /** * Returns true if the Object has at least one property, * which means that the dictionary has at least one key. * * @param o The Object to inspect. * * @return true if the Object has at least one property. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function atLeastOneProperty(o:Object):Boolean { for (var p:String in o) // substitute for an "object.length" property return true; for each (var variable:XML in describeType(data).variable) return true; return false; } /** * Clears the selectedCells property. * * @param transition Specify true to animate the transition. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function clearSelectedCells(transition:Boolean = false):void { for (var p:String in visibleCellRenderers) { for (var q:String in cellSelectionData[p]) { var item:IListItemRenderer; var visibleRenderer:Object = visibleCellRenderers[p]; if (visibleRenderer) item = visibleRenderer[q]; if (item) drawCellItem(item, false, p == highlightUID && highlightColumnIndex == int(q), false, transition); } } _selectedCells = []; cellSelections = {}; clearCellSelectionData(); _selectedIndex = -1; selectedItem = null; caretIndex = -1; anchorIndex = -1; caretBookmark = null; anchorBookmark = null; } /** * @private * Remove entries in pendingCellSelection. */ protected function clearPendingCells():void { cellsWaitingToBeDisplayed = false; pendingCellSelection.removeAll(); } /** * Clears information on cell selection. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function clearCellSelectionData():void { cellSelectionData = {}; firstCellSelectionData = null; } /** * @private */ protected function addIndicatorToSelectionLayer(o:Sprite, columnIndex:int):void { if (columnIndex < lockedColumnCount) { if (o.parent != selectionLayer) selectionLayer.addChild(DisplayObject(o)); else o.parent.setChildIndex(DisplayObject(o), o.parent.numChildren - 1); } else { if (o.parent != movingSelectionLayer) movingSelectionLayer.addChild(DisplayObject(o)); else o.parent.setChildIndex(DisplayObject(o), o.parent.numChildren - 1); } } /** * @private * Draw a cell item renderer based on whether it is/is not selected, * highlighted and the caret is positioned on the cell. * */ protected function drawCellItem(item:IListItemRenderer, selected:Boolean = false, highlighted:Boolean = false, caret:Boolean = false, transition:Boolean = false):void { if (!item || isHeaderItemRenderer(item)) return; var pt:Point = itemRendererToIndices(item); if (!pt) return; var index:int = pt.y; var columnIndex:int = displayToAbsoluteColumnIndex(pt.x); var rowData:BaseListData = rowMap[item.name]; var o:Sprite; var g:Graphics; var itemXPos:Number = item.x; // if (columnIndex > lockedColumnCount) // itemXPos = getAdjustedXPos(itemXPos); if (highlighted && (!highlightItemRenderer || (highlightUID != rowData.uid && columnIndex != highlightColumnIndex))) { if (!highlightIndicator) { o = new SpriteAsset(); highlightIndicator = o; } o = highlightIndicator; addIndicatorToSelectionLayer(o, pt.x); drawHighlightIndicator( o, // sprite itemXPos, // x rowInfo[rowData.rowIndex].y, // y item.width, // width rowInfo[rowData.rowIndex].height, // height getStyle("rollOverColor"), // color item); // IListItemRenderer highlightItemRenderer = item; highlightUID = rowData.uid; highlightColumnIndex = columnIndex; } else if (!highlighted && highlightItemRenderer && (rowData && highlightUID == rowData.uid && highlightColumnIndex == columnIndex)) { if (highlightIndicator) Sprite(highlightIndicator).graphics.clear(); lastHighlightItemRenderer = highlightItemRenderer; highlightItemRenderer = null; highlightUID = null; highlightColumnIndex = -1; } if (selected) { if (!cellSelectionIndicators[rowData.uid]) cellSelectionIndicators[rowData.uid] = {}; var newIndicator:Boolean = false; if (!cellSelectionIndicators[rowData.uid][columnIndex.toString()]) { o = new SpriteAsset(); o.mouseEnabled = false; cellSelectionIndicators[rowData.uid][columnIndex.toString()] = o; newIndicator = true; } o = cellSelectionIndicators[rowData.uid][columnIndex.toString()]; addIndicatorToSelectionLayer(o, pt.x); drawSelectionIndicator( o, // sprite itemXPos, // x rowInfo[rowData.rowIndex].y, // y item.width, // width rowInfo[rowData.rowIndex].height, // height enabled ? // color getStyle("selectionColor") : getStyle("selectionDisabledColor"), item); // IListItemRenderer if (newIndicator) if (transition) applyCellSelectionEffect(o, rowData.uid, columnIndex, item); } else if (!selected) { if (rowData) { var rowIndicators:Object = cellSelectionIndicators[rowData.uid]; var columnIndicator:Sprite; if (rowIndicators && (columnIndicator = rowIndicators[columnIndex.toString()])) { o = columnIndicator; if (o.parent) o.parent.removeChild(o); delete rowIndicators[columnIndex.toString()]; if (!atLeastOneProperty(rowIndicators)) delete cellSelectionIndicators[rowData.uid]; } } } if (caret) { // Only draw the caret if there has been keyboard navigation. if (showCaret) { if (!caretIndicator) { o = new SpriteAsset(); o.mouseEnabled = false; caretIndicator = o; } o = caretIndicator; addIndicatorToSelectionLayer(o, pt.x); drawCaretIndicator( o, // sprite itemXPos, // x rowInfo[rowData.rowIndex].y, // y item.width, // width rowInfo[rowData.rowIndex].height, // height getStyle("selectionColor"), // color item); // IListItemRenderer caretItemRenderer = item; caretUID = rowData.uid; caretColumnIndex = columnIndex; } } else if (!caret && caretItemRenderer && caretUID == rowData.uid && caretColumnIndex == columnIndex) { if (caretIndicator) Sprite(caretIndicator).graphics.clear(); caretItemRenderer = null; caretUID = null; } if (item is IFlexDisplayObject) { if (item is IInvalidating) { IInvalidating(item).invalidateDisplayList(); IInvalidating(item).validateNow(); } } else if (item is IUITextField) { IUITextField(item).validateNow(); } var rowIndex:int = rowMap[item.name].rowIndex; var optimumColumns:Array = getOptimumColumns(); var n:int = optimumColumns.length; for (var i:int = 0; i < n; i++) { var r:IListItemRenderer = listItems[rowIndex][i]; updateDisplayOfItemRenderer(r); } } /** * Sets up the effect for applying the selection indicator. * The default is a basic alpha tween. * * @param indicator A Sprite that contains the graphics depicting selection. * * @param uid The UID of the item being selected which can be used to index * into a table and track more than one selection effect. * * @param columnIndex The column index of the selected cell. * * @param itemRenderer The item renderer that is being shown as selected. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function applyCellSelectionEffect(indicator:Sprite, uid:String, columnIndex:int, itemRenderer:IListItemRenderer):void { var selectionDuration:Number = getStyle("selectionDuration"); if (selectionDuration != 0) { indicator.alpha = 0; if (!cellSelectionTweens[uid]) cellSelectionTweens[uid] = {}; cellSelectionTweens[uid][columnIndex.toString()] = new Tween(indicator, 0, 1, selectionDuration, 5); cellSelectionTweens[uid][columnIndex.toString()].addEventListener( TweenEvent.TWEEN_UPDATE, selectionTween_updateHandler); cellSelectionTweens[uid][columnIndex.toString()].addEventListener( TweenEvent.TWEEN_END, selectionTween_endHandler); cellSelectionTweens[uid][columnIndex.toString()].setTweenHandlers( onSelectionTweenUpdate, onSelectionTweenUpdate); var selectionEasingFunction:Function = getStyle("selectionEasingFunction") as Function; if (selectionEasingFunction != null) cellSelectionTweens[uid][columnIndex.toString()].easingFunction = selectionEasingFunction; } } /** * @private */ protected function removeCellIndicators(uid:String, columnIndex:int):void { if (cellSelectionTweens[uid] && cellSelectionTweens[uid][columnIndex.toString()]) { cellSelectionTweens[uid][columnIndex.toString()].removeEventListener( TweenEvent.TWEEN_UPDATE, selectionTween_updateHandler); cellSelectionTweens[uid][columnIndex.toString()].removeEventListener( TweenEvent.TWEEN_END, selectionTween_endHandler); if (cellSelectionIndicators[uid][columnIndex.toString()].alpha < 1) Tween.removeTween(cellSelectionTweens[uid][columnIndex.toString()]); delete cellSelectionTweens[uid][columnIndex.toString()]; if (!atLeastOneProperty(cellSelectionTweens[uid])) delete cellSelectionTweens[uid]; } // toss associated graphics if needed if (cellSelectionIndicators[uid] && cellSelectionIndicators[uid][columnIndex.toString()]) { cellSelectionIndicators[uid][columnIndex.toString()].parent.removeChild(cellSelectionIndicators[uid][columnIndex.toString()]); delete cellSelectionIndicators[uid][columnIndex.toString()]; if (!atLeastOneProperty(cellSelectionIndicators[uid])) delete cellSelectionIndicators[uid]; } if (uid == highlightUID && columnIndex == highlightColumnIndex) { lastHighlightItemRenderer = highlightItemRenderer; highlightItemRenderer = null; highlightUID = null; if (highlightIndicator) Sprite(highlightIndicator).graphics.clear(); } if (uid == caretUID && columnIndex == caretColumnIndex) { caretItemRenderer = null; caretUID = null; if (caretIndicator) Sprite(caretIndicator).graphics.clear(); } } /** * @inheritDoc mx.controls.listClasses.ListBase#clearIndicators() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ override protected function clearIndicators():void { if (isCellSelectionMode()) clearCellIndicators(); super.clearIndicators(); } /** * @private */ protected function clearCellIndicators():void { for (var p:String in cellSelectionIndicators) { for (var q:String in cellSelectionIndicators[p]) { removeCellIndicators(p, int(q)); } } cellSelectionTweens = {}; cellSelectionIndicators = {}; visibleCellRenderers = {}; } /** * @private * Set selectedCells and update display accordingly. */ mx_internal function setSelectedCells(value:Array):void { var isFirstCell:Boolean = true; var n:int = value.length; for (var i:int = 0; i < n; i++) { var cell:Object = value[i]; if (isCellAlreadySelected(cell.rowIndex, cell.columnIndex)) continue; var visibleCoords:Object = absoluteToVisibleIndices(value[i].rowIndex, value[i].columnIndex); var visibleRowIndex:int = visibleCoords.rowIndex; var visibleColIndex:int = visibleCoords.columnIndex; var listItem:IListItemRenderer; if (listItems[visibleRowIndex] && listItems[visibleRowIndex][visibleColIndex]) { listItem = listItems[visibleRowIndex][visibleColIndex]; } else { addToSelectedCells(value[i].rowIndex, value[i].columnIndex); pendingCellSelection.addItem(value[i]); cellsWaitingToBeDisplayed = true; continue; } if (isFirstCell) { // Replaces selectedCells with only this cell location selectCellItem(listItem, false, false); isFirstCell = false; } else { // Add to selectedCells selectCellItem(listItem, false, true); // ctrl-key => multiple selection } } } /** * @private * Update the selectedCells array based on the changes in non-cell * selection modes. */ protected function updateSelectedCells():void { switch(selectionMode) { case NONE: { if (_selectedCells) _selectedCells = null; break; } case SINGLE_ROW: { // When in row selection mode, selectedCells is updated // along with selectedItems, selectedIndices as well _selectedCells = [ { rowIndex : selectedIndex, columnIndex : -1 } ]; break; } case MULTIPLE_ROWS: { // When in row selection mode, selectedCells is updated // along with selectedItems, selectedIndices as well _selectedCells = selectedIndices.map(rowToCell); break; } } syncCellSelections(_selectedCells); } /** * @private */ private function syncCellSelections(cells:Array):void { if (!cells) { cellSelections = null; } else { cellSelections = {}; for each (var cell:Object in cells) { cellSelections[[cell.rowIndex, cell.columnIndex]] = cell; } } } /** * @private */ private function rowToCell(item:*, index:int, array:Array):Object { return { rowIndex : item, columnIndex : -1 }; } /** * @private */ private function getAdjustedLockedCount(index:int):int { if(visibleHeaderInfos) { index = Math.min(index, visibleHeaderInfos.length); if(visibleHeaderInfos[ index - 1]) return visibleHeaderInfos[index-1].actualColNum + visibleHeaderInfos[index-1].columnSpan; } return 0; } /** * Get the length of the header items. Length calculation is different * in case grouped columns are specified. * @private */ override protected function getHeaderItemsLength():int { if (!columnGrouping) return super.getHeaderItemsLength(); if (headerItemsList) return headerItemsList.length; return 0; } //-------------------------------------------------------------------------- // // Overridden event handlers // //-------------------------------------------------------------------------- /** * @private * Catches any events from the model. Optimized for editing one item. * Creates columns when there are none. Inherited from list. * @param eventObj */ override protected function collectionChangeHandler(event:Event):void { if (event is CollectionEvent) { var ceEvent:CollectionEvent = CollectionEvent(event) if (ceEvent.kind == CollectionEventKind.RESET) { // if the data is grouped, update the groupLabelField. if (_rootModel is IGroupingCollection && IGroupingCollection(_rootModel).grouping) groupLabelField = IGroupingCollection(_rootModel).grouping.label; if (_rootModel is IGroupingCollection2 && IGroupingCollection2(_rootModel).grouping) groupLabelField = IGroupingCollection2(_rootModel).grouping.label; // if collection is reset, then expand the items if displayItemsExpanded is true if (displayItemsExpanded) expandAll(); } if (ceEvent.kind == CollectionEventKind.REFRESH) { // if collection is reset, then expand the items if displayItemsExpanded is true if (displayItemsExpanded) expandAll(); } } super.collectionChangeHandler(event); if (event is CollectionEvent) { var ce:CollectionEvent = CollectionEvent(event); if (ce.kind == CollectionEventKind.ADD) { if (displayItemsExpanded) { // when children are added, display them expanded var n:int = ce.items.length; for (var i:int = 0; i < n; i++) { expandChildrenOf(ce.items[i],true); } } } } } /** * @private */ override protected function headerNavigationHandler(event:KeyboardEvent):void { // If rtl layout, need to swap LEFT and RIGHT so correct action // is done. var keyCode:uint = mapKeycodeForLayoutDirection(event); if (!columnGrouping || headerIndex == -1 || selectedHeaderInfo == null) { super.headerNavigationHandler(event); } else { if (headerIndex == -1 || selectedHeaderInfo == null) return; var newSelectedHeaderInfo:AdvancedDataGridHeaderInfo; var siblings:Array; if (keyCode == Keyboard.UP) { if (selectedHeaderInfo.parent != null) { newSelectedHeaderInfo = selectedHeaderInfo.parent; unselectColumnHeader(headerIndex); selectedHeaderInfo = newSelectedHeaderInfo; headerIndex = newSelectedHeaderInfo.index; selectColumnGroupHeader(newSelectedHeaderInfo); } } else if (keyCode == Keyboard.DOWN) { //Check if we are a leaf-header if (selectedHeaderInfo.visibleChildren == null || selectedHeaderInfo.visibleChildren.length == 0) { unselectColumnHeader(selectedHeaderInfo.column.colNum, true); headerIndex = -1; } else { newSelectedHeaderInfo = selectedHeaderInfo.visibleChildren[0]; unselectColumnHeader(headerIndex); selectedHeaderInfo = newSelectedHeaderInfo; headerIndex = 0; selectColumnGroupHeader(newSelectedHeaderInfo); } } else if (keyCode == Keyboard.LEFT) { siblings = (selectedHeaderInfo.parent !=null)? selectedHeaderInfo.parent.visibleChildren : visibleHeaderInfos; if (selectedHeaderInfo.visibleIndex > 0) { newSelectedHeaderInfo = siblings[selectedHeaderInfo.visibleIndex-1]; scrollColumnGroupIfNeeded(selectedHeaderInfo, newSelectedHeaderInfo); unselectColumnHeader(headerIndex); selectedHeaderInfo = newSelectedHeaderInfo; headerIndex = newSelectedHeaderInfo.visibleIndex; selectColumnGroupHeader(newSelectedHeaderInfo); } } else if (keyCode == Keyboard.RIGHT) { siblings = (selectedHeaderInfo.parent !=null)? selectedHeaderInfo.parent.visibleChildren : visibleHeaderInfos; if (selectedHeaderInfo.visibleIndex < siblings.length - 1) { newSelectedHeaderInfo = siblings[selectedHeaderInfo.visibleIndex+1]; scrollColumnGroupIfNeeded(selectedHeaderInfo, newSelectedHeaderInfo); unselectColumnHeader(headerIndex); selectedHeaderInfo = newSelectedHeaderInfo; headerIndex = newSelectedHeaderInfo.visibleIndex; selectColumnGroupHeader(newSelectedHeaderInfo); } } else if (keyCode == Keyboard.SPACE) { if (sortableColumns && selectedHeaderInfo.column.colNum != -1 && !isNaN(selectedHeaderInfo.column.colNum) && columns[selectedHeaderInfo.column.colNum].sortable) { isKeyPressed = true; selectColumnHeader(selectedHeaderInfo.column.colNum); var advancedDataGridEvent:AdvancedDataGridEvent = new AdvancedDataGridEvent(AdvancedDataGridEvent.SORT, false, true); advancedDataGridEvent.multiColumnSort = event.ctrlKey; advancedDataGridEvent.removeColumnFromSort = event.shiftKey; advancedDataGridEvent.columnIndex = selectedHeaderInfo.column.colNum;//headerIndex; advancedDataGridEvent.dataField = selectedHeaderInfo.column.dataField; dispatchEvent(advancedDataGridEvent); } } // horizontal scrolling when focus is on header else if ( event.shiftKey && (keyCode == Keyboard.PAGE_UP || keyCode == Keyboard.PAGE_DOWN) ) { moveSelectionHorizontally(keyCode, event.shiftKey, event.ctrlKey); } // don't we allow vertical scrolling when focus is on the header? // if the focus is on header and we hit page-down/page-up no action would be taken // is this fine? event.stopPropagation(); } } /** * @private */ override protected function keyDownHandler(event:KeyboardEvent):void { // If tab pressed in editor, it'll skip here and go to the keyFocusChangeHandler. if (itemEditorInstance || event.target != event.currentTarget) return; if (isOpening) { event.stopImmediatePropagation(); return; } if (_rootModel is IHierarchicalData) // tree model/view { // If tree navigation couldn't handle the event, continue looking for a handler. if (treeNavigationHandler(event)) return; } if (isCellSelectionMode() && headerIndex == -1 && event.keyCode != Keyboard.ESCAPE) cellNavigationHandler(event); else super.keyDownHandler(event); if (isRowSelectionMode()) updateSelectedCells(); } /** * @private */ override protected function keyUpHandler(event:KeyboardEvent):void { if (isKeyPressed && columnGrouping) { isKeyPressed = false; if (selectedHeaderInfo != null) selectColumnHeader(selectedHeaderInfo.column.colNum); } else { super.keyUpHandler(event); } } /** * @private */ override protected function editorKeyDownHandler(event:KeyboardEvent):void { if (event.keyCode == Keyboard.F2) { // Save data and close editor endEdit(AdvancedDataGridEventReason.OTHER); setFocus(); invalidateDisplayList(); } super.editorKeyDownHandler(event); } /** * @private */ override protected function mouseOutHandler(event:MouseEvent):void { // To handle case when mouse is at the border of the header item // renderer. Ignoring prevents flickering of the highlight of the // header because of infinite loop of mouseOver and mouseOut events. if (event.target == listContent.getChildByName("headerBG")) return; if (tween) return; // mouse-out should remove the "proposed" numeric sort icon if (!sortExpertMode) { var item:IListItemRenderer = mouseEventToItemRenderer(event); if (item && item is IDropInListItemRenderer && isHeaderItemRenderer(item)) { var colNum:int = IDropInListItemRenderer(item).listData.columnIndex; delete visualSortInfo[colNum]; delete visualSortInfo["HEADERPART" + colNum]; invalidateRenderer(item); } } super.mouseOutHandler(event); } /** * @private */ override protected function mouseOverHandler(event:MouseEvent):void { if (tween) return; if (movingColumn) return; if (!enabled || !selectable) return; var item:IListItemRenderer = mouseEventToItemRenderer(event); if (!sortExpertMode) { // Mouseover should display a "proposed" numeric sort icon var dropInItem:IDropInListItemRenderer; if (item) dropInItem = item as IDropInListItemRenderer; else // we're no longer hovering over any headers, so remove the proposed sorts visualSortInfo = new Dictionary(); var colNum:int; if (dropInItem && dropInItem.listData) colNum = dropInItem.listData.columnIndex; if (dropInItem && isHeaderItemRenderer(item) && sortableColumns && colNum != -1 && columns[colNum].sortable && Object(item).hasOwnProperty("mouseEventToHeaderPart") ) { var headerPart:String = Object(item).mouseEventToHeaderPart(event); if (!visualSortInfo[colNum] || visualSortInfo["HEADERPART" + colNum] != headerPart) { if (headerPart == HEADER_TEXT_PART) { visualSortInfo[colNum] = new SortInfo(1, false, SortInfo.PROPOSEDSORT); invalidateRenderer(item); visualSortInfo["HEADERPART" + colNum] = headerPart; } else if (headerPart == HEADER_ICON_PART) { var sequenceNumber:int; var descending:Boolean; var sortInfo:SortInfo = getFieldSortInfo(columns[colNum]); if (sortInfo) { sequenceNumber = sortInfo.sequenceNumber; // we want to show the proposed sort // which will always be opposite of the present sort descending = !sortInfo.descending; } else if (collection && collection.sort && collection.sort.fields && collection.sort.fields.length) { sequenceNumber = collection.sort.fields.length + 1; descending = false; } else { sequenceNumber = 1; descending = false; } visualSortInfo[colNum] = new SortInfo(sequenceNumber, descending, SortInfo.PROPOSEDSORT); invalidateRenderer(item); visualSortInfo["HEADERPART" + colNum] = headerPart; } } } } if (item && !isHeaderItemRenderer(item) && isCellSelectionMode()) { // Copied from ListBase.mouseOverHandler() var evt:ListEvent; var pt:Point = itemRendererToIndices(item); if (!pt) return; isPressed = event.buttonDown; var columnIndex:int = displayToAbsoluteColumnIndex(pt.x); if (!isPressed || allowDragSelection) { if (getStyle("useRollOver") && (item.data != null)) { if (allowDragSelection) bSelectOnRelease = true; var uid:String = itemToUID(item.data); if (visibleCellRenderers[uid]) drawCellItem(visibleCellRenderers[uid][columnIndex.toString()], cellSelectionData[uid] && cellSelectionData[uid][columnIndex.toString()], true, uid == caretUID && columnIndex == caretColumnIndex); if (pt) // during tweens, we may get null { evt = new ListEvent(ListEvent.ITEM_ROLL_OVER); evt.columnIndex = pt.x; evt.rowIndex = pt.y; evt.itemRenderer = item; dispatchEvent(evt); } } } else { if (DragManager.isDragging) return; if ((/* dragScrollingInterval != 0 && */ allowDragSelection) || menuSelectionMode) { if (selectCellItem(item, event.shiftKey, event.ctrlKey)) { evt = new ListEvent(ListEvent.CHANGE); evt.itemRenderer = item; if (pt) { evt.columnIndex = pt.x; evt.rowIndex = pt.y; } dispatchEvent(evt); } } } } else { super.mouseOverHandler(event); } } /** * @private */ override protected function sortHandler(event:AdvancedDataGridEvent):void { if (event.isDefaultPrevented()) return; if (!sortableColumns || isNaN(event.columnIndex) || event.columnIndex == -1 || !columns[event.columnIndex].sortable) return; if(columnGrouping) { var columnNumber:int = event.columnIndex; //In case it is a column number, skip, we can't sort a column group if(isNaN(columnNumber) || columnNumber == -1) return; var headerInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(columns[columnNumber]); //If this is a leaf column and doesn't get data directly from the data row, but through internalLabelFunction if(headerInfo.internalLabelFunction != null) event.dataField = null; } // If navigating header via keyboard, and you mouse click on some // other header to sort it, then move the keyboard navigation focus // to that column header. if (columnGrouping && selectedHeaderInfo != null) selectedHeaderInfo = getHeaderInfo(columns[event.columnIndex]); super.sortHandler(event); if (isRowSelectionMode()) updateSelectedCells(); } /** * @private */ override protected function headerReleaseHandler(event:AdvancedDataGridEvent):void { if (event.isDefaultPrevented()) return; if (itemEditorInstance) endEdit(AdvancedDataGridEventReason.OTHER); if (sortExpertMode) { super.headerReleaseHandler(event); return; } var adgEvent:AdvancedDataGridEvent = new AdvancedDataGridEvent(AdvancedDataGridEvent.SORT, false, true); adgEvent.columnIndex = event.columnIndex; adgEvent.dataField = event.dataField; adgEvent.triggerEvent = event.triggerEvent; adgEvent.multiColumnSort = false; if (!collection || !collection.sort || !collection.sort.fields || !collection.sort.fields.length) // first sort { dispatchEvent(adgEvent); return; } if (event.headerPart == HEADER_TEXT_PART) { dispatchEvent(adgEvent); } else if (event.headerPart == HEADER_ICON_PART) { adgEvent.multiColumnSort = true; dispatchEvent(adgEvent); } } /** * @private * Blocks mouse events on items that are tweening away and are invalid for input */ override protected function mouseClickHandler(event:MouseEvent):void { if (!tween) super.mouseClickHandler(event); } /** * @private * Blocks mouse events on items that are tweening away and are invalid for input */ override protected function mouseDoubleClickHandler(event:MouseEvent):void { if (!tween) super.mouseDoubleClickHandler(event); } /** * @private */ override protected function mouseDownHandler(event:MouseEvent):void { if (!tween) { super.mouseDownHandler(event); updateSelectedCells(); } } /** * @private * Blocks mouse events on items that are tweening away and are invalid for input */ override protected function mouseUpHandler(event:MouseEvent):void { if (!tween) super.mouseUpHandler(event); } /** * @private * Blocks mouse wheel handling while tween is running */ override protected function mouseWheelHandler(event:MouseEvent):void { if (!tween) super.mouseWheelHandler(event); } //-------------------------------------------------------------------------- // // Event handlers // //-------------------------------------------------------------------------- /** * @private */ mx_internal function expandItemHandler(event:AdvancedDataGridEvent):void { if (event.isDefaultPrevented()) return; if (event.type == AdvancedDataGridEvent.ITEM_OPENING) { // store the item renderer // it is needed while dispatching ITEM_OPEN/ ITEM_CLOSE event eventItemRenderer = event.itemRenderer; expandItem(event.item, event.opening, event.animate, event.dispatchEvent, event.triggerEvent); } } /** * @private */ private function updateCompleteHandler(event:FlexEvent):void { // Are we running inside Flex Builder design view? Then set hard-coded data. if (UIComponentGlobals.designTime) { if (!dataProvider || ((dataProvider is ArrayCollection && !ArrayCollection(dataProvider).length) || (dataProvider is IHierarchicalCollectionView && !IHierarchicalCollectionView(dataProvider).length))) { setDesignViewData(); } if (designViewDataType == designViewDataTreeType && dataProvider && dataProvider is IHierarchicalCollectionView && (designViewDataFlag || !atLeastOneProperty(IHierarchicalCollectionView(collection).openNodes)) ) // set if openNodes is empty { // Open the first branch so that the designer can see // how leaf nodes look like as well IHierarchicalCollectionView(collection).openNodes = IHierarchicalCollectionView(dataProvider).createCursor().current; invalidateProperties(); invalidateDisplayList(); designViewDataFlag = false; } if (designViewDataTypeChanged) { designViewDataTypeChanged = false; // Make sure we do this to avoid infinite loop setDesignViewSort(); } } } /** * Handler for keyboard navigation for the navigation tree. * * @param event The keyboard event. * * @return true if the keyboard navigation is handled correctly. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function treeNavigationHandler(event:KeyboardEvent):Boolean { var evt:ListEvent; var pt:Point; // In cell selection mode, caretColumnIndex is valid. // In row selection mode, caretColumnIndex is -1, so we should use 0 since the // branch node item renderer is in the zeroeth column always. var visibleCoords:Object = absoluteToVisibleIndices(caretIndex, caretColumnIndex != -1 ? caretColumnIndex : 0); var visibleRowIndex:int = visibleCoords.rowIndex; var visibleColIndex:int = visibleCoords.columnIndex; var item:Object; if (listItems[visibleRowIndex] && listItems[visibleRowIndex][visibleColIndex]) item = listItems[visibleRowIndex][visibleColIndex].data; if (!item) return false; var treeItemRenderer:IListItemRenderer; // get the item renderer to be passed in the ITEM_OPENING events if (listItems[visibleRowIndex]) treeItemRenderer = listItems[visibleRowIndex][treeColumnIndex]; // If rtl layout, need to swap LEFT and RIGHT so correct action // is done. var keyCode:uint = mapKeycodeForLayoutDirection(event); if (event.ctrlKey && event.shiftKey && keyCode == Keyboard.SPACE) { // if user has moved the caret cursor from the selected item // move the cursor back to selected item if (caretIndex != selectedIndex) { // erase the caret var renderer:IListItemRenderer = indexToItemRenderer(caretIndex); if (renderer) drawItem(renderer); caretIndex = selectedIndex; } // Spacebar toggles the current open/closed status. No effect for leaf items. if (isBranch(item)) { var o:Boolean = !isItemOpen(item); dispatchAdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_OPENING, item, //item treeItemRenderer, //renderer event, //trigger o, //opening true, //animate true); //dispatch } event.stopImmediatePropagation(); } else if (event.ctrlKey && event.shiftKey && keyCode == Keyboard.LEFT) { // Left Arrow closes an open item. Otherwise selects the parent item. if (isItemOpen(item)) { dispatchAdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_OPENING, item, //item treeItemRenderer, //renderer event, //trigger false, //opening true, //animate true) //dispatch } else { selectedItem = getParentItem(item); evt = new ListEvent(ListEvent.CHANGE); evt.itemRenderer = indexToItemRenderer(selectedIndex); pt = itemRendererToIndices(evt.itemRenderer); if (pt) { evt.rowIndex = pt.y; evt.columnIndex = pt.x; } dispatchEvent(evt); var dI:int = getItemIndex(selectedItem); if (dI != caretIndex) { caretIndex = selectedIndex; } if (dI < _verticalScrollPosition) { verticalScrollPosition = dI; } } event.stopImmediatePropagation(); } else if (event.ctrlKey && event.shiftKey && keyCode == Keyboard.RIGHT) { // Right Arrow has no effect on leaf items. Closed branch items are opened. //Opened branch items select the first child. if (isBranch(item)) { if (isItemOpen(item)) { if (item) { var children:ICollectionView = getChildren(item, iterator.view); if (children) { var cursor:IViewCursor = children.createCursor(); selectedItem = cursor.current; } } else { selectedItem = null; } if (caretIndex != selectedIndex) caretIndex = getItemIndex(selectedItem); evt = new ListEvent(ListEvent.CHANGE); evt.itemRenderer = indexToItemRenderer(selectedIndex); pt = itemRendererToIndices(evt.itemRenderer); if (pt) { evt.rowIndex = pt.y; evt.columnIndex = pt.x; } dispatchEvent(evt); } else { dispatchAdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_OPENING, item, //item treeItemRenderer, //renderer event, //trigger true, //opening true, //animate true); //dispatch } } event.stopImmediatePropagation(); } else if (keyCode == Keyboard.NUMPAD_MULTIPLY) { expandChildrenOf(item, !isItemOpen(item)); } else if (keyCode == Keyboard.NUMPAD_ADD) { if (isBranch(item)) { if (!isItemOpen(item)) { dispatchAdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_OPENING, item, //item treeItemRenderer, //renderer event, //trigger true, //opening true, //animate true); //dispatch } } } else if (keyCode == Keyboard.NUMPAD_SUBTRACT) { if (isItemOpen(item)) { dispatchAdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_OPENING, item, //item treeItemRenderer, //renderer event, //trigger false, //opening true, //animate true); //dispatch } } else { return false; } return true; } /** * @private * Handle keyboard navigation in cell selection mode * i.e. selectionMode == SINGLE_CELL || selectionMode == MULTIPLE_CELLS */ protected function cellNavigationHandler(event:KeyboardEvent):void { // If rtl layout, need to swap LEFT and RIGHT so correct action // is done. var keyCode:uint = mapKeycodeForLayoutDirection(event); // Can't use a straight-forward Object/Dictionary // because they don't allow integer keys. const validKeys:Array = [ Keyboard.UP, Keyboard.DOWN, Keyboard.LEFT, Keyboard.RIGHT, Keyboard.SPACE, Keyboard.HOME, Keyboard.END, Keyboard.PAGE_UP, Keyboard.PAGE_DOWN, Keyboard.F2 ]; var isValidKey:Boolean = false; var n:int = validKeys.length; for (var i:int = 0; i < n; i++) { if (keyCode == validKeys[i]) { isValidKey = true; break; } } if (!isValidKey) { // Handles single-character type-ahead lookup for data in the rows. if (findKey(keyCode)) event.stopPropagation(); return; } var bUpdateHorizontalScrollPosition:Boolean = false; var bUpdateVerticalScrollPosition:Boolean = false; var newVerticalScrollPosition:Number; var newHorizontalScrollPosition:Number; var maxPosition:int; var rowCount:int = listItems.length; if (rowCount == 0) return; var partialRow:int = 0; if (rowInfo[rowCount - 1].y + rowInfo[rowCount - 1].height > listContent.height) { partialRow++; } showCaret = true; bSelectItem = false; var newCaretColumnIndex:int; switch (keyCode) { case Keyboard.UP: { if (caretIndex == 0 && selectedIndex == 0 && headerVisible && !event.ctrlKey && !event.shiftKey) // move to header { var oldCaretColumnIndex:int = caretColumnIndex; clearSelectedCells(); moveFocusToHeader(oldCaretColumnIndex); } else if (caretIndex > 0) { // if the first column of the column span is hidden from view, // then we move to the first visible column if (visibleColumns.length > 0 && visibleColumns[0].colNum > caretColumnIndex) caretColumnIndex = visibleColumns[0].colNum; caretIndex--; bUpdateVerticalScrollPosition = true; bSelectItem = true; } break; } case Keyboard.DOWN: { if (caretIndex < collection.length - 1) { caretIndex++; // When focus is on ADG and down arrow is pressed, focus // should go to the first visible cell if (caretIndex == 0) caretColumnIndex = 0; // if the first column of the column span is hidden from view, // then we move to the first visible column else if (visibleColumns.length > 0 && visibleColumns[0].colNum > caretColumnIndex) caretColumnIndex = visibleColumns[0].colNum; bUpdateVerticalScrollPosition = true; bSelectItem = true; } else if ( (caretIndex == collection.length - 1) && partialRow ) { if (verticalScrollPosition < maxVerticalScrollPosition) newVerticalScrollPosition = verticalScrollPosition + 1; } break; } case Keyboard.LEFT: { newCaretColumnIndex = viewDisplayableColumnAtOffset(caretColumnIndex, -1, caretIndex); if (newCaretColumnIndex != -1) { caretColumnIndex = newCaretColumnIndex; bSelectItem = true; } break; } case Keyboard.RIGHT: { newCaretColumnIndex = viewDisplayableColumnAtOffset(caretColumnIndex, +1, caretIndex); if (newCaretColumnIndex != -1) { caretColumnIndex = newCaretColumnIndex; bSelectItem = true; } break; } case Keyboard.SPACE: { // ctrl-space selects (and unselects) item bSelectItem = event.ctrlKey; break; } case Keyboard.HOME: case Keyboard.END: case Keyboard.PAGE_UP: case Keyboard.PAGE_DOWN: { moveSelectionVertically(keyCode, event.shiftKey, event.ctrlKey); break; } case Keyboard.F2: { if (caretColumnIndex != -1) { var newData:Object = rowNumberToData(caretIndex); if (newData && isDataEditable(newData) && displayableColumns[caretColumnIndex].editable) { // Open editor var advancedDataGridEvent:AdvancedDataGridEvent = new AdvancedDataGridEvent( AdvancedDataGridEvent.ITEM_EDIT_BEGINNING, false, true); // ITEM_EDIT events are cancelable advancedDataGridEvent.columnIndex = caretColumnIndex; advancedDataGridEvent.dataField = columns[caretColumnIndex].dataField; advancedDataGridEvent.rowIndex = caretIndex; var visibleCoords:Object = absoluteToVisibleIndices(caretIndex, caretColumnIndex); var visibleRowIndex:int = visibleCoords.rowIndex; var visibleColIndex:int = visibleCoords.columnIndex; advancedDataGridEvent.itemRenderer = listItems[visibleRowIndex][visibleColIndex]; dispatchEvent(advancedDataGridEvent); } } break; } } event.stopPropagation(); if (bUpdateVerticalScrollPosition) { if (caretIndex < lockedRowCount) { newVerticalScrollPosition = 0; } else if (caretIndex < verticalScrollPosition + lockedRowCount) { newVerticalScrollPosition = caretIndex - lockedRowCount; } else if (caretIndex >= verticalScrollPosition + rowCount - partialRow) { newVerticalScrollPosition = Math.min(maxVerticalScrollPosition, caretIndex - rowCount + partialRow + 1); } if (!isNaN(newVerticalScrollPosition)) { if (verticalScrollPosition != newVerticalScrollPosition) { var se:ScrollEvent = new ScrollEvent(ScrollEvent.SCROLL); se.detail = ScrollEventDetail.THUMB_POSITION; se.direction = ScrollEventDirection.VERTICAL; se.delta = newVerticalScrollPosition - verticalScrollPosition; se.position = newVerticalScrollPosition; verticalScrollPosition = newVerticalScrollPosition; dispatchEvent(se); } // bail if we page faulted if (!iteratorValid) { keySelectionPending = true; return; } } } bShiftKey = event.shiftKey; bCtrlKey = event.ctrlKey; lastKey = keyCode; finishCellKeySelection(); } } }