blob: fe4b9819a0d8c67670e90dd19eada74e90421344 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package mx.controls
{
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.HierarchicalData;
import mx.collections.ICollectionView;
import mx.collections.IGroupingCollection;
import mx.collections.IGroupingCollection2;
import mx.collections.IHierarchicalCollectionView;
import mx.collections.IHierarchicalCollectionViewCursor;
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 <code>backgroundColor</code> 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 <code>TreeNodeIcon</code> 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 <code>TreeDisclosureOpen</code> 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 <code>TreeDisclosureClosed</code> 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 <code>TreeFolderOpen</code> 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 <code>TreeFolderClosed</code> 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.
*
* <p>The default value is <code>undefined</code>.</p>
*
* @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 <code>easingFunction</code> function used for controlling programmatic tweening.
*
* <p>The default value is <code>undefined</code>.</p>
*
* @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 Apache Flex application.
* These features provide greater control of data display, data aggregation, and data formatting.
*
* The <code>AdvancedDataGrid</code> 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.
* <p>
* The AdvancedDataGrid control provides the following features:
* <ul>
* <li>Columns of different widths or identical fixed widths.</li>
* <li>Columns that the user can resize at run time. </li>
* <li>Columns that the user can reorder at run time. </li>
* <li>Optional customizable column headers.</li>
* <li>Ability to use a custom item renderer for any column to display data
* other than text.</li>
* <li>Support for sorting the data by clicking on a column.</li>
* </ul>
* </p>
* 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
* <p>
* The <code>&lt;mx:AdvancedDataGrid&gt;</code> tag inherits all of the tag attributes
* of its superclass, except for <code>labelField</code>, <code>iconField</code>,
* and <code>iconFunction</code>, and adds the following tag attributes:
* </p>
* <pre>
* &lt;mx:AdvancedDataGrid
* <b>Properties</b>
* 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"
*
* <b>Styles</b>
* 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"
*
* <b>Events</b>
* headerDragOutside="<i>No default</i>"
* headerDropOutside="<i>No default</i>"
* itemClose="<i>No default</i>"
* itemOpen="<i>No default</i>"
* itemOpening="<i>No default</i>"
* /&gt;
*
* <b><i>The following AdvancedDataGrid code sample specifies the column order:</i></b>
* &lt;mx:AdvancedDataGrid&gt;
* &lt;mx:dataProvider&gt;
* &lt;mx:Object Artist="Pavement" Price="11.99"
* Album="Slanted and Enchanted"/&gt;
* &lt;mx:Object Artist="Pavement"
* Album="Brighten the Corners" Price="11.99"/&gt;
* &lt;/mx:dataProvider&gt;
* &lt;mx:columns&gt;
* &lt;mx:AdvancedDataGridColumn dataField="Album"/&gt;
* &lt;mx:AdvancedDataGridColumn dataField="Price"/&gt;
* &lt;/mx:columns&gt;
* &lt;/mx:AdvancedDataGrid&gt;
* </pre>
* </p>
*
* @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 <code>AdvancedDataGridHeaderRenderer.mouseEventToHeaderPart</code> 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
//
//--------------------------------------------------------------------------
/**
* Maps renders to row and column spanning info.
*
* @see mx.controls.advancedDataGridClasses.AdvancedDataGridRendererDescription
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 4.11
*/
protected 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 <code>selectedIndex</code> 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 <code>caretIndex</code> 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 <code>ListBase.anchorIndex</code> 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.
*
* <p>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.</p>
*
* @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 <code>columns</code> and <code>groupedColumns</code> properties,
* the control uses the <code>groupedColumns</code> property and ignores
* the <code>columns</code> 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 <code>selectionMode</code> property
* sets this property to <code>null</code>.
*
* <p>Note that these row and column indices are absolute values,
* irrespective of the display. This is explained by a typical
* <code>selectedCells</code> property setting as follows:</p>
*
* <pre>
* selectedCells = [ { rowIndex : r1, columnIndex : c1 },
* { rowIndex : r2, columnIndex : c2 },
* ... ]</pre>
*
* <p>Then, dataProvider[r1], columns[c1], dataProvider[r2], columns[c2],
* etc. will always be valid.</p>
*
* <p>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 <code>selectedCells</code>
* property. This is because the AdvancedDataGrid control returns a new
* copy of the Array of selectedCells and therefore does not notice the
* changes.</p>
*
* <p>The value of the <code>selectionMode</code> property determines the data in
* the <code>rowIndex</code> and <code>columnIndex</code> properties, as the following table describes:</p>
*
* <table class="innertable">
* <tr>
* <th><code>selectionMode</code></th>
* <th>Value of <code>rowIndex</code> and <code>columnIndex</code> properties</th>
* </tr>
* <tr>
* <td>none</td>
* <td>No selection allowed in the control, and <code>selectedCells</code> is null. </td>
* </tr>
* <tr>
* <td><code>singleRow</code> </td>
* <td>Click any cell in the row to select the row. After the selection,
* <code>selectedCells</code> contains a single Object:
* <p>[{rowIndex:selectedRowIndex, columnIndex: -1}]</p></td>
* </tr>
* <tr>
* <td><code>multipleRows</code></td>
* <td>Click 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, <code>selectedCells</code> contains one Object for each selected row:
* <p>[ {rowIndex: selectedRowIndex1, columnIndex: -1},
* {rowIndex: selectedRowIndex2, columnIndex: -1},
* ...
* {rowIndex: selectedRowIndexN, columnIndex: -1}
* ] </p></td>
* </tr>
* <tr>
* <td><code>singleCell</code></td>
* <td>Click any cell to select the cell.
* After the selection, <code>selectedCells</code> contains a single Object:
* <p>[{rowIndex: selectedRowIndex, columnIndex:selectedColIndex}] </p></td>
* </tr>
* <tr>
* <td><code>multipleCells</code></td>
* <td>Click 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, <code>selectedCells</code> contains one Object for each selected cell:
* <p>[ {rowIndex: selectedRowIndex1, columnIndex: selectedColIndex1},
* {rowIndex: selectedRowIndex2, columnIndex: selectedColIndex2},
* ...
* {rowIndex: selectedRowIndexN, columnIndex: selectedColIndexN}
* ] </p></td>
* </tr>
* </table>
*
* @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);
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 <code>itemIcons</code> or <code>setItemIcon</code> properties
* if you have predetermined icons for data items.
* Use this callback function to determine the icon dynamically after examining the data.
*
* <p>The <code>groupIconFunction</code> takes the item in the data provider
* and its depth and returns a Class defining the icon, or <code>null</code> property
* to use the default icon.
* The callback function must have the following signature:</p>
*
* <pre>
* groupIconFunction(item:Object,depth:int):Class</pre>
*
* @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 <code>label</code>
* on each data provider item and displays it.
* However, some data sets do not have a <code>label</code> 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.
*
* <p>You can supply a <code>groupLabelFunction</code> that finds the
* appropriate fields and returns a displayable string. The
* <code>groupLabelFunction</code> is also good for handling formatting and
* localization. </p>
*
* <p>The method signature for the AdvancedDataGrid
* and AdvancedDataGridColumn classes is:</p>
* <pre>
* myGroupLabelFunction(item:Object, column:AdvancedDataGridColumn):String</pre>
*
* <p>Where <code>item</code> contains the AdvancedDataGrid item object, and
* <code>column</code> specifies the AdvancedDataGrid column.
* The function returns a String containing the label. </p>
*
* @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:
* <pre>
* {iconID: <i>Class</i>, iconID2: <i>Class</i>}
* </pre>
* The <code>iconID</code> field value is the class of the icon for
* a closed or leaf item and the <code>iconID2</code> is the class
* of the icon for an open item.
*
* <p>This property is intended to allow initialization of item icons.
* Changes to this array after initialization are not detected
* automatically.
* Use the <code>setItemIcon()</code> method to change icons dynamically.</p>
*
* @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 <code>verticalScrollPosition</code> of the List control.
*
* <p>If the item is not currently viewable (for example, because it
* is under a nonexpanded item), setting this property has no effect.</p>
*
* <p>The default value is the first item in the AdvancedDataGrid control.</p>
*
* @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.
*
* <p>The default value is an internal instance of the
* HierarchicalCollectionView class.</p>
*
* @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 <code>false</code>, 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 <code>true</code>, 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 <code>id</code> property of an
* AdvancedDataGridColumn instance, as the following example shows:
*
* <pre>
* &lt;mx:AdvancedDataGrid
* width="100%" height="100%"
* treeColumn="{rep}"&gt;
* &lt;mx:dataProvider&gt;
* &lt;mx:HierarchicalData source="{dpHierarchy}"
* childrenField="categories"/&gt;
* &lt;/mx:dataProvider&gt;
* &lt;mx:columns&gt;
* &lt;mx:AdvancedDataGridColumn dataField="Region"/&gt;
* &lt;mx:AdvancedDataGridColumn id="rep"
* dataField="Territory_Rep"
* headerText="Territory Rep"/&gt;
* &lt;mx:AdvancedDataGridColumn dataField="Actual"/&gt;
* &lt;mx:AdvancedDataGridColumn dataField="Estimate"/&gt;
* &lt;/mx:columns&gt;
* &lt;/mx:AdvancedDataGrid&gt;</pre>
*
* @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 <code>true</code> if the Shift key was held down when
* the mouse was clicked.
*
* @param ctrlKey <code>true</code> if the Control key was held down when
* the mouse was clicked.
*
* @param transition <code>true</code> if the graphics for the selected
* state should be faded in using an effect.
*
* @return <code>true</code> 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];
var bHighlightCell:Boolean = highlightUID == uid
&& highlightColumnIndex == i;
var bCaretCell:Boolean = caretUID == uid
&& caretColumnIndex == i;
drawCellItem(visibleCellRenderers[uid][i],
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)] = 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 <code>listItems</code> 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();
columnsToInfo = new Dictionary();
headerInfos = initializeHeaderInfo(_columnsValue);
headerInfoInitialized = true;
super.columns = _columnsValue;
}
if (displayItemsExpandedChanged)
{
// 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);
}
// redraw the border
layoutChrome(unscaledWidth, unscaledHeight);
}
/**
* @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], actualRow);
}
else
{
drawRowBackground(rowBGs, i++, rowInfo[curRow].y, rowInfo[curRow].height, uint(color), actualRow);
}
}
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 <code>DragEvent.DRAG_DROP</code> event.
* This method hides
* the drop feedback by calling the <code>hideDropFeedback()</code> method.
*
* By default, only the <code>DragManager.MOVE</code> drag action is supported.
* To support the <code>DragManager.COPY</code>
* drag action, you must write an event handler for the
* <code>DragEvent.DRAG_DROP</code> 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 <code>DragEvent.DRAG_COMPLETE</code> event.
*
* By default, only the <code>DragManager.MOVE</code> drag action is supported.
* To support the <code>DragManager.COPY</code>
* drag action, you must write an event handler for the
* <code>DragEvent.DRAG_DROP</code> 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 columns
* configuration
*/
override protected function initializeHeaderInfo(columns:Array):Array
{
if (!columnGrouping)
return super.initializeHeaderInfo(columns);
else
return initializeGroupedHeaderInfo(columns, 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 IHierarchicalCollectionViewCursor && 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"
|| styleProp == "layoutDirection")
{
itemsSizeChanged = true;
rendererChanged = true;
invalidateProperties();
invalidateDisplayList();
}
}
/**
* @private
*/
override protected function removeIndicators(uid:String):void
{
if (isCellSelectionMode())
{
var keys:Array = [];
for (var p:String in cellSelectionIndicators[uid]) {
keys.push(p);
}
for each (p in keys) {
removeCellIndicators(uid, int(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 <code>true</code> if <code>offset</code> contains the new scroll position,
* and <code>false</code> 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 <code>true</code> 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
* <code>iconField</code> and <code>iconFunction</code> properties for
* this item if it is a leaf item. Branch items don't use the
* <code>iconField</code> and <code>iconFunction</code> properties.
* They use the <code>folderOpenIcon</code> and <code>folderClosedIcon</code> 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 <code>true</code> 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 <code>true</code> if open, and <code>false</code> 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.
*
* <p>If you set the <code>dataProvider</code> property and then immediately call
* the <code>expandItem()</code> method, you may not see the correct behavior.
* You should either wait for the component to validate
* or call <code>validateNow()</code>.</p>
*
* @param item An Object defining the branch node. This Object contains the
* data provider element for the branch node.
*
* @param open Specify <code>true</code> to open the branch node,
* and <code>false</code> to close it.
*
* @param animate Specify <code>true</code> 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 <code>open</code> event
* after the open animation is complete (<code>true</code>), or not (<code>false</code>).
*
* @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]);
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.
*
* <p>If you set the <code>dataProvider</code> property and then immediately call
* the <code>expandChildrenOf()</code> method, you may not see the correct behavior.
* You should either wait for the component to validate,
* or call the <code>validateNow()</code> method before calling <code>expandChildrenOf()</code>.</p>
*
* @param item An Object defining the branch node. This Object contains the
* data provider element for the branch node.
*
* @param open Specify <code>true</code> to open the items,
* and <code>false</code> 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 <code>null</code>.
*
* @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;
}
override protected 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"];
if (headerItem == null)
continue;
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;
}
}
// There is no point of keep looping over if x is not within range
if (!xMatched)
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];
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] != 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];
if (bShiftKey)
{
selectCellItem(listItem, bShiftKey, bCtrlKey);
bSelChanged = true;
}
else if (bCtrlKey)
{
drawCellItem(listItem,
cellSelectionData[uid]
&& cellSelectionData[uid][columnIndex] != 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.
*
* <p>This method also updates the display of the item renderers based on their updated
* selected state.</p>
*
* @param item The item renderer for the cell.
*
* @param shiftKey <code>true</code> to specify that the Shift key is pressed,
* and <code>false</code> if not.
*
* @param ctrlKey <code>true</code> to specify that the Control key is pressed,
* and <code>false</code> if not.
*
* @param transition Specify <code>true</code> to animate the transition.
*
* @return Returns <code>true</code> 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])
{
removeCellSelectionData(uid, columnIndex);
drawCellItem(visibleCellRenderers[uid][columnIndex], // 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], // 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]
&& visibleCellRenderers[uid][columnIndex].visible)
// Checking visibility to handle column spanning
{
drawCellItem(visibleCellRenderers[uid][columnIndex],
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])
{
drawCellItem(visibleCellRenderers[uid][columnIndex],
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
{
}
/**
* Determines if cell is highlighted.
*
* @param data The data provider item.
* @param columnIndex index of column.
*
* @return <code>true</code> if the cell item is highlighted.
*
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 4.11
*/
public function isCellItemHighlighted(data:Object, columnIndex:int): Boolean
{
if (data == null)
return false;
if (isCellItemSelected(data, columnIndex))
return false;
return highlightUID == data && highlightColumnIndex == columnIndex;
}
/**
* Determines if cell is selected.
*
* @param data The data provider item.
* @param columnIndex index of column.
*
* @return <code>true</code> if the cell item is selected.
*
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 4.11
*/
public function isCellItemSelected(data:Object, columnIndex:int): Boolean
{
if (data == null)
return false;
return cellSelectionData[data] && cellSelectionData[data][columnIndex];
}
/**
* @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] = 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];
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];
// Remove uid if there are no columns for that uid in cellSelectionData
if (!atLeastOneProperty(cellSelectionData[uid]))
delete cellSelectionData[uid];
}
/**
* Returns <code>true</code> 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 <code>true</code> 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 <code>selectedCells</code> property.
*
* @param transition Specify <code>true</code> 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)
{
// IMPORTANT! Clear the selection before drawCellItem() is called -> IInvalidating(item).validateNow()
cellSelectionData[p][q] = null;
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;
clearPendingCells();
}
/**
* @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])
{
o = new SpriteAsset();
o.mouseEnabled = false;
cellSelectionIndicators[rowData.uid][columnIndex] = o;
newIndicator = true;
}
o = cellSelectionIndicators[rowData.uid][columnIndex];
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];
if (rowIndicators && rowIndicators[columnIndex])
{
o = rowIndicators[columnIndex];
if (o.parent)
o.parent.removeChild(o);
delete rowIndicators[columnIndex];
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] =
new Tween(indicator, 0, 1, selectionDuration, 5);
cellSelectionTweens[uid][columnIndex].addEventListener(
TweenEvent.TWEEN_UPDATE,
selectionTween_updateHandler);
cellSelectionTweens[uid][columnIndex].addEventListener(
TweenEvent.TWEEN_END,
selectionTween_endHandler);
cellSelectionTweens[uid][columnIndex].setTweenHandlers(
onSelectionTweenUpdate,
onSelectionTweenUpdate);
var selectionEasingFunction:Function =
getStyle("selectionEasingFunction") as Function;
if (selectionEasingFunction != null)
cellSelectionTweens[uid][columnIndex].easingFunction
= selectionEasingFunction;
}
}
/**
* @private
*/
protected function removeCellIndicators(uid:String, columnIndex:int):void
{
if (cellSelectionTweens[uid] && cellSelectionTweens[uid][columnIndex])
{
cellSelectionTweens[uid][columnIndex].removeEventListener(
TweenEvent.TWEEN_UPDATE, selectionTween_updateHandler);
cellSelectionTweens[uid][columnIndex].removeEventListener(
TweenEvent.TWEEN_END, selectionTween_endHandler);
if (cellSelectionIndicators[uid][columnIndex].alpha < 1)
Tween.removeTween(cellSelectionTweens[uid][columnIndex]);
delete cellSelectionTweens[uid][columnIndex];
if (!atLeastOneProperty(cellSelectionTweens[uid]))
delete cellSelectionTweens[uid];
}
// toss associated graphics if needed
if (cellSelectionIndicators[uid] && cellSelectionIndicators[uid][columnIndex])
{
cellSelectionIndicators[uid][columnIndex].parent.removeChild(cellSelectionIndicators[uid][columnIndex]);
delete cellSelectionIndicators[uid][columnIndex];
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)
{
var keys:Array = [];
for (var q:String in cellSelectionIndicators[p]) {
keys.push(q);
}
for each (q in keys)
{
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;
if (!visibleColumns || !visibleColumns.length)
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],
cellSelectionData[uid] &&
cellSelectionData[uid][columnIndex],
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 && visibleColumns && visibleColumns.length > 0)
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 && visibleColumns && visibleColumns.length > 0)
super.mouseDoubleClickHandler(event);
}
/**
* @private
*/
override protected function mouseDownHandler(event:MouseEvent):void
{
if (!tween && visibleColumns && visibleColumns.length > 0)
{
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 && visibleColumns && visibleColumns.length > 0)
super.mouseUpHandler(event);
}
/**
* @private
* Blocks mouse wheel handling while tween is running
*/
override protected function mouseWheelHandler(event:MouseEvent):void
{
if (!tween && visibleColumns && visibleColumns.length > 0)
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 <code>true</code> 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();
}
}
}