| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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.advancedDataGridClasses |
| { |
| |
| import flash.display.DisplayObject; |
| import flash.display.Sprite; |
| import flash.events.Event; |
| import flash.events.MouseEvent; |
| import flash.geom.Point; |
| import flash.ui.Keyboard; |
| import flash.utils.Dictionary; |
| import flash.utils.getDefinitionByName; |
| |
| import mx.collections.ArrayCollection; |
| import mx.collections.CursorBookmark; |
| import mx.collections.ItemResponder; |
| import mx.collections.errors.ChildItemPendingError; |
| import mx.collections.errors.ItemPendingError; |
| import mx.controls.listClasses.AdvancedListBase; |
| import mx.controls.listClasses.AdvancedListBaseContentHolder; |
| 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.IFactory; |
| import mx.core.IFlexModuleFactory; |
| import mx.core.IFontContextComponent; |
| import mx.core.IInvalidating; |
| import mx.core.IUIComponent; |
| import mx.core.UIComponentGlobals; |
| import mx.core.mx_internal; |
| import mx.events.ListEvent; |
| import mx.events.ScrollEvent; |
| import mx.events.ScrollEventDetail; |
| import mx.events.ScrollEventDirection; |
| import mx.events.TweenEvent; |
| |
| use namespace mx_internal; |
| |
| //-------------------------------------- |
| // Excluded APIs |
| //-------------------------------------- |
| |
| [Exclude(name="maxHorizontalScrollPosition", kind="property")] |
| [Exclude(name="maxVerticalScrollPosition", kind="property")] |
| |
| /** |
| * The AdvancedDataGridBase class is the base class for controls |
| * that display lists of items in multiple columns, |
| * such as the AdvancedDataGrid and OLAPDataGrid controls. |
| * It is not used directly in applications. |
| * |
| * @mxml |
| * |
| * <p>The AdvancedDataGridBase class inherits all the properties of its parent classes |
| * and adds the following properties:</p> |
| * |
| * <pre> |
| * <mx:<i>tagname</i> |
| * headerHeight="depends on styles and header renderer" |
| * headerWordWrap="false|true" |
| * selectionMode="SINGLE_ROW" |
| * showHeaders="true|false" |
| * sortItemRenderer="null" |
| * styleFunction="null" |
| * /> |
| * </pre> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public class AdvancedDataGridBase extends AdvancedListBase implements IFontContextComponent |
| { |
| include "../../core/Version.as"; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Class constants |
| // |
| //-------------------------------------------------------------------------- |
| |
| // 'Cell Selection' constants |
| /** |
| * Constant definition for the <code>selectionMode</code> property. |
| * No selection is allowed in the control, |
| * and the <code>selectedCells</code> property is null. |
| * |
| * @see mx.controls.AdvancedDataGrid#selectedCells |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public static const NONE:String = "none"; |
| |
| /** |
| * Constant definition for the <code>selectionMode</code> property |
| * to allow the selection of a single row. |
| * Click any cell in the row to select the row. |
| * |
| * @see mx.controls.AdvancedDataGrid#selectedCells |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public static const SINGLE_ROW:String = "singleRow"; |
| |
| /** |
| * Constant definition for the <code>selectionMode</code> property |
| * to allow the selection of multiple rows. |
| * 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. |
| * |
| * @see mx.controls.AdvancedDataGrid#selectedCells |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public static const MULTIPLE_ROWS:String = "multipleRows"; |
| |
| /** |
| * Constant definition for the <code>selectionMode</code> property |
| * to allow the selection of a single cell. |
| * Click any cell to select the cell. |
| * |
| * @see mx.controls.AdvancedDataGrid#selectedCells |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public static const SINGLE_CELL:String = "singleCell"; |
| |
| /** |
| * Constant definition for the <code>selectionMode</code> property |
| * to allow the selection of multiple cells. |
| * Click any cell in the row to select the cell. |
| * While holding down the Control key, click any cell to select |
| * the cell for discontiguous selection. |
| * While holding down the Shift key, click any cell to select |
| * multiple, contiguous cells. |
| * |
| * @see mx.controls.AdvancedDataGrid#selectedCells |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public static const MULTIPLE_CELLS:String = "multipleCells"; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Constructor. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function AdvancedDataGridBase() |
| { |
| super(); |
| |
| listType = "vertical"; |
| |
| lockedRowCount = 0; |
| defaultRowCount = 7; // default number of rows is 7 |
| columnMap = {}; |
| freeItemRenderersTable = new Dictionary(false); |
| itemRendererToFactoryMap = new Dictionary(true); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var lockedRowCountResetShowHeaders:Boolean = false; |
| |
| /** |
| * The set of visible columns. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| mx_internal var visibleColumns:Array; |
| |
| /** |
| * The set sub content area holding the scrollable list items. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| mx_internal var listSubContent:AdvancedListBaseContentHolder; |
| |
| /** |
| * Flag specifying that the set of visible columns and/or their sizes needs to |
| * be recomputed. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| mx_internal var columnsInvalid:Boolean = true; |
| |
| /** |
| * @private |
| */ |
| mx_internal var _explicitHeaderHeight:Boolean; |
| |
| /** |
| * A map of item renderers to columns. |
| * Like <code>AdvancedListBase.rowMap</code>, this property contains |
| * a hash map of item renderers and the columns they belong to. |
| * Item renderers are indexed by their DisplayObject name. |
| * |
| * @see mx.controls.listClasses.ListBase#rowMap |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected var columnMap:Object; |
| |
| /** |
| * @private |
| * A per-factory table of unused item renderers. |
| * Most list classes recycle item renderers that they have already created |
| * as they scroll off screen. |
| * The recycled renderers are stored here. |
| * The table is a Dictionary where the entries are Arrays indexed |
| * by the actual factory used to create them (not the column's dataField or other |
| * properties), and each array is a stack of currently unused renderers |
| */ |
| protected var freeItemRenderersTable:Dictionary; |
| |
| /** |
| * @private |
| * Denotes if the child item is pending |
| */ |
| protected var itemPending:Boolean; |
| |
| // these three keep track of the key selection that caused |
| // the page fault |
| /** |
| * @private |
| */ |
| protected var bShiftKey:Boolean = false; |
| /** |
| * @private |
| */ |
| protected var bCtrlKey:Boolean = false; |
| /** |
| * @private |
| */ |
| protected var lastKey:uint = 0; |
| |
| /** |
| * @private |
| */ |
| protected var bSelectItem:Boolean = false; |
| |
| // these variable keep track of the current item been drawn |
| /** |
| * The height, in pixels, of the current row. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected var currentRowHeight:Number; |
| |
| /** |
| * Contains the index of the column for which a renderer is currently being created. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected var currentColNum:int; |
| |
| /** |
| * Contains the index of the row for which a renderer is currently being created. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected var currentRowNum:int; |
| |
| /** |
| * Contains the top position of the renderer that is currently being created. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected var currentItemTop:Number; |
| |
| /** |
| * An Array of ListRowInfo instances that cache header height and |
| * other information for the headers in the <code>headerItems</code> Array. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected var headerRowInfo:Array = []; |
| |
| /** |
| * Maps item renderers to the Factory instacne from which they have been created. |
| * |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected var itemRendererToFactoryMap:Dictionary; |
| |
| /** |
| * @private |
| * Array of Columns |
| */ |
| protected var _columns:Array; /* of AdvancedDataGridColumns */ |
| |
| /** |
| * An Array of AdvancedDataGridHeaderInfo instances for all columns |
| * in the control. |
| * |
| * @see mx.controls.advancedDataGridClasses.AdvancedDataGridHeaderInfo |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected var headerInfos:Array; |
| |
| /** |
| * An Array of AdvancedDataGridHeaderRenderer instances that |
| * define the header item renderers for the displayable columns. |
| * |
| * @see mx.controls.advancedDataGridClasses.AdvancedDataGridHeaderRenderer |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected var visibleHeaderInfos:Array; |
| |
| /** |
| * If there have been values set using selectedCells setter, but they have not |
| * yet been processed because they have never come into display. |
| * |
| * @private |
| */ |
| protected var cellsWaitingToBeDisplayed:Boolean = true; |
| |
| /** |
| * The list of selectedCells never displayed till now. |
| * Each cell is an Object with attributes rowIndex and columnIndex referring to |
| * their respective absolute values for the cell. |
| * |
| * @private |
| */ |
| protected var pendingCellSelection:ArrayCollection = new ArrayCollection([]); |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // headerItems |
| //---------------------------------- |
| |
| /** |
| * @private |
| * An Array of AdvancedDataGridHeaderRenderer instances that |
| * define the header item renderers for the control. |
| * |
| * @see mx.controls.advancedDataGridClasses.AdvancedDataGridHeaderRenderer |
| */ |
| public var headerItems:Array = []; |
| |
| //---------------------------------- |
| // fontContext |
| //---------------------------------- |
| |
| /** |
| * @inheritDoc |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get fontContext():IFlexModuleFactory |
| { |
| return moduleFactory; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set fontContext(moduleFactory:IFlexModuleFactory):void |
| { |
| this.moduleFactory = moduleFactory; |
| } |
| |
| //---------------------------------- |
| // selectionMode |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| protected var _selectionMode:String = SINGLE_ROW; |
| |
| [Inspectable(category="General", |
| enumeration="none,singleRow,multipleRows,singleCell,multipleCells", |
| defaultValue="singleRow")] |
| /** |
| * The selection mode of the control. Possible values are: |
| * <code>MULTIPLE_CELLS</code>, <code>MULTIPLE_ROWS</code>, <code>NONE</code>, |
| * <code>SINGLE_CELL</code>, and <code>SINGLE_ROW</code>. |
| * Changing the value of this property |
| * sets the <code>selectedCells</code> property to null. |
| * |
| * <p>You must set the <code>allowMultipleSelection</code> property to <code>true</code> |
| * to select more than one item in the control at the same time.</p> |
| * |
| * <p>Information about the selected cells is written to the <code>selectedCells</code> property.</p> |
| * |
| * @default SINGLE_ROW |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get selectionMode():String |
| { |
| return _selectionMode; |
| } |
| |
| public function set selectionMode(value:String):void |
| { |
| setSelectionMode(value); |
| itemsSizeChanged = true; |
| invalidateDisplayList(); |
| } |
| |
| //---------------------------------- |
| // headerHeight |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the headerHeight property. |
| */ |
| mx_internal var _headerHeight:Number = 22; |
| |
| [Bindable("resize")] |
| [Inspectable(category="General", defaultValue="22")] |
| |
| /** |
| * The height of the header cell of the column, in pixels. |
| * If set explicitly, that height will be used for all of |
| * the headers. If not set explicitly, |
| * the height will based on style settings and the header |
| * renderer. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get headerHeight():Number |
| { |
| return _headerHeight; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set headerHeight(value:Number):void |
| { |
| _headerHeight = value; |
| _explicitHeaderHeight = true; |
| itemsSizeChanged = true; |
| |
| invalidateDisplayList(); |
| } |
| |
| //---------------------------------- |
| // headerWordWrap |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the headerWordWrap property. |
| */ |
| private var _headerWordWrap:Boolean; |
| |
| [Inspectable(category="General")] |
| |
| /** |
| * If <code>true</code>, specifies that text in the header is |
| * wrapped if it does not fit on one line. |
| * |
| * If the <code>headerWordWrap</code> property is set in AdvancedDataGridColumn, |
| * this property will not have any effect. |
| * |
| * @default false |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get headerWordWrap():Boolean |
| { |
| return _headerWordWrap; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set headerWordWrap(value:Boolean):void |
| { |
| if (value == _headerWordWrap) |
| return; |
| |
| _headerWordWrap = value; |
| |
| itemsSizeChanged = true; |
| |
| invalidateDisplayList(); |
| |
| dispatchEvent(new Event("headerWordWrapChanged")); |
| } |
| |
| //---------------------------------- |
| // showHeaders |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the showHeaders property. |
| */ |
| private var _showHeaders:Boolean = true; |
| |
| [Bindable("showHeadersChanged")] |
| [Inspectable(category="General", defaultValue="true")] |
| |
| /** |
| * A flag that indicates whether the control should show |
| * column headers. |
| * If <code>true</code>, the control shows column headers. |
| * |
| * @default true |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get showHeaders():Boolean |
| { |
| return _showHeaders; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set showHeaders(value:Boolean):void |
| { |
| _showHeaders = value; |
| itemsSizeChanged = true; |
| |
| invalidateDisplayList(); |
| |
| dispatchEvent(new Event("showHeadersChanged")); |
| } |
| |
| /** |
| * @private |
| * headers are not renderered if showHeaders = false |
| * or headerheight = 0, so this test is whether row0 is |
| * a header or not. |
| */ |
| mx_internal function get headerVisible():Boolean |
| { |
| return showHeaders && (headerHeight > 0); |
| } |
| |
| //---------------------------------- |
| // headerRenderer |
| //---------------------------------- |
| |
| private var _headerRenderer:IFactory; |
| |
| [Inspectable(category="Data")] |
| [Bindable("headerRendererChanged")] |
| |
| /** |
| * The header renderer used to display the header rows of the column. |
| * |
| * @default AdvancedDataGridHeaderRenderer |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get headerRenderer():IFactory |
| { |
| return _headerRenderer; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set headerRenderer(value:IFactory):void |
| { |
| _headerRenderer = value; |
| |
| invalidateSize(); |
| invalidateDisplayList(); |
| |
| itemsSizeChanged = true; |
| rendererChanged = true; |
| |
| dispatchEvent(new Event("headerRendererChanged")); |
| } |
| |
| //---------------------------------- |
| // sortItemRenderer |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the sortItemRenderer property. |
| */ |
| private var _sortItemRenderer:IFactory; |
| |
| [Inspectable] |
| [Bindable("sortItemRendererChanged")] |
| |
| /** |
| * The sort item renderer to be used to display the sort icon in the |
| * column header. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get sortItemRenderer():IFactory |
| { |
| return _sortItemRenderer; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set sortItemRenderer(value:IFactory):void |
| { |
| _sortItemRenderer = value; |
| |
| itemsSizeChanged = true; |
| rendererChanged = true; |
| |
| invalidateSize(); |
| invalidateDisplayList(); |
| |
| dispatchEvent(new Event("sortItemRendererChanged")); |
| } |
| |
| //---------------------------------- |
| // styleFunction |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _styleFunction:Function; |
| |
| /** |
| * A callback function called while rendering each cell. |
| * |
| * The signature of the callback function is: |
| * |
| * <pre>function myStyleFunction(data:Object, column:AdvancedDataGridColumn):Object</pre> |
| * |
| * <p>where <code>data</code> is the data object associated with the item renderer being rendered, |
| * and <code>column</code> is the AdvancedDataGridColumn instance with which the item renderer is associated.</p> |
| * |
| * <p>The return value should be a Object with styles as properties. |
| * For example: <code>{ color:0xFF0000, fontWeight:"bold" }</code>.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get styleFunction():Function |
| { |
| return _styleFunction; |
| } |
| |
| public function set styleFunction(value:Function):void |
| { |
| _styleFunction = value; |
| |
| invalidateDisplayList(); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden methods |
| // |
| //-------------------------------------------------------------------------- |
| /** |
| * @private |
| */ |
| override protected function makeRowsAndColumns(left:Number, top:Number, |
| right:Number, bottom:Number, |
| firstCol:int, firstRow:int, |
| byCount:Boolean = false, rowsNeeded:uint = 0):Point |
| { |
| // ignoring padding left |
| // Use paddingLeft for each item |
| /* if(left == 0) |
| left = getStyle("paddingLeft"); */ |
| |
| var xx:Number; |
| var ww:Number; |
| var hh:Number; |
| |
| var rowData:AdvancedDataGridListData; |
| |
| var i:int; |
| |
| currentColNum = lockedColumnCount; |
| currentRowNum = lockedRowCount; |
| |
| var rowsMade:int = 0; |
| |
| var item:IListItemRenderer; |
| var extraItem:DisplayObject; |
| |
| // bail if we have no columns |
| if (!visibleColumns || visibleColumns.length == 0) |
| { |
| // remove the header items and list items |
| purgeHeaderRenderers(); |
| purgeItemRenderers(); |
| visibleData = {}; |
| return new Point(0,0); |
| } |
| |
| invalidateSizeFlag = true; |
| |
| var uid:String; |
| var c:AdvancedDataGridColumn; |
| var more:Boolean = true; |
| |
| currentItemTop = top; |
| // if we have headers or other locked items and either haven't created the |
| // items for those items or we're redrawing the items above the locked region... |
| if ( firstRow <= lockedRowCount) |
| { |
| //If needed |
| createHeaders(left, top); |
| //If needed |
| createLockedRows(left, top, right, bottom); |
| } |
| else |
| { |
| currentRowNum = firstRow; |
| } |
| more = (iterator != null && !iterator.afterLast && iteratorValid); |
| while ((!byCount && currentItemTop < bottom) || (byCount && rowsNeeded > 0)) |
| { |
| if (byCount) |
| rowsNeeded--; |
| |
| createRow(left,top,right,bottom,more); |
| |
| more = moveIterator(more); |
| ++rowsMade; |
| } |
| |
| // byCount means we're making rows and wont get all the way to the bottom |
| // so we skip this cleanup pass |
| if (!byCount) |
| { |
| // delete extra rows |
| while (currentRowNum < listItems.length) |
| { |
| var rr:Array = listItems.pop(); |
| rowInfo.pop(); |
| while (rr.length) |
| { |
| item = rr.pop(); |
| addToFreeItemRenderers(item); |
| } |
| } |
| } |
| |
| invalidateSizeFlag = false; |
| |
| return new Point(currentColNum, rowsMade); |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function purgeItemRenderers():void |
| { |
| rendererChanged = false; |
| var item:IListItemRenderer; |
| while (listItems.length) |
| { |
| currentRowNum = listItems.length - 1; |
| while (listItems[currentRowNum].length) |
| { |
| // remove extra columns |
| item = listItems[currentRowNum].pop(); |
| item.parent.removeChild(DisplayObject(item)); |
| } |
| listItems.pop(); |
| } |
| rowMap = {}; |
| rowInfo = []; |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function drawItem(item:IListItemRenderer, |
| selected:Boolean = false, |
| highlighted:Boolean = false, |
| caret:Boolean = false, |
| transition:Boolean = false):void |
| { |
| if (!item) |
| return; |
| if (rowMap[item.name] == null) |
| return; |
| |
| super.drawItem(item, selected, highlighted, caret, transition); |
| |
| 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]; |
| if (r) { |
| r.validateDisplayList(); |
| updateDisplayOfItemRenderer(r); |
| } |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function finishKeySelection():void |
| { |
| var uid:String; |
| var rowCount:int = listItems.length; |
| var partialRow:int = (rowInfo[rowCount-1].y + rowInfo[rowCount-1].height > |
| listContent.height) ? 1 : 0; |
| |
| if (lastKey == Keyboard.PAGE_DOWN) |
| { |
| if (lockedRowCount >= rowCount - partialRow - 1) |
| caretIndex = Math.min(verticalScrollPosition + lockedRowCount, |
| collection.length - 1); |
| // set caret to last full row of new screen |
| else |
| caretIndex = Math.min(verticalScrollPosition + rowCount - partialRow - 1, |
| collection.length - 1); |
| } |
| |
| var listItem:IListItemRenderer; |
| var bSelChanged:Boolean = false; |
| |
| if (bSelectItem && caretIndex - verticalScrollPosition >= 0) |
| { |
| if (caretIndex - verticalScrollPosition > listItems.length - 1) |
| caretIndex = listItems.length - 1 + verticalScrollPosition; |
| |
| listItem = listItems[caretIndex - verticalScrollPosition][0]; |
| if (listItem) |
| { |
| uid = itemToUID(listItem.data); |
| listItem = visibleData[uid]; |
| if (listItem) |
| { |
| if (lastKey == Keyboard.SPACE) |
| { |
| bSelChanged = selectItem(listItem, bShiftKey, bCtrlKey); |
| } |
| else |
| { |
| if (!bCtrlKey) |
| { |
| selectItem(listItem, bShiftKey, bCtrlKey); |
| bSelChanged = true; |
| } |
| if (bCtrlKey) |
| { |
| drawItem(listItem, selectedData[uid] != null, uid == highlightUID, 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 = pt.x; |
| } |
| dispatchEvent(evt); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function addToFreeItemRenderers(item:IListItemRenderer):void |
| { |
| DisplayObject(item).visible = false; |
| |
| delete rowMap[item.name]; |
| |
| // Only delete from visibleData if we are freeing the renderer for |
| // the first visible column |
| var UID:String = itemToUID(item.data); |
| if (visibleData[UID] == item) |
| delete visibleData[UID]; |
| |
| if (columnMap[item.name]) |
| { |
| var c:AdvancedDataGridColumn = columnMap[item.name]; |
| var factory:IFactory = itemRendererToFactoryMap[item]; |
| if (!freeItemRenderersTable[c]) |
| freeItemRenderersTable[c] = new Dictionary(false); |
| if (!freeItemRenderersTable[c][factory]) |
| freeItemRenderersTable[c][factory] = []; |
| freeItemRenderersTable[c][factory].push(item); |
| delete columnMap[item.name]; |
| } |
| // Remove the item if it is not present in the column map. |
| // it cant be recycled |
| // If we are coming here, renderers are not getting recycled properly. |
| else |
| { |
| item.parent.removeChild(DisplayObject(item)); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function adjustListContent(unscaledWidth:Number = -1, |
| unscaledHeight:Number = -1):void |
| { |
| super.adjustListContent(unscaledWidth, unscaledHeight); |
| listSubContent.setActualSize(listContent.width, listContent.height); |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function removeIndicators(uid:String):void |
| { |
| if (isRowSelectionMode()) |
| super.removeIndicators(uid); |
| } |
| |
| /** |
| * Removes all selection and highlight and caret indicators. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| override protected function clearIndicators():void |
| { |
| if (isRowSelectionMode()) |
| super.clearIndicators(); |
| } |
| |
| override mx_internal function clearHighlight(item:IListItemRenderer):void |
| { |
| if (isRowSelectionMode()) |
| super.clearHighlight(item); |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function createChildren():void |
| { |
| super.createChildren(); |
| |
| if (!listSubContent) |
| { |
| listSubContent = new AdvancedListBaseContentHolder(this); |
| listSubContent.styleName = this; |
| listContent.addChild(listSubContent); |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Creates the column headers. |
| * After creating the headers, this method updates the <code>currentItemTop</code> property |
| * with the height of the header area. |
| * It also updates the <code>headerHeight</code> property |
| * if <code>headerHeight</code> has not been specified explicitly. |
| * |
| * @param left The x coordinate of the header renderer. |
| * |
| * @param top The y coordinate of the header renderer. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function createHeaders(left:Number, top:Number):void |
| { |
| var xx:Number; |
| var ww:Number; |
| var hh:Number; |
| var item:IListItemRenderer; |
| var extraItem:DisplayObject; |
| var c:AdvancedDataGridColumn; |
| var rowData:AdvancedDataGridListData; |
| var i:int; |
| var n:int = 0; |
| var maxHeaderHeight:Number = 0; |
| if(!headerItems[0] || !headerItems[0][0] || (top < headerItems[0][0].y + headerItems[0][0].height)) |
| { |
| xx = left; |
| hh = 0; |
| currentRowNum = 0; |
| currentColNum = 0; // visible columns compensate for firstCol offset |
| |
| // Clear the dangling pointers of headerItems from headerInfo structures |
| n = headerInfos.length; |
| for( i = 0; i < n; i++) |
| headerInfos[i].headerItem = null; |
| |
| var optimumColumns:Array = getOptimumColumns(); |
| while (/* xx < right && */ currentColNum < optimumColumns.length) |
| { |
| c = optimumColumns[currentColNum]; |
| if (!headerItems[currentRowNum]) |
| { |
| headerItems[currentRowNum] = []; |
| } |
| else if (headerItems[currentRowNum][currentColNum]) |
| { |
| // add header to the free item renderers table |
| var freeHeaderItem:IListItemRenderer = headerItems[currentRowNum][currentColNum]; |
| addHeaderToFreeItemRenderers(freeHeaderItem, freeHeaderItem.data as AdvancedDataGridColumn); |
| } |
| // get the header renderer |
| 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; |
| headerItems[currentRowNum][currentColNum] = item; |
| headerInfos[c.colNum].headerItem = item; |
| // set prefW so we can compute prefH |
| item.explicitWidth = ww = c.width; |
| UIComponentGlobals.layoutManager.validateClient(item, true); |
| // but size it regardless of what prefW is |
| currentRowHeight = item.getExplicitOrMeasuredHeight(); |
| item.setActualSize(ww, _explicitHeaderHeight ? |
| _headerHeight - cachedPaddingTop - cachedPaddingBottom : currentRowHeight); |
| item.move(xx, currentItemTop + cachedPaddingTop); |
| xx += ww; |
| hh = Math.ceil(Math.max(hh, _explicitHeaderHeight ? |
| _headerHeight : currentRowHeight + cachedPaddingBottom + cachedPaddingTop)); |
| maxHeaderHeight = Math.max(maxHeaderHeight, _explicitHeaderHeight ? |
| _headerHeight - cachedPaddingTop - cachedPaddingBottom : currentRowHeight); |
| item.visible = headerVisible; |
| currentColNum++; |
| } |
| if (headerItems[currentRowNum]) |
| { |
| // expand all headers to be of maximum height |
| n = headerItems[currentRowNum].length; |
| for (i = 0; i < n; i++) |
| { |
| headerItems[currentRowNum][i].setActualSize(headerItems[currentRowNum][i].width, maxHeaderHeight); |
| |
| // invalide the header item. |
| // InvalidateDisplayList of the header will not be called if the width/height |
| // of the header has not changed. |
| // We have to call it explicitly. |
| var headerItem:IInvalidating = headerItems[currentRowNum][i] as IInvalidating; |
| |
| if (headerItem) |
| IInvalidating(headerItem).invalidateDisplayList(); |
| |
| } |
| |
| while (headerItems[currentRowNum].length > currentColNum) |
| { |
| // remove extra columns |
| extraItem = headerItems[currentRowNum].pop(); |
| extraItem.parent.removeChild(extraItem); |
| } |
| } |
| headerRowInfo[currentRowNum] = new ListRowInfo(currentItemTop, hh, uid); |
| if (headerVisible) |
| currentItemTop += item ? hh : 0; |
| if (!_explicitHeaderHeight) |
| _headerHeight = item ? hh : 0; |
| |
| } |
| } |
| |
| /** |
| * Creates the locked rows, if necessary. |
| * |
| * @param left The x coordinate of the upper-left corner of the header renderer. |
| * |
| * @param top The y coordinate of the upper-left corner of the header renderer. |
| * |
| * @param right The x coordinate of the lower-right corner of the header renderer. |
| * |
| * @param bottom The y coordinate of the lower-right corner of the header renderer. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function createLockedRows(left:Number, top:Number, right:Number, bottom:Number):void |
| { |
| var more:Boolean = true; |
| var numLocked:int = lockedRowCount; |
| var i:int; |
| var rowsMade:int = 0; |
| if(lockedRowCount > 0 && (!listItems[lockedRowCount-1] || !listItems[lockedRowCount-1][0] || (top < listItems[lockedRowCount-1][0].y + listItems[lockedRowCount-1][0].height))) |
| { |
| currentRowNum = 0; |
| var bookmark:CursorBookmark; |
| if (numLocked && iterator) |
| { |
| bookmark = iterator.bookmark; |
| try |
| { |
| iterator.seek(CursorBookmark.FIRST); |
| } |
| catch (e:ItemPendingError) |
| { |
| lastSeekPending = new ListBaseSeekPending(CursorBookmark.FIRST, 0); |
| e.addResponder(new ItemResponder(seekPendingResultHandler, seekPendingFailureHandler, |
| lastSeekPending)); |
| iteratorValid = false; |
| } |
| |
| } |
| more = (iterator != null && !iterator.afterLast && iteratorValid); |
| for (i = 0; i < numLocked; i++) |
| { |
| createRow(left,top,right,bottom,more); |
| more = moveIterator(more); |
| ++rowsMade; |
| } |
| |
| if (bookmark) |
| { |
| try |
| { |
| iterator.seek(bookmark, numLocked); |
| } |
| catch (e:ItemPendingError) |
| { |
| lastSeekPending = new ListBaseSeekPending(CursorBookmark.CURRENT, 0) |
| e.addResponder(new ItemResponder(seekPendingResultHandler, seekPendingFailureHandler, |
| lastSeekPending)); |
| iteratorValid = false; |
| } |
| } |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| protected function purgeHeaderRenderers():void |
| { |
| var item:IListItemRenderer; |
| while(headerItems.length) |
| { |
| var headerRow:Array = headerItems.pop(); |
| while(headerRow.length) |
| { |
| item = IListItemRenderer(headerRow.pop()); |
| addHeaderToFreeItemRenderers(item, item.data as AdvancedDataGridColumn); |
| } |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| protected function updateDisplayOfItemRenderer(r:IListItemRenderer):void |
| { |
| if (r is IInvalidating) |
| { |
| var ui:IInvalidating = IInvalidating(r); |
| ui.invalidateDisplayList(); |
| } |
| } |
| |
| /** |
| * Sets the cell defined by <code>uid</code> to use the item renderer |
| * specified by <code>item</code>. |
| * |
| * @param uid The UID of the cell. |
| * |
| * @param item The item renderer to use for the cell. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function setVisibleDataItem(uid:String, item:IListItemRenderer):void |
| { |
| if (uid && currentColNum == 0) |
| visibleData[uid] = item; |
| } |
| |
| /** |
| * Draws the item renderer corresponding to the specified UID. |
| * |
| * @param uid The UID of the selected cell. |
| * |
| * @param selected Set to <code>true</code> to draw the cell as selected. |
| * |
| * @param highlighted Set to <code>true</code> to draw the cell as highlighted. |
| * |
| * @param caret Set to <code>true</code> to draw the cell with a caret. |
| * |
| * @param Set to <code>true</code> to animate the change to the cell's appearance. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function drawVisibleItem(uid:String, |
| selected:Boolean = false, |
| highlighted:Boolean = false, |
| caret:Boolean = false, |
| transition:Boolean = false):void |
| { |
| if (isRowSelectionMode()) |
| if (visibleData[uid]) |
| drawItem(visibleData[uid], selected, highlighted, caret); |
| } |
| |
| /** |
| * @private |
| * |
| * @return The newly selected item or <code>null</code> if the selection |
| * has not changed. |
| */ |
| override protected function moveSelectionVertically(code:uint, shiftKey:Boolean, ctrlKey:Boolean):void |
| { |
| var newVerticalScrollPosition:Number; |
| showCaret = true; |
| |
| 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++; |
| } |
| |
| var bUpdateVerticalScrollPosition:Boolean = false; |
| bSelectItem = false; |
| |
| switch (code) |
| { |
| case Keyboard.UP: |
| { |
| if (caretIndex > 0) |
| { |
| caretIndex--; |
| bUpdateVerticalScrollPosition = true; |
| bSelectItem = true; |
| } |
| break; |
| } |
| |
| case Keyboard.DOWN: |
| { |
| if (caretIndex < collection.length - 1) |
| { |
| caretIndex++; |
| bUpdateVerticalScrollPosition = true; |
| bSelectItem = true; |
| } |
| else if ((caretIndex == collection.length - 1) && partialRow) |
| { |
| if (verticalScrollPosition < maxVerticalScrollPosition) |
| newVerticalScrollPosition = verticalScrollPosition + 1; |
| } |
| break; |
| } |
| |
| case Keyboard.PAGE_UP: |
| { |
| if (caretIndex < lockedRowCount) |
| { |
| newVerticalScrollPosition = 0; |
| caretIndex = 0; |
| } |
| // if the caret is on-screen, but not at the top row |
| // just move the caret to the top row |
| else if (caretIndex > verticalScrollPosition + lockedRowCount && |
| caretIndex < verticalScrollPosition + rowCount) |
| { |
| caretIndex = verticalScrollPosition + lockedRowCount; |
| } |
| else |
| { |
| // paging up is really hard because we don't know how many |
| // rows to move because of variable row height. We would have |
| // to double-buffer a previous screen in order to get this exact |
| // so we just guess for now based on current rowCount |
| caretIndex = Math.max(caretIndex - rowCount + lockedRowCount, 0); |
| newVerticalScrollPosition = Math.max(caretIndex - lockedRowCount,0) |
| } |
| bSelectItem = true; |
| |
| break; |
| } |
| |
| case Keyboard.PAGE_DOWN: |
| { |
| if (caretIndex < lockedRowCount) |
| { |
| newVerticalScrollPosition = 0; |
| } |
| // if the caret is on-screen, but not at the bottom row |
| // just move the caret to the bottom row (not partial row) |
| else if (caretIndex >= verticalScrollPosition + lockedRowCount && |
| caretIndex < verticalScrollPosition + rowCount - partialRow - 1) |
| { |
| caretIndex = Math.min(verticalScrollPosition + listItems.length |
| + lockedRowCount, |
| collection.length - 1); |
| } |
| else if (lockedRowCount >= rowCount - partialRow - 1) |
| { |
| newVerticalScrollPosition = Math.min(verticalScrollPosition + 1, maxVerticalScrollPosition); |
| } |
| else |
| { |
| newVerticalScrollPosition = Math.min(caretIndex - lockedRowCount, maxVerticalScrollPosition); |
| } |
| bSelectItem = true; |
| break; |
| } |
| |
| case Keyboard.HOME: |
| { |
| if (caretIndex > 0) |
| { |
| caretIndex = 0; |
| newVerticalScrollPosition = 0; |
| bSelectItem = true; |
| } |
| break; |
| } |
| |
| case Keyboard.END: |
| { |
| if (caretIndex < collection.length - 1) |
| { |
| caretIndex = collection.length - 1; |
| newVerticalScrollPosition = maxVerticalScrollPosition; |
| bSelectItem = true; |
| } |
| break; |
| } |
| |
| case Keyboard.SPACE: |
| { |
| bUpdateVerticalScrollPosition = true; |
| bSelectItem = true; |
| break; |
| } |
| } |
| |
| 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 = shiftKey; |
| bCtrlKey = ctrlKey; |
| lastKey = code; |
| |
| finishKeySelection(); |
| } |
| |
| /** |
| * @private |
| */ |
| protected function addHeaderToFreeItemRenderers(item:IListItemRenderer, c:AdvancedDataGridColumn):void |
| { |
| DisplayObject(item).visible = false; |
| |
| var factory:IFactory = itemRendererToFactoryMap[item]; |
| if (!freeItemRenderersTable[c]) |
| freeItemRenderersTable[c] = new Dictionary(false); |
| if (!freeItemRenderersTable[c][factory]) |
| freeItemRenderersTable[c][factory] = []; |
| freeItemRenderersTable[c][factory].push(item); |
| } |
| |
| /** |
| * Creates a new AdvancedDataGridListData instance and populates the fields based on |
| * the input data provider item. |
| * |
| * @param data The data provider item used to populate the ListData. |
| * |
| * @param uid The UID for the item. |
| * |
| * @param rowNum The index of the item in the data provider. |
| * |
| * @param columnNum The column index associated with this item. |
| * |
| * @param column The column associated with this item. |
| * |
| * @return A newly constructed AdvancedDataGridListData object. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function makeListData(data:Object, uid:String, |
| rowNum:int, columnNum:int, column:AdvancedDataGridColumn):BaseListData |
| { |
| if (data is AdvancedDataGridColumn) |
| { |
| return new AdvancedDataGridListData((column.headerText != null) ? column.headerText : column.dataField, |
| column.dataField, columnNum, uid, this, rowNum); |
| } |
| else |
| { |
| var label:String = column.itemToLabel(data); |
| return new AdvancedDataGridListData(label, column.dataField, |
| columnNum, uid, this, rowNum); |
| } |
| } |
| |
| /** |
| * @private |
| * This grid just returns the column size, |
| * but could handle column spanning. |
| */ |
| mx_internal function getWidthOfItem(item:IListItemRenderer, |
| col:AdvancedDataGridColumn, visibleColumnIndex:int):Number |
| { |
| return col.width; |
| } |
| |
| /** |
| * Calculates the row height of columns in a row. |
| * If <code>skipVisible</code> is <code>true</code> |
| * the AdvancedDataGridBase already knows the height of |
| * the renderers for the column that do fit in the display area, |
| * so this method only needs to calculate for the item renderers |
| * that would exist if other columns in that row were in the |
| * display area. This is needed so that if the user scrolls |
| * horizontally, the height of the row does not adjust as different |
| * columns appear and disappear. |
| * |
| * @param data The data provider item for the row. |
| * |
| * @param hh The current height of the row. |
| * |
| * @param skipVisible If <code>true</code>, no need to measure item |
| * renderers in visible columns. |
| * |
| * @return The row height, in pixels. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function calculateRowHeight(data:Object, hh:Number, skipVisible:Boolean = false):Number |
| { |
| return NaN; |
| } |
| |
| /** |
| * Get the appropriate renderer for a column, using the default renderer if none specified |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| mx_internal function columnItemRenderer(c:AdvancedDataGridColumn, forHeader:Boolean, itemData:Object):IListItemRenderer |
| { |
| var factory:IFactory = columnItemRendererFactory(c,forHeader,itemData); |
| var renderer:IListItemRenderer = factory.newInstance(); |
| |
| itemRendererToFactoryMap[renderer] = factory; |
| |
| renderer.owner = this; |
| return renderer; |
| } |
| |
| /** |
| * 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 |
| */ |
| mx_internal function columnItemRendererFactory(c:AdvancedDataGridColumn, forHeader:Boolean, itemData:Object):IFactory |
| { |
| var factory:IFactory; |
| if (forHeader) |
| { |
| if (c.headerRenderer) |
| factory = c.headerRenderer; |
| else |
| factory = headerRenderer; |
| } |
| else |
| { |
| if (c.itemRenderer) |
| { |
| factory = c.itemRenderer; |
| } |
| } |
| if (!factory) |
| { |
| factory = itemRenderer; |
| } |
| |
| return factory; |
| } |
| |
| /** |
| * Get the headerWordWrap for a column, using the default wordWrap if none specified |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| mx_internal function columnHeaderWordWrap(c:AdvancedDataGridColumn):Boolean |
| { |
| if (c) |
| { |
| if (c.headerWordWrap === undefined) |
| return headerWordWrap; |
| else |
| return c.headerWordWrap; |
| } |
| |
| return headerWordWrap; |
| } |
| |
| /** |
| * Get the wordWrap for a column, using the default wordWrap if none specified |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| mx_internal function columnWordWrap(c:AdvancedDataGridColumn):Boolean |
| { |
| if (c) |
| { |
| if (c.wordWrap === undefined) |
| return wordWrap; |
| else |
| return c.wordWrap; |
| } |
| |
| return wordWrap; |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function drawHighlightIndicator(indicator:Sprite, x:Number, y:Number, width:Number, height:Number, color:uint, itemRenderer:IListItemRenderer):void |
| { |
| if (isRowSelectionMode()) |
| width = unscaledWidth - viewMetrics.left - viewMetrics.right; |
| |
| super.drawHighlightIndicator(indicator, x, y, width, height, color, itemRenderer); |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function drawCaretIndicator(indicator:Sprite, x:Number, y:Number, width:Number, height:Number, color:uint, itemRenderer:IListItemRenderer):void |
| { |
| if (isRowSelectionMode()) |
| width = unscaledWidth - viewMetrics.left - viewMetrics.right; |
| |
| super.drawCaretIndicator(indicator, x, y, width, height, color, itemRenderer); |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function drawSelectionIndicator(indicator:Sprite, x:Number, y:Number, width:Number, height:Number, color:uint, itemRenderer:IListItemRenderer):void |
| { |
| if (isRowSelectionMode()) |
| width = unscaledWidth - viewMetrics.left - viewMetrics.right; |
| |
| super.drawSelectionIndicator(indicator, x, y, width, height, color, itemRenderer); |
| } |
| |
| /** |
| * @private |
| */ |
| override mx_internal function mouseEventToItemRendererOrEditor(event:MouseEvent):IListItemRenderer |
| { |
| var target:DisplayObject = DisplayObject(event.target); |
| |
| if (target == listContent) |
| { |
| var pt:Point = new Point(event.stageX, event.stageY); |
| pt = listContent.globalToLocal(pt); |
| // if ADG is empty then length of rowInfo is 0 |
| var yy:Number = rowInfo.length == 0 ? 0 : rowInfo[0].y; |
| var n:int = listItems.length; |
| for (var i:int = 0; i < n; i++) |
| { |
| if (listItems[i].length) |
| { |
| if (pt.y < yy + rowInfo[i].height) |
| { |
| var xx:Number = 0; |
| var m:int = listItems[i].length; |
| for (var j:int = 0; j < m; j++) |
| { |
| if (pt.x < xx + visibleColumns[j].width) |
| return listItems[i][j]; |
| xx += visibleColumns[j].width; |
| } |
| } |
| } |
| yy += rowInfo[i].height; |
| } |
| } |
| else if (target == highlightIndicator) |
| { |
| return lastHighlightItemRenderer; |
| } |
| |
| while (target && target != this) |
| { |
| if (target is IListItemRenderer && |
| (target.parent == listSubContent || target.parent == listContent)) |
| { |
| if (target.visible) |
| return IListItemRenderer(target); |
| break; |
| } |
| |
| if (target is IUIComponent) |
| target = IUIComponent(target).owner; |
| else |
| target = target.parent; |
| } |
| return null; |
| } |
| |
| /** |
| * @private |
| */ |
| mx_internal function get gridColumnMap():Object |
| { |
| return columnMap; |
| } |
| |
| /** |
| * @private |
| */ |
| private function createRow(left:Number, top:Number, right:Number, bottom:Number, more:Boolean):void |
| { |
| var xx:Number; |
| var hh:Number; |
| |
| var i:int; |
| var n:int; |
| |
| var item:IListItemRenderer; |
| var extraItem:IListItemRenderer; |
| |
| var bSelected:Boolean = false; |
| var bHighlight:Boolean = false; |
| var bCaret:Boolean = false; |
| var itemData:Object; |
| |
| var uid:String = null; |
| |
| // fill the itemData with Data Loading if the item is pending |
| if (itemPending) |
| { |
| var optimumColumns:Array = getOptimumColumns(); |
| var obj:Object = {}; |
| n = optimumColumns.length; |
| for (i = 0; i < n; i++) |
| { |
| obj[AdvancedDataGridColumn(optimumColumns[i]).dataField] = "Data Loading"; |
| } |
| |
| itemData = obj; |
| } |
| else |
| { |
| itemData = more ? iterator.current : null; |
| } |
| |
| xx = left; |
| hh = 0; |
| currentColNum = 0; |
| if (!listItems[currentRowNum]) |
| listItems[currentRowNum]= []; |
| if (more) |
| { |
| uid = itemToUID(itemData); |
| // create the renderers |
| setupRenderer(itemData,uid); |
| } |
| else |
| { |
| // if we've run out of data, we don't make renderers |
| // and we inherit the previous row's height or rowHeight |
| // if it is the first row. |
| hh = currentRowNum > 1 ? rowInfo[currentRowNum - 1].height : rowHeight; |
| } |
| |
| // layout the renderers |
| hh = layoutRow(more,left,hh); |
| while (listItems[currentRowNum].length > currentColNum) |
| { |
| // remove extra columns |
| extraItem = listItems[currentRowNum].pop(); |
| addToFreeItemRenderers(extraItem); |
| } |
| if (more && variableRowHeight) |
| { |
| hh = Math.ceil(calculateRowHeight(itemData, hh, true)); |
| } |
| |
| if (listItems[currentRowNum]) |
| { |
| var itemHeight:Number = hh - cachedPaddingTop - cachedPaddingBottom; |
| n = listItems[currentRowNum].length; |
| for (i = 0; i < n; i++) |
| { |
| listItems[currentRowNum][i].setActualSize( |
| listItems[currentRowNum][i].width, itemHeight); |
| } |
| } |
| if (cachedVerticalAlign != "top") |
| { |
| if (cachedVerticalAlign == "bottom") |
| { |
| for (i = 0; i < currentColNum; i++) |
| { |
| item = listItems[currentRowNum][i]; |
| item.move(item.x, currentItemTop + hh - cachedPaddingBottom - item.getExplicitOrMeasuredHeight()); |
| } |
| } |
| else |
| { |
| for (i = 0; i < currentColNum; i++) |
| { |
| item = listItems[currentRowNum][i]; |
| item.move(item.x, currentItemTop + cachedPaddingTop + |
| (hh - cachedPaddingBottom - cachedPaddingTop - item.getExplicitOrMeasuredHeight()) / 2); |
| } |
| } |
| |
| } |
| bSelected = selectedData[uid] != null; |
| bHighlight = highlightUID == uid; |
| bCaret = caretUID == uid; |
| rowInfo[currentRowNum] = new ListRowInfo(currentItemTop, hh, uid); |
| |
| if (more) |
| drawVisibleItem(uid, bSelected, bHighlight, bCaret); |
| |
| if (hh == 0) // hh can be zero if we had zero width |
| hh = rowHeight; |
| currentItemTop += hh; |
| currentRowNum++; |
| } |
| |
| /** |
| * Moves the iterator to the next item. |
| * |
| * @private |
| */ |
| private function moveIterator(more:Boolean):Boolean |
| { |
| if (itemPending) |
| { |
| itemPending = false; |
| return true; |
| } |
| if (iterator && more) |
| { |
| try |
| { |
| more = iterator.moveNext(); |
| } |
| catch (e:ChildItemPendingError) |
| { |
| itemPending = true; |
| } |
| catch (e:ItemPendingError) |
| { |
| lastSeekPending = new ListBaseSeekPending(CursorBookmark.CURRENT, 0); |
| e.addResponder(new ItemResponder(seekPendingResultHandler, seekPendingFailureHandler, lastSeekPending)); |
| more = false; |
| iteratorValid = false; |
| } |
| } |
| |
| return more; |
| } |
| |
| /** |
| * Contains the layout logic for the rows. |
| * |
| * @private |
| */ |
| protected function layoutRow(more:Boolean,xx:Number,hh:Number):Number |
| { |
| var rh:Number = 0; |
| if (more) |
| { |
| var item:IListItemRenderer; |
| var c:AdvancedDataGridColumn; |
| var ww:Number; |
| currentColNum = 0; |
| var itemYPos:Number = currentItemTop + cachedPaddingTop; |
| var cachedPadding:Number = cachedPaddingTop + cachedPaddingBottom; |
| |
| var optimumColumns:Array = getOptimumColumns(); |
| |
| while (currentColNum < optimumColumns.length) |
| { |
| c = optimumColumns[currentColNum]; |
| item = listItems[currentRowNum][currentColNum]; |
| |
| ww = getWidthOfItem(item, c, currentColNum); |
| |
| item.explicitWidth = ww; |
| |
| //from list - setting variableRowHeight/wordWrap at runtime will adjust the data |
| if ((item is IInvalidating) && |
| (wordWrapChanged || |
| variableRowHeight)) |
| IInvalidating(item).invalidateSize(); |
| |
| UIComponentGlobals.layoutManager.validateClient(item, true); |
| currentRowHeight = item.getExplicitOrMeasuredHeight(); |
| rh = getRowHeight(item.data); |
| item.setActualSize(ww, variableRowHeight |
| ? currentRowHeight |
| : rh - cachedPaddingTop - cachedPaddingBottom); |
| item.move(xx, itemYPos); |
| xx += c.width; |
| |
| // consider the height of the visible item only |
| // otherwise its height will be taken into account |
| // while calculating rowHeight in case of variableRowHeight being true |
| if(variableRowHeight && item.visible) |
| { |
| hh = Math.ceil(Math.max(hh, variableRowHeight ? currentRowHeight + cachedPadding : rh)); |
| } |
| currentColNum++; |
| } |
| } |
| |
| if (!variableRowHeight) |
| hh = rh != 0 ? rh : getRowHeight(); |
| |
| return hh; |
| } |
| |
| /** |
| * Returns the row height. |
| * |
| * @param itemData The data provider object for the row. |
| * |
| * @return The height of the row, in pixels. |
| * |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function getRowHeight(itemData:Object = null):Number |
| { |
| return rowHeight; |
| } |
| |
| /** |
| * Returns the header item renderer. |
| * |
| * @param c The column of the control. |
| * |
| * @return The header item renderer. |
| * |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function getHeaderRenderer(c:AdvancedDataGridColumn):IListItemRenderer |
| { |
| var renderer:IListItemRenderer; |
| var factory:IFactory = columnItemRendererFactory(c, true, null); |
| |
| if (freeItemRenderersTable[c] |
| && freeItemRenderersTable[c][factory] |
| && freeItemRenderersTable[c][factory].length) |
| { |
| renderer = freeItemRenderersTable[c][factory].pop(); |
| } |
| else |
| { |
| renderer = columnItemRenderer(c, true, null); |
| addRendererToContentArea(renderer, c); |
| } |
| |
| return renderer; |
| } |
| |
| /** |
| * Return the item renderer |
| * |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| mx_internal function getRenderer(c:AdvancedDataGridColumn, itemData:Object, forDragProxy:Boolean = false ):IListItemRenderer |
| { |
| var renderer:IListItemRenderer; |
| var factory:IFactory = columnItemRendererFactory(c, false, itemData); |
| |
| if (freeItemRenderersTable[c] |
| && freeItemRenderersTable[c][factory] |
| && freeItemRenderersTable[c][factory].length) |
| { |
| renderer = freeItemRenderersTable[c][factory].pop(); |
| } |
| else |
| { |
| renderer = columnItemRenderer(c, false, itemData); |
| renderer.styleName = c; |
| } |
| |
| return renderer; |
| } |
| |
| /** |
| * @private |
| * Make renderer and populate <code>listItems</code> with them. |
| * |
| * @param itemData The data provider object for the row. |
| * |
| * @param uid The UID of the row. |
| * |
| * @param insertItems |
| * |
| */ |
| protected function setupRenderer(itemData:Object,uid:String,insertItems:Boolean = false):void |
| { |
| var c:AdvancedDataGridColumn; |
| var item:IListItemRenderer; |
| var rowData:AdvancedDataGridListData; |
| var row:Array = []; |
| |
| var optimumColumns:Array = getOptimumColumns(); |
| while (currentColNum < optimumColumns.length) |
| { |
| c = optimumColumns[currentColNum]; |
| if (insertItems) |
| { |
| item = getRenderer(c, itemData); |
| // as the itemRenderer can move between listContent and listSubContent |
| // we need to fix the reparting here |
| addRendererToContentArea(item, c); |
| // add item to column map, so that they can be recycled |
| columnMap[item.name] = c; |
| } |
| else |
| { |
| item = listItems[currentRowNum][currentColNum]; |
| if (!item || itemToUID(item.data) != uid |
| || columnMap[item.name] != c) |
| { |
| item = getRenderer(c, itemData); |
| // as the itemRenderer can move between listContent and listSubContent |
| // we need to fix the reparting here |
| addRendererToContentArea(item, c); |
| columnMap[item.name] = c; |
| if (listItems[currentRowNum][currentColNum]) |
| addToFreeItemRenderers(listItems[currentRowNum][currentColNum]); |
| listItems[currentRowNum][currentColNum] = item; |
| } |
| } |
| |
| rowData = AdvancedDataGridListData(makeListData(itemData, uid, currentRowNum, c.colNum, c)); |
| rowMap[item.name] = rowData; |
| if (item is IDropInListItemRenderer) |
| { |
| IDropInListItemRenderer(item).listData = itemData ? rowData : null; |
| } |
| item.data = itemData; |
| item.visible = true; |
| setVisibleDataItem(uid, item); |
| row[currentColNum] = item ; |
| currentColNum++; |
| } |
| |
| // If we are opening AdvancedDataGrid Node then insert into listItems |
| if (insertItems) |
| listItems.splice(currentRowNum, 0, row); |
| } |
| |
| // Cell Selection methods |
| /** |
| * Return <code>true</code> if <code>selectedMode</code> is |
| * <code>SINGLE_ROW</code> or <code>MULTIPLE_ROWS</code>. |
| * |
| * @return <code>true</code> if <code>selectedMode</code> is |
| * <code>SINGLE_ROW</code> or <code>MULTIPLE_ROWS</code>. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function isRowSelectionMode():Boolean |
| { |
| return (selectionMode == SINGLE_ROW || selectionMode == MULTIPLE_ROWS); |
| } |
| |
| /** |
| * Returns <code>true</code> if <code>selectedMode</code> is |
| * <code>SINGLE_CELL</code> or <code>MULTIPLE_CELLS</code>. |
| * |
| * @return <code>true</code> if <code>selectedMode</code> is |
| * <code>SINGLE_CELL</code> or <code>MULTIPLE_CELLS</code>. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function isCellSelectionMode():Boolean |
| { |
| return (selectionMode == SINGLE_CELL || selectionMode == MULTIPLE_CELLS); |
| } |
| |
| /** |
| * Handle selection mode changing. |
| * |
| * @private |
| */ |
| protected function setSelectionMode(newSelectionMode:String):void |
| { |
| if (selectionMode == newSelectionMode) |
| return; |
| |
| if (newSelectionMode == NONE) |
| { |
| selectable = false; |
| } |
| else |
| { |
| if (!selectable) |
| selectable = true; |
| } |
| |
| if (newSelectionMode == SINGLE_ROW || newSelectionMode == SINGLE_CELL) |
| { |
| if (allowMultipleSelection) |
| allowMultipleSelection = false; |
| } |
| else if (newSelectionMode == MULTIPLE_ROWS || newSelectionMode == MULTIPLE_CELLS) |
| { |
| if (!allowMultipleSelection) |
| allowMultipleSelection = true; |
| } |
| else if (newSelectionMode != NONE) |
| { |
| // Default to single row selection mode |
| newSelectionMode = SINGLE_ROW; |
| if (allowMultipleSelection) |
| allowMultipleSelection = false; |
| } |
| |
| clearAllSelection(); |
| |
| _selectionMode = newSelectionMode; |
| } |
| |
| /** |
| * @private |
| */ |
| protected function selectionTween_updateHandler(event:TweenEvent):void |
| { |
| Sprite(event.target.listener).alpha = Number(event.value); |
| } |
| |
| /** |
| * @private |
| */ |
| protected function selectionTween_endHandler(event:TweenEvent):void |
| { |
| selectionTween_updateHandler(event); |
| } |
| |
| /** |
| * @private |
| */ |
| protected function onSelectionTweenUpdate(value:Number):void |
| { |
| } |
| |
| /** |
| * @private |
| */ |
| protected function addRendererToContentArea(item:IListItemRenderer, column:AdvancedDataGridColumn):void |
| { |
| if (column.colNum < lockedColumnCount) |
| { |
| if (item.parent != listContent) |
| listContent.addChild(DisplayObject(item)); |
| } |
| else |
| { |
| if (item.parent != listSubContent) |
| listSubContent.addChild(DisplayObject(item)); |
| } |
| } |
| |
| /** |
| * @private |
| * Returns the columns array which is optimum for the current context. |
| * If users are not using horizontal scrolling and rendererProviders/columnGrouping |
| * then visibleColumns array is best. If users are using any of the other features |
| * then displayableColumns need to be used. |
| */ |
| protected function getOptimumColumns():Array |
| { |
| return visibleColumns; |
| } |
| |
| /** |
| * @private |
| * |
| * Clear all the selected data. |
| * |
| */ |
| protected function clearAllSelection():void |
| { |
| if (isRowSelectionMode()) |
| { |
| clearSelected(); |
| clearIndicators(); |
| } |
| } |
| } |
| |
| } |