//////////////////////////////////////////////////////////////////////////////// // // 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.DisplayObjectContainer; import flash.display.GradientType; import flash.display.Graphics; import flash.display.Shape; import flash.display.Sprite; import flash.events.Event; import flash.events.FocusEvent; import flash.events.KeyboardEvent; import flash.events.MouseEvent; import flash.geom.Matrix; import flash.geom.Point; import flash.geom.Rectangle; import flash.ui.Keyboard; import flash.utils.Dictionary; import flash.utils.describeType; import flash.utils.getTimer; import mx.collections.CursorBookmark; import mx.collections.ICollectionView; import mx.collections.ISort; import mx.collections.ISortField; import mx.collections.IViewCursor; import mx.collections.ItemResponder; import mx.collections.Sort; import mx.collections.SortField; import mx.collections.errors.ItemPendingError; import mx.controls.advancedDataGridClasses.AdvancedDataGridBase; import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn; import mx.controls.advancedDataGridClasses.AdvancedDataGridDragProxy; import mx.controls.advancedDataGridClasses.AdvancedDataGridHeaderInfo; import mx.controls.advancedDataGridClasses.AdvancedDataGridHeaderRenderer; import mx.controls.advancedDataGridClasses.AdvancedDataGridItemRenderer; import mx.controls.advancedDataGridClasses.AdvancedDataGridListData; import mx.controls.advancedDataGridClasses.AdvancedDataGridSortItemRenderer; import mx.controls.advancedDataGridClasses.SortInfo; import mx.controls.listClasses.IDropInListItemRenderer; import mx.controls.listClasses.IListItemRenderer; import mx.controls.listClasses.ListBaseSeekPending; import mx.controls.listClasses.ListBaseSelectionData; import mx.controls.listClasses.ListRowInfo; import mx.controls.scrollClasses.ScrollBar; import mx.core.ClassFactory; import mx.core.ContextualClassFactory; import mx.core.EdgeMetrics; import mx.core.EventPriority; import mx.core.FlexShape; import mx.core.FlexSprite; import mx.core.IBorder; import mx.core.IFactory; import mx.core.IFlexDisplayObject; import mx.core.IFlexModuleFactory; import mx.core.IIMESupport; import mx.core.IInvalidating; import mx.core.IPropertyChangeNotifier; import mx.core.IUIComponent; import mx.core.LayoutDirection; import mx.core.ScrollPolicy; import mx.core.UIComponent; import mx.core.UIComponentGlobals; import mx.core.mx_internal; import mx.events.AdvancedDataGridEvent; import mx.events.AdvancedDataGridEventReason; import mx.events.CollectionEvent; import mx.events.CollectionEventKind; import mx.events.DragEvent; import mx.events.IndexChangedEvent; import mx.events.ListEvent; import mx.events.SandboxMouseEvent; import mx.events.ScrollEvent; import mx.events.ScrollEventDetail; import mx.events.ScrollEventDirection; import mx.managers.CursorManager; import mx.managers.CursorManagerPriority; import mx.managers.IFocusManager; import mx.managers.IFocusManagerComponent; import mx.skins.halo.DataGridColumnDropIndicator; import mx.styles.ISimpleStyleClient; import mx.utils.ObjectUtil; import mx.utils.StringUtil; use namespace mx_internal; //-------------------------------------- // Events //-------------------------------------- /** * Dispatched when the user releases the mouse button while over an item * renderer, tabs to the AdvancedDataGrid control or within the AdvancedDataGrid control, * or in any other way attempts to edit an item. * * @eventType mx.events.AdvancedDataGridEvent.ITEM_EDIT_BEGINNING * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Event(name="itemEditBeginning", type="mx.events.AdvancedDataGridEvent")] /** * Dispatched when the editedItemPosition property has been set * and the item can be edited. * * @eventType mx.events.AdvancedDataGridEvent.ITEM_EDIT_BEGIN * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Event(name="itemEditBegin", type="mx.events.AdvancedDataGridEvent")] /** * Dispatched when an item editing session ends for any reason. * * @eventType mx.events.AdvancedDataGridEvent.ITEM_EDIT_END * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Event(name="itemEditEnd", type="mx.events.AdvancedDataGridEvent")] /** * Dispatched when an item renderer gets focus, which can occur if the user * clicks on an item in the AdvancedDataGrid control or navigates to the item using * a keyboard. Only dispatched if the item is editable. * * @eventType mx.events.AdvancedDataGridEvent.ITEM_FOCUS_IN * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Event(name="itemFocusIn", type="mx.events.AdvancedDataGridEvent")] /** * Dispatched when an item renderer loses focus, which can occur if the user * clicks another item in the AdvancedDataGrid control or clicks outside the control, * or uses the keyboard to navigate to another item in the AdvancedDataGrid control * or outside the control. * Only dispatched if the item is editable. * * @eventType mx.events.AdvancedDataGridEvent.ITEM_FOCUS_OUT * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Event(name="itemFocusOut", type="mx.events.AdvancedDataGridEvent")] /** * Dispatched when a user changes the width of a column, indicating that the * amount of data displayed in that column may have changed. * If horizontalScrollPolicy is "none", other * columns shrink or expand to compensate for the columns' resizing, * and they also dispatch this event. * * @eventType mx.events.AdvancedDataGridEvent.COLUMN_STRETCH * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Event(name="columnStretch", type="mx.events.AdvancedDataGridEvent")] /** * Dispatched when the user releases the mouse button on a column header * to request the control to sort * the grid contents based on the contents of the column. * Only dispatched if the column is sortable and the data provider supports * sorting. The AdvancedDataGrid control has a default handler for this event that implements * a single-column sort. Multiple-column sort can be implemented by calling the * preventDefault() method to prevent the single column sort and setting * the sort property of the data provider. *

* Note: The sort arrows are defined by the default event handler for * the headerRelease event. If you call the preventDefault() method * in your event handler, the arrows are not drawn. *

* * @eventType mx.events.AdvancedDataGridEvent.HEADER_RELEASE * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Event(name="headerRelease", type="mx.events.AdvancedDataGridEvent")] /** * Dispatched when sorting is to be performed on the AdvancedDataGrid control. * * @eventType mx.events.AdvancedDataGridEvent.SORT * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Event(name="sort", type="mx.events.AdvancedDataGridEvent")] /** * Dispatched when the user releases the mouse button on a column header after * having dragged the column to a new location resulting in shifting the column * to a new index * * @eventType mx.events.IndexChangedEvent.HEADER_SHIFT * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Event(name="headerShift", type="mx.events.IndexChangedEvent")] //-------------------------------------- // Styles //-------------------------------------- include "../styles/metadata/IconColorStyles.as" /** * Name of the class of the itemEditor to be used if one is not * specified for a column. This is a way to set * an item editor for a group of AdvancedDataGrids instead of having to * set each one individually. If you set the AdvancedDataGridColumn's itemEditor * property, it supercedes this value. * @default null * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion Flex 4 */ [Style(name="defaultDataGridItemEditor", type="Class", inherit="no")] /** * Name of the class of the itemRenderer to be used if one is not * specified for a column. This is a way to set * an itemRenderer for a group of AdvancedDataGrids instead of having to * set each one individually. If you set the AdvancedDataGrid's itemRenderer * property, it supercedes this value. * @default null * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion Flex 4 */ [Style(name="defaultDataGridItemRenderer", type="Class", inherit="no")] /** * A flag that indicates whether to show vertical grid lines between * the columns. * If true, shows vertical grid lines. * If false, hides vertical grid lines. * @default true * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="verticalGridLines", type="Boolean", inherit="no")] /** * A flag that indicates whether to show horizontal grid lines between * the rows. * If true, shows horizontal grid lines. * If false, hides horizontal grid lines. * @default false * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="horizontalGridLines", type="Boolean", inherit="no")] /** * The color of the vertical grid lines. * @default 0x666666 * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="verticalGridLineColor", type="uint", format="Color", inherit="yes")] /** * The color of the horizontal grid lines. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="horizontalGridLineColor", type="uint", format="Color", inherit="yes")] /** * An array of two colors used to draw the header background gradient. * The first color is the top color. * The second color is the bottom color. * @default [0xFFFFFF, 0xE6E6E6] * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="headerColors", type="Array", arrayType="uint", format="Color", inherit="yes")] /** * The color of the row background when the user rolls over the row. * @default 0xE3FFD6 * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="rollOverColor", type="uint", format="Color", inherit="yes")] /** * The color of the background for the row when the user selects * an item renderer in the row. * @default 0xCDFFC1 * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="selectionColor", type="uint", format="Color", inherit="yes")] /** * The name of a CSS style declaration for controlling other aspects of * the appearance of the column headers. * @default "dataGridStyles" * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="headerStyleName", type="String", inherit="no")] /** * The class to use as the skin for a column that is being resized. * * @default mx.skins.halo.DataGridColumnResizeSkin (for both Halo and Spark themes) * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="columnResizeSkin", type="Class", inherit="no")] /** * The class to use as the skin that defines the appearance of the * background of the column headers in a AdvancedDataGrid control. * *

The default skin class is based on the theme. For example, with the Halo theme, * the default skin class is mx.skins.halo.DataGridHeaderBackgroundSkin. For the Spark theme, the default skin * class is mx.skins.spark.DataGridHeaderBackgroundSkin.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="headerBackgroundSkin", type="Class", inherit="no")] /** * The class to use as the skin that defines the appearance of the * separator between column headers in a AdvancedDataGrid control. * *

The default skin class is based on the theme. For example, with the Halo theme, * the default skin class is mx.skins.halo.DataGridHeaderSeparator. For the Spark theme, the default skin * class is mx.skins.spark.DataGridHeaderSeparatorSkin.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="headerSeparatorSkin", type="Class", inherit="no")] /** * The class to use as the skin that defines the appearance of the * separator between a column group and its children columns/column group headers * in an AdvancedDataGrid control. * @default mx.skins.halo.AdvancedDataGridHeaderHorizontalSeparator * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="headerHorizontalSeparatorSkin", type="Class", inherit="no")] /** * The class to use as the skin that defines the appearance of the * separator between rows in a AdvancedDataGrid control. * By default, the AdvancedDataGrid control uses the * drawHorizontalLine() and drawVerticalLine() methods * to draw the separators. * * @default undefined * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="horizontalSeparatorSkin", type="Class", inherit="no")] /** * The class to use as the skin that defines the appearance of the * separator between the locked and unlocked rows in a AdvancedDataGrid control. * By default, the AdvancedDataGrid control uses the * drawHorizontalLine() and drawVerticalLine() methods * to draw the separators. * * @default undefined * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="horizontalLockedSeparatorSkin", type="Class", inherit="no")] /** * The class to use as the skin that defines the appearance of the * separators between columns in a AdvancedDataGrid control. * By default, the AdvancedDataGrid control uses the * drawHorizontalLine() and drawVerticalLine() methods * to draw the separators. * * @default undefined * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="verticalSeparatorSkin", type="Class", inherit="no")] /** * The class to use as the skin that defines the appearance of the * separator between the locked and unlocked columns in a AdvancedDataGrid control. * By default, the AdvancedDataGrid control uses the * drawHorizontalLine() and drawVerticalLine() methods * to draw the separators. * * @default undefined * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="verticalLockedSeparatorSkin", type="Class", inherit="no")] /** * The class to use as the skin for the cursor that indicates that a column * can be resized. * @default mx.skins.halo.DataGridStretchCursor * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="stretchCursor", type="Class", inherit="no")] /** * The class to use as the skin that indicates that * a column can be dropped in the current location. * * @default mx.skins.halo.DataGridColumnDropIndicator (for both Halo and Spark themes) * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="columnDropIndicatorSkin", type="Class", inherit="no")] /** * The name of a CSS style declaration for controlling aspects of the * appearance of column when the user is dragging it to another location. * * @default "headerDragProxyStyle" * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Style(name="headerDragProxyStyleName", type="String", inherit="no")] //-------------------------------------- // Excluded APIs //-------------------------------------- [Exclude(name="columnCount", kind="property")] [Exclude(name="labelField", kind="property")] [Exclude(name="offscreenExtraRowsOrColumns", kind="property")] [Exclude(name="offscreenExtraRows", kind="property")] [Exclude(name="offscreenExtraRowsTop", kind="property")] [Exclude(name="offscreenExtraRowsBottom", kind="property")] [Exclude(name="offscreenExtraColumns", kind="property")] [Exclude(name="offscreenExtraColumnsLeft", kind="property")] [Exclude(name="offscreenExtraColumnsRight", kind="property")] [Exclude(name="offscreenExtraRowsOrColumnsChanged", kind="property")] [Exclude(name="maxHorizontalScrollPosition", kind="property")] [Exclude(name="maxVerticalScrollPosition", kind="property")] [Exclude(name="showDataTips", kind="property")] [Exclude(name="cornerRadius", kind="style")] //-------------------------------------- // Other metadata //-------------------------------------- [DataBindingInfo("acceptedTypes", "{ dataProvider: "String" }")] [DefaultBindingProperty(source="selectedItem", destination="dataProvider")] [DefaultProperty("dataProvider")] [DefaultTriggerEvent("change")] [IconFile("AdvancedDataGrid.png")] [RequiresDataBinding(true)] /** * The AdvancedDataGridBaseEx class is a base class of the AdvancedDataGrid control. * This class contains code that provides functionality similar to the DataGrid control. * * @mxml *

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

*
 *  <mx:AdvancedDataGridBaseEx
 *    Properties
 *    columns="From dataProvider"
 *    draggableColumns="true|false"
 *    editable="item group summary"
 *    editedItemPosition="null"
 *    horizontalScrollPosition="null"
 *    imeMode="null"
 *    itemEditorInstance="null"
 *    lookAheadDuratio="400"
 *    minColumnWidth="NaN"
 *    resizableColumns="true|false"
 *    sortableColumns="true|false"
 *    sortExpertMode="false|true"
 *     
 *    Styles
 *    columnDropIndicatorSkin="DataGridColumnDropIndicator"
 *    columnResizeSkin="DataGridColumnResizeSkin"
 *    disabledIconColor="0x999999"
 *    headerBackgroundSkin="DataGridHeaderSeparator"
 *    headerColors="[#FFFFFF, #E6E6E6]"
 *    headerDragProxyStyleName="headerDragProxyStyle"
 *    headerHorizontalSeparatorSkin="AdvancedDataGridHeaderHorizontalSeparator"
 *    headerSeparatorSkin="DataGridHeaderSeparator"
 *    headerStyleName="No default"
 *    horizontalGridLineColor="No default"
 *    horizontalGridLines="false|true"
 *    horizontalLockedSeparatorSkin="undefined"
 *    horizontalSeparatorSkin="undefined"
 *    iconColor="0x111111"
 *    rollOverColor="#E3FFD6"
 *    selectionColor="#CDFFC1"
 *    stretchCursor="DataGridStretchCursor"
 *    verticalGridLineColor="#666666"
 *    verticalGridLines="false|true"
 *    verticalLockedSeparatorSkin="undefined"
 *    verticalSeparatorSkin="undefined"
 *     
 *    Events
 *    columnStretch="No default"
 *    headerRelease="No default"
 *    headerShift="No default"
 *    itemEditBegin="No default"
 *    itemEditBeginning="No default" 
 *    itemEditEnd="No default"
 *    itemFocusIn="No default"
 *    itemFocusOut="No default"
 *  />
 *   
 *  The following AdvancedDataGrid code sample specifies the column order:
 *  <mx:AdvancedDataGrid>
 *    <mx:dataProvider>
 *        <mx:Object Artist="Pavement" Price="11.99"
 *          Album="Slanted and Enchanted"/>
 *        <mx:Object Artist="Pavement"
 *          Album="Brighten the Corners" Price="11.99"/>
 *    </mx:dataProvider>
 *    <mx:columns>
 *        <mx:AdvancedDataGridColumn dataField="Album"/>
 *        <mx:AdvancedDataGridColumn dataField="Price"/>
 *    </mx:columns>
 *  </mx:AdvancedDataGrid>
 *  
*

* * @see mx.controls.AdvancedDataGrid * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public class AdvancedDataGridBaseEx extends AdvancedDataGridBase implements IIMESupport { include "../core/Version.as"; //-------------------------------------------------------------------------- // // Class constants // //-------------------------------------------------------------------------- /** * @private * TODO!!! Replace with global versioning infrastructure */ public static var useOldDGHeaderBGLogic:Boolean = false; //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * Constructor. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function AdvancedDataGridBaseEx() { super(); _columns = []; headerRenderer = new ClassFactory(AdvancedDataGridHeaderRenderer); sortItemRenderer = new ClassFactory(AdvancedDataGridSortItemRenderer); // pick a default row height setRowHeight(20); // Register default handlers for item editing and sorting events. addEventListener(AdvancedDataGridEvent.ITEM_EDIT_BEGINNING, itemEditorItemEditBeginningHandler, false, EventPriority.DEFAULT_HANDLER); addEventListener(AdvancedDataGridEvent.ITEM_EDIT_BEGIN, itemEditorItemEditBeginHandler, false, EventPriority.DEFAULT_HANDLER); addEventListener(AdvancedDataGridEvent.ITEM_EDIT_END, itemEditorItemEditEndHandler, false, EventPriority.DEFAULT_HANDLER); addEventListener(AdvancedDataGridEvent.HEADER_RELEASE, headerReleaseHandler, false, EventPriority.DEFAULT_HANDLER); addEventListener(AdvancedDataGridEvent.SORT, sortHandler, false, EventPriority.DEFAULT_HANDLER); addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- /** * @private * true if we want to block editing on mouseUp */ private var dontEdit:Boolean = false; /** * @private * true if we want to block editing on mouseUp */ private var losingFocus:Boolean = false; /** * @private */ private var _focusPane:Sprite; /** * @private * true if we're in the endEdit call. Used to handle * some timing issues with collection updates */ private var inEndEdit:Boolean = false; /** * @private * true if we've disabled updates in the collection */ private var collectionUpdatesDisabled:Boolean = false; /** * Specifies a graphic that shows the proposed column width as the user stretches it. * * @private */ private var resizeGraphic:IFlexDisplayObject; // /** * @private * A tmp var to store the stretching col's X coord. */ private var startX:Number; /** * @private * A tmp var to store the stretching col's min X coord for column's minWidth. */ private var minX:Number; /** * @private * A tmp var to store the last point (in dataGrid coords) received while dragging. */ private var lastPt:Point; /** * @private * List of header separators for column resizing. */ private var separators:Array; /** * @private * List of header separators for column resizing in the locked column area. */ protected var lockedSeparators:Array; /** * @private * The column that is being resized. */ private var resizingColumn:AdvancedDataGridColumn; /** * @private * The index of the column being sorted. */ private var sortIndex:int = -1; /** * @private * The column being sorted. */ private var sortColumn:AdvancedDataGridColumn; /** * @private * The direction of the sort */ private var sortDirection:String; /** * @private * The index of the last column being sorted on. */ private var lastSortIndex:int = -1; /** * @private */ private var lastItemDown:IListItemRenderer; /** * @private * The column that is being moved. */ protected var movingColumn:AdvancedDataGridColumn; /** * @private * Index of column before which to drop */ protected var dropColumnIndex:int = -1; /** * @private */ mx_internal var columnDropIndicator:IFlexDisplayObject; /** * @private */ private var displayWidth:Number; /** * @private * Additional affordance given to header separators. */ private var separatorAffordance:Number = 3; /** * @private * Columns with visible="true" */ protected var displayableColumns:Array; /** * @private * Whether we have auto-generated the set of columns * Defaults to true so we'll run the auto-generation at init time if needed */ protected var generatedColumns:Boolean = true; /** * @private * A hash table of objects used to calculate sizes */ protected var measuringObjects:Dictionary; /** * @private */ private var resizeCursorID:int = CursorManager.NO_CURSOR; // last known position of item editor instance private var actualRowIndex:int; private var actualColIndex:int; /** * @private * Flag to indicate whether sorting is manual or programmatic. If it's * not manual, we try to draw the sort arrow on the right column header. */ private var manualSort:Boolean; /** * An ordered list of AdvancedDataGridHeaderInfo instances that * correspond to the visible column headers. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected var orderedHeadersList:Array = []; /** * Contains true if the headerInfos property * has been initialized with AdvancedDataGridHeaderInfo instances. * * @see mx.controls.advancedDataGridClasses.AdvancedDataGridHeaderInfo * * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected var headerInfoInitialized:Boolean = false; /** * Contains true if a key press is in progress. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected var isKeyPressed:Boolean = false; /** * @private * Stores the last typed character(s) * for multiple characters type ahead lookup. */ private var lookAheadCache:String = ""; /** * @private * Stores the time of the last typed character * for multiple characters type ahead lookup. */ private var previousTime:uint; private var headerBGSkinChanged:Boolean = false; private var headerSepSkinChanged:Boolean = false; private var columnsChanged:Boolean = false; /** * @private * Set to true when the view is scrolled and * optimumColumns != visibleColumns */ private var subContentScrolled:Boolean = false; /** * @private */ private var minColumnWidthInvalid:Boolean = false; /** * @private */ private var bEditedItemPositionChanged:Boolean = false; /** * @private * undefined means we've processed it * null means don't put up an editor * {} is the coordinates for the editor */ private var _proposedEditedItemPosition:*; /** * @private * the last editedItemPosition. We restore editing * to this point if we get focus from the TAB key */ private var lastEditedItemPosition:*; private var _headerWordWrapPresent:Boolean = false; private var _originalExplicitHeaderHeight:Boolean = false; private var _originalHeaderHeight:Number = 0; /** * @private * true if based on mouse position, a dropIndex has been found */ private var dropIndexFound:Boolean = false; /** * @private * true if header getting dragged is outside the permissible area */ private var isHeaderDragOutside:Boolean = false; /** * The AdvancedDataGridHeaderInfo instances that * correspond to the currently selected column header. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ mx_internal var selectedHeaderInfo:AdvancedDataGridHeaderInfo; //-------------------------------------------------------------------------- // // Overridden properties // //-------------------------------------------------------------------------- //---------------------------------- // itemRenderer //---------------------------------- /** * @private * * Defer creation of the class factory to give a chance for the moduleFactory to be set. */ override public function get itemRenderer():IFactory { if (super.itemRenderer == null) { var fontName:String = StringUtil.trimArrayElements(getStyle("fontFamily"),","); var fontWeight:String = getStyle("fontWeight"); var fontStyle:String = getStyle("fontStyle"); var bold:Boolean = (fontWeight == "bold"); var italic:Boolean = (fontStyle == "italic"); var flexModuleFactory:IFlexModuleFactory = getFontContext(fontName, bold, italic); var c:Class = getStyle("defaultDataGridItemRenderer"); if (!c) c = AdvancedDataGridItemRenderer; super.itemRenderer = new ContextualClassFactory(c, flexModuleFactory); } return super.itemRenderer; } //---------------------------------- // baselinePosition //---------------------------------- /** * @private */ override public function get baselinePosition():Number { var top:Number = 0; if (border && border is IBorder) top = IBorder(border).borderMetrics.top; return top + measureText(" ").ascent; } /** * @private * Number of columns that can be displayed. * Some may be offscreen depending on horizontalScrollPolicy * and the width of the AdvancedDataGrid. */ override public function get columnCount():int { if (_columns) return _columns.length; else return 0; } //---------------------------------- // enabled //---------------------------------- [Inspectable(category="General", enumeration="true,false", defaultValue="true")] /** * @private */ override public function set enabled(value:Boolean):void { super.enabled = value; if (itemEditorInstance) endEdit(AdvancedDataGridEventReason.OTHER); invalidateDisplayList(); } //---------------------------------- // horizontalScrollPosition //---------------------------------- /** * The offset into the content from the left edge. * This can be a pixel offset in some subclasses or some other metric * like the number of columns in an AdvancedDataGrid control. * * The AdvancedDataGrid scrolls by columns so the value of the * horizontalScrollPosition property is always * in the range of 0 to the index of the columns * that will make the last column visible. * This is different from the List control, which scrolls by pixels. * The AdvancedDataGrid control always aligns the left edge * of a column with the left edge of the AdvancedDataGrid control. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ override public function set horizontalScrollPosition(value:Number):void { // if not init or no data; if (!initialized || listItems.length == 0) { super.horizontalScrollPosition = value; return; } var oldValue:int = super.horizontalScrollPosition; super.horizontalScrollPosition = value; columnsInvalid = true; calculateColumnSizes(); // we are going to get a full repaint so don't repaint now if (itemsSizeChanged) return; if (oldValue != value) { removeClipMask(); if (getOptimumColumns() == visibleColumns) { //clearIndicators(); visibleData = {}; // columns have variable width so we need to recalc scroll parms scrollAreaChanged = true; var bookmark:CursorBookmark; if (iterator) bookmark = iterator.bookmark; //if we scrolled more than the number of scrollable columns makeRowsAndColumns(0, 0, listContent.width, listContent.height, 0, 0); if (iterator && bookmark) iterator.seek(bookmark, 0); } else { // In case of column grouping and // column span we just move the scroll rect subContentScrolled = true; } updateSubContent(); updateHeaderSearchList(); addClipMask(false); //an invalidation is needed, to redraw the vertical lines and separators invalidateDisplayList(); } } //---------------------------------- // verticalScrollPosition //---------------------------------- /** * @private * Sets verticalScrollPosition and draw horizontal lines again * variableRowHeight is true. */ override public function set verticalScrollPosition(value:Number):void { super.verticalScrollPosition = value; // draw the horizontal lines afresh if variableRowHeight is true // i.e., row height may differ for each row if (variableRowHeight) drawHorizontalSeparators(); } //---------------------------------- // focusPane //---------------------------------- /** * @private */ override public function set focusPane(value:Sprite):void { super.focusPane = value; if (value) value.scrollRect = listSubContent ? listSubContent.scrollRect : null; if (!value && _focusPane) _focusPane.mask = null; _focusPane = value; } //---------------------------------- // horizontalScrollPolicy //---------------------------------- /** * @private * Accomodates ScrollPolicy.AUTO. * Makes sure column widths stay in synch. * * @param policy on, off, or auto */ override public function set horizontalScrollPolicy(value:String):void { super.horizontalScrollPolicy = value; columnsInvalid = true; itemsSizeChanged = true; invalidateDisplayList(); } //---------------------------------- // lockedColumnCount //---------------------------------- /** * @private */ override public function set lockedColumnCount(value:int):void { var i:int = 0; var j:int = 0; var m:int = 0; // remove the items from columnMap, so that they can be created again if (value > super.lockedColumnCount) { for (i = super.lockedColumnCount; i < value ;i++) { m = listItems.length; for(j = 0; j < m; j++) { if (listItems[j] && listItems[j][i]) delete columnMap[listItems[j][i].name]; } } } else if (value < super.lockedColumnCount) { for (i = value; i < super.lockedColumnCount ;i++) { m = listItems.length; for(j = 0; j < m; j++) { if (listItems[j] && listItems[j][i]) delete columnMap[listItems[j][i].name]; } } } super.lockedColumnCount = value; //listSubContent scrollRectneed to be changed in case lockedColumnCount has changed // otherwise items in the scrollrect overlap with the items which have come // because of change in lockedColumnCount updateSubContent(); itemsSizeChanged = true; columnsInvalid = true; // set the horizontalScrollPosition so that all the changes are reflected correctly horizontalScrollPosition = super.horizontalScrollPosition; } //---------------------------------- // dragImage //---------------------------------- /** * @private */ override protected function get dragImage():IUIComponent { var image:AdvancedDataGridDragProxy = new AdvancedDataGridDragProxy(); image.owner = this; image.moduleFactory = moduleFactory; return image; } //-------------------------------------------------------------------------- // // Properties // //-------------------------------------------------------------------------- //---------------------------------- // columns //---------------------------------- /** * @private */ // Added to AdvancedDataGridBase //private var _columns:Array; // the array of our AdvancedDataGridColumns [Bindable("columnsChanged")] [Inspectable(category="General", arrayType="mx.controls.advancedDataGridClasses.AdvancedDataGridColumn")] /** * An array of AdvancedDataGridColumn objects, one for each column that * can be displayed. If not explicitly set, the AdvancedDataGrid control * attempts to examine the first data provider item to determine the * set of properties and display those properties in alphabetic * order. * *

If you want to change the set of columns, you must get this Array, * make modifications to the columns and order of columns in the Array, * and then assign the new Array to the columns property. This is because * the AdvancedDataGrid control returns a copy of the Array of columns, * not a reference, and therefore cannot detect changes to the copy.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get columns():Array { return _columns.slice(0); } /** * @private */ public function set columns(value:Array):void { var n:int; var i:int; // remove the header items purgeHeaderRenderers(); n = _columns.length; for (i = 0; i < n; i++) { columnRendererChanged(_columns[i]); } freeItemRenderersTable = new Dictionary(false); itemRendererToFactoryMap = new Dictionary(true); columnMap = {}; _columns = value.slice(0); columnsInvalid = true; generatedColumns = false; n = value.length; for (i = 0; i < n; i++) { var column:AdvancedDataGridColumn = _columns[i]; column.owner = this; column.colNum = i; } updateSortIndexAndDirection(); itemsSizeChanged = true; columnsChanged = true; invalidateDisplayList(); dispatchEvent(new Event("columnsChanged")); } //---------------------------------- // draggableColumns //---------------------------------- /** * @private * Storage for the draggableColumns property. */ private var _draggableColumns:Boolean = true; [Inspectable(defaultValue="true")] /** * Indicates whether you are allowed to reorder columns. * If true, you can reorder the columns * of the AdvancedDataGrid control by dragging the header cells. * * @default true * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get draggableColumns():Boolean { return _draggableColumns; } /** * @private */ public function set draggableColumns(value:Boolean):void { _draggableColumns = value; } //---------------------------------- // enableIME //---------------------------------- /** * A flag that indicates whether the IME should * be enabled when the component receives focus. * * If the editor is up, it will set enableIME * accordingly. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion Flex 4 */ public function get enableIME():Boolean { return false; } //---------------------------------- // imeMode //---------------------------------- /** * @private */ private var _imeMode:String = null; [Inspectable(environment="none")] /** * Specifies the IME (input method editor) mode. * The IME mode enables users to enter text in Chinese, Japanese, and Korean. * Flex sets the specified IME mode when the control gets the focus, * and sets it back to the previous value when the control loses the focus. * *

The flash.system.IMEConversionMode class defines constants for the * valid values for this property. * You can also specify null to specify no IME.

* * @see flash.system.IMEConversionMode * * @default null * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get imeMode():String { return _imeMode; } /** * @private */ public function set imeMode(value:String):void { _imeMode = value; } //---------------------------------- // minColumnWidth //---------------------------------- /** * @private */ private var _minColumnWidth:Number; [Inspectable(defaultValue="NaN")] /** * The minimum width of the columns, in pixels. If not NaN, * the AdvancedDataGrid control applies this value as the minimum width for * all columns. Otherwise, individual columns can have * their own minimum widths. * * @default NaN * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get minColumnWidth():Number { return _minColumnWidth; } /** * @private */ public function set minColumnWidth(value:Number):void { _minColumnWidth = value; minColumnWidthInvalid = true; itemsSizeChanged = true; columnsInvalid = true; invalidateDisplayList(); } //---------------------------------- // itemEditorInstance //---------------------------------- [Inspectable(environment="none")] /** * A reference to the currently active instance of the item editor, * if it exists. * *

To access the item editor instance and the new item value when an * item is being edited, you use the itemEditorInstance * property. The itemEditorInstance property * is not valid until after the event listener for * the itemEditBegin event executes. Therefore, you typically * only access the itemEditorInstance property from within * the event listener for the itemEditEnd event.

* *

The AdvancedDataGridColumn.itemEditor property defines the * class of the item editor, * and therefore the data type of the item editor instance.

* *

You do not set this property in MXML.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public var itemEditorInstance:IListItemRenderer; //---------------------------------- // editedItemRenderer //---------------------------------- /** * A reference to the item renderer * in the AdvancedDataGrid control whose item is currently being edited. * *

From within an event listener for the itemEditBegin * and itemEditEnd events, * you can access the current value of the item being edited * using the editedItemRenderer.data property.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get editedItemRenderer():IListItemRenderer { if (!itemEditorInstance) return null; return listItems[actualRowIndex][actualColIndex]; } //---------------------------------- // headerIndex //---------------------------------- /** * @private * Storage for headerIndex */ private var _headerIndex:int = -1; /** * If a header is selected via keyboard. * * headerIndex is the absolute column number i.e. index of 'columns'. * * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ mx_internal function set headerIndex(value:int):void { _headerIndex = value; dispatchEvent(new ListEvent(ListEvent.CHANGE)); } mx_internal function get headerIndex():int { return _headerIndex; } //---------------------------------- // editable //---------------------------------- private var _editable:String = ""; [Inspectable(category="General")] /** * Indicates whether or not the user can edit items in the data provider. * *

If "item", the item renderers in the control are editable. * The user can click on an item renderer to open an editor.

* *

If "item group", the item renderers and grouping headers can be edited.

* *

If "item summary", the item renderers and summary cells can be edited.

* *

You can combine these values. For example, editable = "item group summary". * Note that item editing has to be enabled if enabling group or summary editing.

* *

If you specify an empty String, no editing is allowed.

* *

The values "true" and "false" correspond * to item editing and no editing.

* *

A value of "all" means everything is editable.

* *

You can turn off editing for individual columns of the * AdvancedDataGrid control using the AdvancedDataGridColumn.editable property, * or by handling the itemEditBeginning and * itemEditBegin events.

* * @default "" * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get editable():String { return _editable; } public function set editable(value:String):void { _editable = ""; if (!value) return; var editableFlags:Array = value.split(" "); // space delimited var n:int = editableFlags.length; var keepProcessingFlags:Boolean = true; for (var i:int = 0; i < n && keepProcessingFlags; i++) { switch (editableFlags[i]) { case "item": case "group": case "summary": { _editable += editableFlags[i] + " "; break; } case "true": { _editable = "item" + " "; keepProcessingFlags = false; break; } case "false": { _editable = "" + " "; keepProcessingFlags = false; break; } case "all": { _editable = "item group summary" + " "; keepProcessingFlags = false; break; } } } _editable = _editable.slice(0, -1); // remove trailing space } //---------------------------------- // editedItemPosition //---------------------------------- /** * @private */ private var _editedItemPosition:Object; [Bindable("itemFocusIn")] /** * The column and row index of the item renderer for the * data provider item being edited, if any. * *

This Object has two fields, columnIndex and * rowIndex, * the zero-based column and row indexes of the item. * For example: {columnIndex:2, rowIndex:3}

* *

Setting this property scrolls the item into view and * dispatches the itemEditBegin event to * open an item editor on the specified item renderer.

* * @default null * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get editedItemPosition():Object { if (_editedItemPosition) return {rowIndex: _editedItemPosition.rowIndex, columnIndex: _editedItemPosition.columnIndex}; else return _editedItemPosition; } /** * @private */ public function set editedItemPosition(value:Object):void { if (!value) { setEditedItemPosition(null); return; } var newValue:Object = {rowIndex: value.rowIndex, columnIndex: value.columnIndex}; setEditedItemPosition(newValue); } //---------------------------------- // lookAheadDuration //---------------------------------- [Inspectable(defaultValue="400")] /** * The type look-ahead duration, in milliseconds, for multi-character look ahead. * Setting it to 0 will turn off multiple character type ahead lookup. * * @default 400 * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public var lookAheadDuration:Number = 400; //---------------------------------- // resizableColumns //---------------------------------- [Inspectable(category="General")] /** * A flag that indicates whether the user can change the size of the * columns. * If true, the user can stretch or shrink the columns of * the AdvancedDataGrid control by dragging the grid lines between the header cells. * If true, individual columns must also have their * resizeable properties set to false to * prevent the user from resizing a particular column. * * @default true * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public var resizableColumns:Boolean = true; //---------------------------------- // sortableColumns //---------------------------------- [Inspectable(category="General")] /** * A flag that indicates whether the user can sort the data provider items * by clicking on a column header cell. * If true, the user can sort the data provider items by * clicking on a column header cell. * The AdvancedDataGridColumn.dataField property of the column * or the AdvancedDataGridColumn.sortCompareFunction property * of the column is used as the sort field. * If a column is clicked more than once, * the sort alternates between ascending and descending order. * If true, individual columns can be made to not respond * to a click on a header by setting the column's sortable * property to false. * *

When a user releases the mouse button over a header cell, the AdvancedDataGrid * control dispatches a headerRelease event if both * this property and the column's sortable property are true. * If no handler calls the preventDefault() method on the event, the * AdvancedDataGrid sorts using that column's AdvancedDataGridColumn.dataField or * AdvancedDataGridColumn.sortCompareFunction properties.

* * @default true * * @see mx.controls.advancedDataGridClasses.AdvancedDataGridColumn#dataField * @see mx.controls.advancedDataGridClasses.AdvancedDataGridColumn#sortCompareFunction * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public var sortableColumns:Boolean = true; //---------------------------------- // sortExpertMode //---------------------------------- // Type of sorting UI displayed private var _sortExpertMode:Boolean = false; /** * By default, the sortExpertMode property is set to false, * which means you click in the header area of a column to sort the rows of * the AdvancedDataGrid control by that column. * You then click in the multiple-column sort area of the header to sort by additional columns. * If you set the sortExpertMode property to true, * you use the Control key to select every column after the first column to perform sort. * * @default false * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ [Inspectable(enumeration="true,false", defaultValue="false")] public function get sortExpertMode():Boolean { return _sortExpertMode; } /** * @private */ public function set sortExpertMode(value:Boolean):void { _sortExpertMode = value; invalidateHeaders(); invalidateProperties(); invalidateDisplayList(); } //-------------------------------------------------------------------------- // // Overridden methods // //-------------------------------------------------------------------------- [Inspectable(category="Data", defaultValue="undefined")] /** * @private */ override public function set dataProvider(value:Object):void { if (itemEditorInstance) endEdit(AdvancedDataGridEventReason.OTHER); lastEditedItemPosition = null; super.dataProvider = value; invalidateProperties(); } /** * @private * Adds support for multiple characters type ahead lookup. */ override protected function findKey(eventCode:int):Boolean { var tmpCode:int = eventCode; // get the timer value now var now:uint = getTimer(); var str:String = String.fromCharCode(tmpCode); if (!(tmpCode >= 33 && tmpCode <= 126)) return false; // store the value of the _selectedIndex var selIndex:Number = _selectedIndex; // compare the timer value with the previously stored // timer value and set up multiple character type ahead // lookup. if ((now - previousTime) < lookAheadDuration) { str = lookAheadCache + str; lookAheadCache = str; previousTime = now; // decrement the _selecteIndex // we want the lookup to start from the previous item if (_selectedIndex > 0) { selIndex = _selectedIndex; _selectedIndex--; } } else { previousTime = now; lookAheadCache = str; } var selectionChanged:Boolean = findString(str); // set the _selectedIndex back if we cant find the item if (!selectionChanged && _selectedIndex != selIndex) _selectedIndex = selIndex; return selectionChanged; } /** * @private * Measures the AdvancedDataGrid based on its contents, * summing the total of the visible column widths. */ override protected function measure():void { super.measure(); var o:EdgeMetrics = viewMetrics; var n:int = columns.length; if (n == 0) { measuredWidth = DEFAULT_MEASURED_WIDTH; measuredMinWidth = DEFAULT_MEASURED_MIN_WIDTH; return; } var columnWidths:Number = 0; var columnMinWidths:Number = 0; for (var i:int = 0; i < n; i++) { if (columns[i].visible) { columnWidths += columns[i].preferredWidth; if (isNaN(_minColumnWidth)) columnMinWidths += columns[i].minWidth; } } if (!isNaN(_minColumnWidth)) columnMinWidths = n * _minColumnWidth; measuredWidth = columnWidths + o.left + o.right; measuredMinWidth = columnMinWidths + o.left + o.right; // factor out scrollbars if policy == AUTO. See Container.viewMetrics if (verticalScrollPolicy == ScrollPolicy.AUTO && verticalScrollBar && verticalScrollBar.visible) { measuredWidth -= verticalScrollBar.minWidth; measuredMinWidth -= verticalScrollBar.minWidth; } if (horizontalScrollPolicy == ScrollPolicy.AUTO && horizontalScrollBar && horizontalScrollBar.visible) { measuredHeight -= horizontalScrollBar.minHeight; measuredMinHeight -= horizontalScrollBar.minHeight; } } /** * @private * Sizes and positions the column headers, columns, and items based on the * size of the AdvancedDataGrid. */ override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { // Note: We can't immediately call super.updateDisplayList() // because the visibleColumns array must be populated first. var updateContent:Boolean = false; if (displayWidth != unscaledWidth - viewMetrics.right - viewMetrics.left) { displayWidth = unscaledWidth - viewMetrics.right - viewMetrics.left; columnsInvalid = true; updateContent = true; } calculateColumnSizes(); if (updateContent) updateSubContent(); if (rendererChanged) purgeItemRenderers(); super.updateDisplayList(unscaledWidth, unscaledHeight); // We need to explicitly call configureScrollBars // when horizontal scrolling is optimized. In this case // because scrollAreaChanged is false, super doesn't // configure scrollbars. if (horizontalScrollPolicy != ScrollPolicy.OFF && getOptimumColumns() != visibleColumns && !itemsSizeChanged && !bSelectionChanged && !scrollAreaChanged && subContentScrolled) { configureScrollBars(); subContentScrolled = false; } if (collection && collection.length) { setRowCount(listItems.length); if (headerInfos && headerInfos.length) setColumnCount(headerInfos.length); else setColumnCount(0); } if (_horizontalScrollPolicy == ScrollPolicy.OFF) { // If we have a vScroll only and if we have room to fit the scrollbar below the header, // we want the scrollbar to be below var bm:EdgeMetrics = borderMetrics; var hh:Number = headerRowInfo.length ? headerRowInfo[0].height : headerHeight; if (verticalScrollBar != null && verticalScrollBar.visible && headerVisible && roomForScrollBar(verticalScrollBar, unscaledWidth-bm.left-bm.right, unscaledHeight-hh-bm.top-bm.bottom)) { verticalScrollBar.move(verticalScrollBar.x, viewMetrics.top + hh); verticalScrollBar.setActualSize( verticalScrollBar.width, unscaledHeight - viewMetrics.top - viewMetrics.bottom - hh); verticalScrollBar.visible = (verticalScrollBar.height >= verticalScrollBar.minHeight); } } if (bEditedItemPositionChanged) { bEditedItemPositionChanged = false; // don't do this if mouse is down on an item // on mouse up, we'll let the edit session logic // request a new position if (!lastItemDown) commitEditedItemPosition(_proposedEditedItemPosition); _proposedEditedItemPosition = undefined; itemsSizeChanged = false; } var headerBG:UIComponent = UIComponent(listContent.getChildByName("headerBG")); if (headerBGSkinChanged) { headerBGSkinChanged = false; if (headerBG) listContent.removeChild(headerBG); headerBG = null; } if (!headerBG) { headerBG = new UIComponent(); headerBG.name = "headerBG"; listContent.addChildAt(DisplayObject(headerBG), listContent.getChildIndex(selectionLayer)); var headerBGSkinClass:Class = getStyle("headerBackgroundSkin"); if (headerBGSkinClass != null) { var headerBGSkin:IFlexDisplayObject = new headerBGSkinClass(); if (headerBGSkin is ISimpleStyleClient) ISimpleStyleClient(headerBGSkin).styleName = this; headerBG.addChild(DisplayObject(headerBGSkin)); } } if (headerVisible) { headerBG.visible = true; if (useOldDGHeaderBGLogic) { drawHeaderBackground(headerBG); } else { if (headerBG.numChildren > 0) drawHeaderBackgroundSkin(IFlexDisplayObject(headerBG.getChildAt(0))); } } else { headerBG.visible = false; } drawRowBackgrounds(); if (headerVisible) drawSeparators(); else clearSeparators(); drawLinesAndColumnBackgrounds(); // trace("< 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; } // horizontal page up, page down /** * @private */ override protected function moveSelectionHorizontally(code:uint, shiftKey:Boolean, ctrlKey:Boolean):void { // The new calculated value of the horizontal scroll position var newHorizontalScrollPosition:Number; // Has the horizontal scroll position actually changed? var bUpdateHorizontalScrollPosition:Boolean = false; // Max horizontal position var maxPosition:int; if (shiftKey && code == Keyboard.PAGE_UP) { newHorizontalScrollPosition = Math.max( horizontalScrollPosition - (visibleColumns.length - lockedColumnCount) , 0); if (newHorizontalScrollPosition != horizontalScrollPosition) bUpdateHorizontalScrollPosition = true; } else if (shiftKey && code == Keyboard.PAGE_DOWN) { // We don't want to exceed the max scroll value or the last column's index maxPosition = Math.min(maxHorizontalScrollPosition, columns.length-1); newHorizontalScrollPosition = Math.min( horizontalScrollPosition + (visibleColumns.length - lockedColumnCount) , maxPosition); if (newHorizontalScrollPosition != horizontalScrollPosition) bUpdateHorizontalScrollPosition = true; } else { super.moveSelectionHorizontally(code, shiftKey, ctrlKey); } // Mark the event of the horizontal scroll position changing if (bUpdateHorizontalScrollPosition) { var scrollEvent:ScrollEvent = new ScrollEvent(ScrollEvent.SCROLL); scrollEvent.detail = ScrollEventDetail.THUMB_POSITION; scrollEvent.direction = ScrollEventDirection.HORIZONTAL; scrollEvent.delta = newHorizontalScrollPosition - horizontalScrollPosition; scrollEvent.position = newHorizontalScrollPosition; horizontalScrollPosition = newHorizontalScrollPosition; dispatchEvent(scrollEvent); if (headerIndex != -1) unselectColumnHeader(headerIndex); } } /** * @private */ override protected function makeRowsAndColumns(left:Number, top:Number, right:Number, bottom:Number, firstCol:int, firstRow:int, byCount:Boolean = false, rowsNeeded:uint = 0):Point { listContent.allowItemSizeChangeNotification = false; listSubContent.allowItemSizeChangeNotification = false; if (headerVisible && itemsSizeChanged) calculateHeaderHeight(); var pt:Point = super.makeRowsAndColumns(left, top, right, bottom, firstCol, firstRow, byCount, rowsNeeded); var optimumColumns:Array = getOptimumColumns(); if (itemEditorInstance) { itemEditorInstance.parent.setChildIndex(DisplayObject(itemEditorInstance), itemEditorInstance.parent.numChildren - 1); var col:AdvancedDataGridColumn = optimumColumns[actualColIndex]; var item:IListItemRenderer = listItems[actualRowIndex][actualColIndex]; var rowData:ListRowInfo = rowInfo[actualRowIndex]; if (item && !col.rendererIsEditor) { var dx:Number = col.editorXOffset; var dy:Number = col.editorYOffset; var dw:Number = col.editorWidthOffset; var dh:Number = col.editorHeightOffset; itemEditorInstance.move(item.x + dx, rowData.y + dy); itemEditorInstance.setActualSize(Math.min(col.width + dw, listContent.width - listContent.x - itemEditorInstance.x), Math.min(rowData.height + dh, listContent.height - listContent.y - itemEditorInstance.y)); // Commenting to show the item (with disclosure icon) behind the item editor //item.visible = false; } } var lines:Sprite = Sprite(listSubContent.getChildByName("lines")); if (lines) listSubContent.setChildIndex(lines, listSubContent.numChildren - 1); listContent.allowItemSizeChangeNotification = variableRowHeight; listSubContent.allowItemSizeChangeNotification = variableRowHeight; return pt; } /** * @private */ override protected function commitProperties():void { if(columnsInvalid) { // initializeHeaderInfo need to be called only if columns array have been changed // no need to call it everytime columnsInvalid becomes true if(columnsChanged && !headerInfoInitialized) { headerInfoInitialized = true; headerInfos = initializeHeaderInfo(columns); headerInfoInitialized = false; columnsChanged = false; } columnsChanged = false; visibleHeaderInfos = updateVisibleHeaders(); updateHeaderSearchList(); createDisplayableColumns(); } super.commitProperties(); measureItems(); } /** * @private * Instead of measuring the items, we measure the visible columns instead. */ override public function measureWidthOfItems(index:int = -1, count:int = 0):Number { var w:Number = 0; var n:int = columns ? columns.length : 0; for (var i:int = 0; i < n; i++) { if (columns[i].visible) w += columns[i].width; } return w; } /** * @private */ override public function measureHeightOfItems(index:int = -1, count:int = 0):Number { return measureHeightOfItemsUptoMaxHeight(index, count); } /** * @private */ override protected function calculateRowHeight(data:Object, hh:Number, skipVisible:Boolean = false):Number { var item:IListItemRenderer; var c:AdvancedDataGridColumn; var n:int = columns.length; var i:int; var j:int = 0; if (skipVisible && visibleColumns.length == _columns.length) return hh; var paddingTop:Number = getStyle("paddingTop"); var paddingBottom:Number = getStyle("paddingBottom"); if (!measuringObjects) measuringObjects = new Dictionary(false); for (i = 0; i < n; i++) { // skip any columns that are visible if (skipVisible && j < visibleColumns.length && visibleColumns[j].colNum == columns[i].colNum) { j++; continue; } c = columns[i]; if (!c.visible) continue; item = getMeasuringRenderer(c, false,data); setupRendererFromData(c, item, data); hh = Math.max(hh, item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop); } return hh; } /** * @private */ override protected function scrollHandler(event:Event):void { if (event.target == verticalScrollBar || event.target == horizontalScrollBar) { // TextField.scroll bubbles so you might see it here if (event is ScrollEvent) { if (!liveScrolling && ScrollEvent(event).detail == ScrollEventDetail.THUMB_TRACK) { return; } if (itemEditorInstance) endEdit(AdvancedDataGridEventReason.OTHER); var scrollBar:ScrollBar = ScrollBar(event.target); var pos:Number = scrollBar.scrollPosition; if (scrollBar == verticalScrollBar) verticalScrollPosition = pos; else if (scrollBar == horizontalScrollBar) horizontalScrollPosition = pos; super.scrollHandler(event); } } } /** * @private */ override protected function configureScrollBars():void { var oldHorizontalScrollBar:Object = horizontalScrollBar; var oldVerticalScrollBar:Object = verticalScrollBar; var rowCount:int = listItems.length; // check whether the header items are present if (rowCount + getHeaderItemsLength() == 0) { // Get rid of any existing scrollbars. if (oldHorizontalScrollBar || oldVerticalScrollBar) setScrollBarProperties(0, 0, 0, 0); return; } var vScrollProperties:Array; var hScrollProperties:Array; // partial last rows don't count if (rowCount > 1 && rowInfo[rowCount - 1].y + rowInfo[rowCount - 1].height > listContent.height) rowCount--; // offset, when added to rowCount, is the index of the dataProvider // item for that row. IOW, row 10 in listItems is showing dataProvider // item 10 + verticalScrollPosition - lockedRowCount; var offset:int = verticalScrollPosition - lockedRowCount; // don't count filler rows at the bottom either. var fillerRows:int = 0; while (rowCount && listItems[rowCount - 1].length == 0) { // as long as we're past the end of the collection, add up // fillerRows if (collection && rowCount + offset >= collection.length) { rowCount--; ++fillerRows; } else { break; } } // we have to scroll up. We can't have filler rows unless the scrollPosition is 0 if (verticalScrollPosition > 0 && fillerRows > 0) { if (adjustVerticalScrollPositionDownward(Math.max(rowCount, 1))) return; } vScrollProperties = [collection ? collection.length - lockedRowCount : 0, Math.max(rowCount - lockedRowCount, 1)]; var colCount:int = visibleColumns.length; var lastHeaderInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(visibleColumns[visibleColumns.length - 1]); var headerPosX:int = lastHeaderInfo.headerItem.x; if(visibleColumns.length - 1 > lockedColumnCount) headerPosX = getAdjustedXPos(headerPosX); // if the last column is visible and partially offscreen (but it isn't the only // column) then adjust the column count so we can scroll to see it if (colCount > 1 && visibleColumns[colCount - 1] == displayableColumns[displayableColumns.length - 1] && headerPosX + visibleColumns[colCount - 1].width > displayWidth) { colCount--; } hScrollProperties = [displayableColumns.length - lockedColumnCount, Math.max(colCount - lockedColumnCount, 1)]; //Finally set both the scroll bar properties setScrollBarProperties(hScrollProperties[0], hScrollProperties[1], vScrollProperties[0], vScrollProperties[1]); if ((!verticalScrollBar || !verticalScrollBar.visible) && collection && collection.length - lockedRowCount > rowCount - lockedRowCount) maxVerticalScrollPosition = collection.length - lockedRowCount - (rowCount - lockedRowCount); if ((!horizontalScrollBar || !horizontalScrollBar.visible) && displayableColumns.length - lockedColumnCount > colCount - lockedColumnCount) maxHorizontalScrollPosition = displayableColumns.length - lockedColumnCount - (colCount - lockedColumnCount); } /** * @private */ override protected function scrollVertically(pos:int, deltaPos:int, scrollUp:Boolean):void { // temporarily shift the cursor index to first movable row. iterator.seek(CursorBookmark.CURRENT, lockedRowCount); super.scrollVertically(pos, deltaPos, scrollUp); // move the cursor back to actual first row. iterator.seek(CursorBookmark.CURRENT, - lockedRowCount); } /** * @private */ override public function calculateDropIndex(event:DragEvent = null):int { if (event) { var item:IListItemRenderer; var pt:Point = new Point(event.localX, event.localY); pt = DisplayObject(event.target).localToGlobal(pt); pt = listContent.globalToLocal(pt); var n:int = listItems.length; for (var i:int = 0; i < n; i++) { if (rowInfo[i].y <= pt.y && pt.y <= rowInfo[i].y + rowInfo[i].height) { item = listItems[i][0]; break; } } if (item) lastDropIndex = itemRendererToIndex(item); else lastDropIndex = collection ? collection.length : 0; } return lastDropIndex; } /** * @private */ override protected function calculateDropIndicatorY(rowCount:Number, rowNum:int):Number { var i:int; // we need to take care of headerHeight var yy:Number = headerVisible ? headerHeight : 0; if (rowCount && listItems[rowNum].length && listItems[rowNum][0]) { return listItems[rowNum][0].y - 1 } for (i = 0; i < rowCount; i++) { if (listItems[i].length) yy += rowInfo[i].height; else break; } return yy; } /** * @private */ override protected function drawRowBackgrounds():void { var rowBGs:Sprite = Sprite(listContent.getChildByName("rowBGs")); if (!rowBGs) { rowBGs = new FlexSprite(); rowBGs.mouseEnabled = false; rowBGs.name = "rowBGs"; listContent.addChildAt(rowBGs, 0); } var colors:Array; var colorsStyle:Object = getStyle("alternatingItemColors"); if (colorsStyle) colors = (colorsStyle is Array) ? (colorsStyle as Array) : [colorsStyle]; if (!colors || colors.length == 0) return; styleManager.getColorNames(colors); var curRow:int = 0; var i:int = 0; var actualRow:int = verticalScrollPosition; var actualLockedRow:int = 0; var n:int = listItems.length; // for Locked rows while (curRow < lockedRowCount && curRow < n) { drawRowBackground(rowBGs, i++, rowInfo[curRow].y, rowInfo[curRow].height, colors[actualLockedRow % colors.length], actualLockedRow); curRow++; actualLockedRow++; actualRow++; } // for unlocked rows while (curRow < n) { 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 mouseEventToItemRenderer(event:MouseEvent):IListItemRenderer { 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 // headerItems are created even if showHeader is false // dont look for header renderers if headerVisible is false if (headerVisible) r = findHeaderRenderer(pt); // For listItems // if ADG is empty then length of rowInfo is 0 if (!r && rowInfo.length !=0) r = findRenderer(pt,listItems,rowInfo,rowInfo[0].y); } if (!r) r = super.mouseEventToItemRenderer(event); return r == itemEditorInstance ? null : r; } /** * @private */ override public function styleChanged(styleProp:String):void { super.styleChanged(styleProp); var changed:Boolean = false; if (styleProp == "headerBackgroundSkin") { changed = true; headerBGSkinChanged = true; } else if (styleProp == "headerSortSeparatorSkin") { changed = true; } else if (styleProp == "headerSeparatorSkin") { headerSepSkinChanged = true; changed = true; } if (changed) { itemsSizeChanged = true; } } /** * @private * handle header selection */ override protected function selectItem(item:IListItemRenderer, shiftKey:Boolean, ctrlKey:Boolean, transition:Boolean = true):Boolean { var val:Boolean = super.selectItem(item, shiftKey, ctrlKey, transition); // if item.data is AdvancedDataGridColumn, it means that a header is selected // selectedItem should be null if (item.data is AdvancedDataGridColumn) _selectedItem = null; return val; } /** * @private * handle header selection */ override mx_internal function addSelectionData(uid:String, selectionData:ListBaseSelectionData):void { // if data is AdvancedDataGridColumn, it means that a header is selected // it should not be added into the list if (selectionData.data is AdvancedDataGridColumn) return ; super.addSelectionData(uid, selectionData); } /** * @private * used by ListBase.findString. Shouldn't be used elsewhere * because column's itemToLabel is preferred */ override public function itemToLabel(data:Object):String { return displayableColumns[sortIndex == -1 ? 0 : sortIndex].itemToLabel(data); } //-------------------------------------------------------------------------- // // Methods // //-------------------------------------------------------------------------- /** * @private * When column width changes or horizontal scrolling happens * we need to adjust the sub content area. */ private function updateSubContent():void { if(!visibleColumns || getOptimumColumns() == visibleColumns) { listSubContent.scrollRect = null; listSubContent.x = 0; return ; } var lockedWidth:Number = 0; for(var i:int = 0; i < lockedColumnCount; ++i) { lockedWidth += displayableColumns[i].width; } var scrollWidth:Number = 0; if(visibleColumns.length > lockedColumnCount) { for(i = lockedColumnCount; i < lockedColumnCount+horizontalScrollPosition; ++i) { scrollWidth += displayableColumns[i].width; } } if (horizontalScrollPosition == 0) { //tmpMask.x = 0; listSubContent.scrollRect = null; listSubContent.x = 0; } else { if (lockedColumnCount > 0) listSubContent.x = lockedWidth; else listSubContent.x = 0; //tmpMask.x = lockedWidth; if (lockedWidth > 0) listSubContent.scrollRect = new Rectangle(lockedWidth+scrollWidth, 0, listContent.width - lockedWidth, listContent.height); else listSubContent.scrollRect = new Rectangle(scrollWidth, 0, listContent.width, listContent.height); } } /** * @private */ protected function updateVisibleHeaders():Array { var visibleHeaderInfos:Array = []; var n:int = headerInfos ? headerInfos.length : 0; var i:int; var k:int= 0;; for ( i = 0; i < n; i++) { headerInfos[i].visible = headerInfos[i].column.visible; if(headerInfos[i].visible) { visibleHeaderInfos.push(headerInfos[i]); headerInfos[i].actualColNum = k++; headerInfos[i].columnSpan = 1; } else { headerInfos[i].actualColNum = NaN; } } return visibleHeaderInfos; } /** * @private */ protected function updateHeaderSearchList():void { var n:int = visibleHeaderInfos? visibleHeaderInfos.length : 0; orderedHeadersList = []; for (var i:int = 0; i < n; i++) { orderedHeadersList.push(visibleHeaderInfos[i]); } } /** * @private */ protected function initializeHeaderInfo(a:Array):Array { var newArray:Array = []; var n:int = columns.length; for(var i:int = 0; i < n; i++) { var headerInfo:AdvancedDataGridHeaderInfo = new AdvancedDataGridHeaderInfo(columns[i],null,i, 0) ; newArray.push(headerInfo); } return newArray; } /** * Get the length of the header items * * @private */ protected function getHeaderItemsLength():int { return headerItems.length; } /** * @private */ mx_internal function getMeasuringRenderer(c:AdvancedDataGridColumn, forHeader:Boolean, data:Object):IListItemRenderer { var factory:IFactory = columnItemRendererFactory(c,forHeader,data); if (!measuringObjects) measuringObjects = new Dictionary(false); var item:IListItemRenderer = measuringObjects[factory]; if (!item) { item = columnItemRenderer(c, forHeader, data); item.visible = false; item.styleName = c; listContent.addChild(DisplayObject(item)); measuringObjects[factory] = item; } return item; } mx_internal function setupRendererFromData(c:AdvancedDataGridColumn, item:IListItemRenderer, data:Object):void { var rowData:AdvancedDataGridListData = AdvancedDataGridListData(makeListData(data, itemToUID(data), 0, c.colNum, c)); if (item is IDropInListItemRenderer) { if (data != null) IDropInListItemRenderer(item).listData = makeListData(data, itemToUID(data), 0 /* rowNum */, c.colNum, c); else IDropInListItemRenderer(item).listData = null; } item.data = data; if (item is IInvalidating) IInvalidating(item).invalidateSize(); item.explicitWidth = getWidthOfItem(item, c, currentColNum); UIComponentGlobals.layoutManager.validateClient(item, true); } /** * @private */ mx_internal function measureHeightOfItemsUptoMaxHeight(index:int = -1, count:int = 0, maxHeight:Number = -1):Number { if (!columns.length) return rowHeight * count; var h:Number = 0; var item:IListItemRenderer; var c:AdvancedDataGridColumn; var ch:Number = 0; var n:int; var j:int; var paddingTop:Number = getStyle("paddingTop"); var paddingBottom:Number = getStyle("paddingBottom"); if (!measuringObjects) measuringObjects = new Dictionary(false); var lockedCount:int = lockedRowCount; if (headerVisible && count > 0 && index == -1) { h = calculateHeaderHeight(); if (maxHeight != -1 && h > maxHeight) { setRowCount(0); return 0; } // trace(this + " header preferredHeight = " + h); } var bookmark:CursorBookmark = (iterator) ? iterator.bookmark : null; var bMore:Boolean = iterator != null; if (index != -1 && iterator) { try { iterator.seek(CursorBookmark.FIRST, index); } catch (e:ItemPendingError) { bMore = false; } } if (lockedCount > 0 && collectionIterator) { try { collectionIterator.seek(CursorBookmark.FIRST,0); } catch (e:ItemPendingError) { bMore = false; } } for (var i:int = 0; i < count; i++) { var data:Object; if (bMore) { data = (lockedCount > 0) ? collectionIterator.current : iterator.current; ch = 0; n = columns.length; for (j = 0; j < n; j++) { c = columns[j]; if (!c.visible) continue; item = getMeasuringRenderer(c, false,data); setupRendererFromData(c, item, data); ch = Math.max(ch, variableRowHeight ? item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop : rowHeight); } } if (maxHeight != -1 && (h + ch > maxHeight || !bMore)) { try { if (iterator) iterator.seek(bookmark, 0); } catch (e:ItemPendingError) { // we don't recover here since we'd only get here if the first seek failed. } count = i; setRowCount(count); return h; } h += ch; if (iterator) { try { bMore = iterator.moveNext(); if (lockedCount > 0) { collectionIterator.moveNext(); lockedCount--; } } catch (e:ItemPendingError) { // if we run out of data, assume all remaining rows are the size of the previous row bMore = false; } } } if (iterator) { try { iterator.seek(bookmark, 0); } catch (e:ItemPendingError) { // we don't recover here since we'd only get here if the first seek failed. } } // trace("calcheight = " + h); return h; } /** * @private */ protected function calculateHeaderHeight():Number { if (!columns.length) return rowHeight; var item:IListItemRenderer; var c:AdvancedDataGridColumn; var rowData:AdvancedDataGridListData; var ch:Number = 0; var n:int; var j:int; var paddingTop:Number = getStyle("paddingTop"); var paddingBottom:Number = getStyle("paddingBottom"); if (!measuringObjects) measuringObjects = new Dictionary(false); if (headerVisible) { ch = 0; n = columns.length; if (_headerWordWrapPresent) { _headerHeight = _originalHeaderHeight; _explicitHeaderHeight = _originalExplicitHeaderHeight; } for (j = 0; j < n; j++) { c = columns[j]; if (!c.visible) continue; // passing data as null, as it is used for header renderer item = getMeasuringRenderer(c, true, null); rowData = AdvancedDataGridListData(makeListData(c, uid, 0, c.colNum, c)); rowMap[item.name] = rowData; if (item is IDropInListItemRenderer) IDropInListItemRenderer(item).listData = rowData; item.data = c; item.explicitWidth = c.width; UIComponentGlobals.layoutManager.validateClient(item, true); ch = Math.max(ch, _explicitHeaderHeight ? headerHeight : item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop); if (columnHeaderWordWrap(c)) _headerWordWrapPresent = true; } if (_headerWordWrapPresent) { // take backups _originalHeaderHeight = _headerHeight; _originalExplicitHeaderHeight = _explicitHeaderHeight; headerHeight = ch; } } return ch; } /** * @private */ protected function getAdjustedXPos(posx:int):int { if (listSubContent.scrollRect) { if (listSubContent.x == 0) posx -= listSubContent.scrollRect.x; else posx -= (listSubContent.scrollRect.x - listSubContent.x); } return posx; } /** * @private */ mx_internal function getHeaderInfo(col:AdvancedDataGridColumn):AdvancedDataGridHeaderInfo { return headerInfos[col.colNum]; } /** * @private */ mx_internal function getHeaderInfoAt(colIndex:int):AdvancedDataGridHeaderInfo { if(headerInfos) return headerInfos[colIndex]; return null; } /** * @private */ protected function getNumColumns():int { if(headerItems && headerItems[0]) return headerItems[0].length; return -1; } /** * @private * Makes verticalScrollPosition smaller until it is 0 or there * are no empty rows. This is needed if we're scrolled to the * bottom and something is deleted or the rows resize so more * rows can be shown. */ private function adjustVerticalScrollPositionDownward(rowCount:int):Boolean { var bookmark:CursorBookmark = iterator.bookmark; // add up how much space we're currently taking with valid items var h:Number = 0; var item:IListItemRenderer; var c:AdvancedDataGridColumn; var ch:Number = 0; var n:int; var j:int; var paddingTop:Number = getStyle("paddingTop"); var paddingBottom:Number = getStyle("paddingBottom"); h = rowInfo[rowCount - 1].y + rowInfo[rowCount - 1].height; h = listContent.height - h; // back up one var numRows:int = 0; try { if (iterator.afterLast) iterator.seek(CursorBookmark.LAST, 0) else var bMore:Boolean = iterator.movePrevious(); } catch (e:ItemPendingError) { bMore = false; } if (!bMore) { // reset to 0; super.verticalScrollPosition = 0; try { iterator.seek(CursorBookmark.FIRST, 0); // sometimes, if the iterator is invalid we'll get lucky and succeed // here, then we have to make the iterator valid again if (!iteratorValid) { iteratorValid = true; lastSeekPending = null; } } catch (e:ItemPendingError) { lastSeekPending = new ListBaseSeekPending(CursorBookmark.FIRST, 0); e.addResponder(new ItemResponder(seekPendingResultHandler, seekPendingFailureHandler, lastSeekPending)); iteratorValid = false; invalidateList(); return true; } updateList(); return true; } // now work backwards to see how many more rows we need to create while (h > 0 && bMore) { var data:Object; if (bMore) { data = iterator.current; ch = 0; n = columns.length; for (j = 0; j < n; j++) { c = columns[j]; if (!c.visible) continue; if (variableRowHeight) { item = getMeasuringRenderer(c, false,data); setupRendererFromData(c, item, data); } ch = Math.max(ch, variableRowHeight ? item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop : rowHeight); } } h -= ch; try { bMore = iterator.movePrevious(); numRows++; } catch (e:ItemPendingError) { // if we run out of data, assume all remaining rows are the size of the previous row bMore = false; } } // if we overrun, go back one. if (h < 0) { numRows--; } iterator.seek(bookmark, 0); verticalScrollPosition = Math.max(0, verticalScrollPosition - numRows); // make sure we get through configureScrollBars w/o coming in here. if (numRows > 0 && !variableRowHeight) configureScrollBars(); return (numRows > 0); } /** * @private * Move a column to a new position in the columns array, shifting all * other columns left or right and updating the sortIndex and * lastSortIndex variables accordingly. */ mx_internal function shiftColumns(oldIndex:int, newIndex:int, trigger:Event = null):void { var groupInfos:Array = headerInfos; //getPossibleDropPositions(movingColumn); if (newIndex >= 0 && oldIndex != newIndex) { var incr:int = oldIndex < newIndex ? 1 : -1; for (var i:int = oldIndex; i != newIndex; i += incr) { var j:int = i + incr; var c:AdvancedDataGridColumn = _columns[i]; _columns[i] = _columns[j]; _columns[j] = c; _columns[i].colNum = i; _columns[j].colNum = j; var cInfo:AdvancedDataGridHeaderInfo = groupInfos[i]; groupInfos[i] = groupInfos[j]; groupInfos[j] = cInfo; groupInfos[i].index -=incr; groupInfos[j].index += incr; } if (sortIndex == oldIndex) sortIndex += newIndex - oldIndex; else if ((oldIndex < sortIndex && sortIndex <= newIndex) || (newIndex <= sortIndex && sortIndex < oldIndex)) sortIndex -= incr; if (lastSortIndex == oldIndex) lastSortIndex += newIndex - oldIndex; else if ((oldIndex < lastSortIndex && lastSortIndex <= newIndex) || (newIndex <= lastSortIndex && lastSortIndex < oldIndex)) lastSortIndex -= incr; columnsInvalid = true; itemsSizeChanged = true; visibleHeaderInfos = updateVisibleHeaders(); updateHeaderSearchList(); createDisplayableColumns(); invalidateDisplayList(); var icEvent:IndexChangedEvent = new IndexChangedEvent(IndexChangedEvent.HEADER_SHIFT); icEvent.oldIndex = oldIndex; icEvent.newIndex = newIndex; icEvent.triggerEvent = trigger; dispatchEvent(icEvent); } } /** * @private * Searches the iterator to determine columns. */ private function generateCols():void { if (collection.length > 0) { var col:AdvancedDataGridColumn; var newCols:Array = []; var cols:Array; if (dataProvider) { try { iterator.seek(CursorBookmark.FIRST); } catch (e:ItemPendingError) { lastSeekPending = new ListBaseSeekPending(CursorBookmark.FIRST, 0); e.addResponder(new ItemResponder(generateColumnsPendingResultHandler, seekPendingFailureHandler, lastSeekPending)); iteratorValid = false; return; } var info:Object = ObjectUtil.getClassInfo(iterator.current, ["uid", "mx_internal_uid"]); if(info) cols = info.properties; } if (!cols) { // introspect the first item and use its fields var itmObj:Object = iterator.current; for (var p:String in itmObj) { if (p != "uid") { col = new AdvancedDataGridColumn(); col.dataField = p; newCols.push(col); } } } else { // this is an old recordset - use its columns var n:int = cols.length; var colName:Object; for (var i:int = 0; i < n; i++) { colName = cols[i]; if (colName is QName) colName = QName(colName).localName; col = new AdvancedDataGridColumn(); col.dataField = String(colName); newCols.push(col); } } columns = newCols; generatedColumns = true; } } /** * @private */ private function generateColumnsPendingResultHandler(data:Object, info:ListBaseSeekPending):void { // generate cols if we haven't successfully generated them if (columns.length == 0) generateCols(); seekPendingResultHandler(data, info); } /** * @private */ protected function createDisplayableColumns():void { var i:int; var n:int; displayableColumns = null; n = _columns.length; for (i = 0; i < n; i++) { if (displayableColumns && _columns[i].visible) { displayableColumns.push(_columns[i]); } else if (!displayableColumns && !_columns[i].visible) { displayableColumns = new Array(i); for (var j:int = 0; j < i; j++) { displayableColumns[j] = _columns[j]; } } } // If there are no hidden columns, displayableColumns points to // _columns (we don't need a duplicate copy of _columns). if (!displayableColumns) displayableColumns = _columns; } /** * @private */ private function calculateColumnSizes():void { var delta:Number; var n:int; var i:int; var totalWidth:Number = 0; var col:AdvancedDataGridColumn; var cw:Number; if (columns.length == 0) { visibleColumns = []; return; } // no columns are visible so figure out which ones // to make visible if (columnsInvalid) { columnsInvalid = false; visibleColumns = []; if (minColumnWidthInvalid) { n = columns.length; for (i = 0; i < n; i++) { columns[i].minWidth = minColumnWidth; } minColumnWidthInvalid = false; } // if no hscroll, then pack columns in available space if (horizontalScrollPolicy == ScrollPolicy.OFF) { n = displayableColumns.length; for (i = 0; i < n; i++) { visibleColumns.push(displayableColumns[i]); } } else { n = displayableColumns.length; for (i = 0; i < n; i++) { if (i >= lockedColumnCount && i < lockedColumnCount + horizontalScrollPosition) { continue; } col = displayableColumns[i]; if (col.preferredWidth < col.minWidth) col.preferredWidth = col.minWidth; if (totalWidth < displayWidth) { visibleColumns.push(col); totalWidth += isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth; if (col.width != col.preferredWidth) col.setWidth(col.preferredWidth); } else { if (visibleColumns.length == 0) visibleColumns.push(displayableColumns[0]); break; } } } } var lastColumn:AdvancedDataGridColumn; var newSize:Number; // if no hscroll, then pack columns in available space if (horizontalScrollPolicy == ScrollPolicy.OFF) { var numResizable:int = 0; var fixedWidth:Number = 0; // trace("resizing columns"); // count how many resizable columns and how wide they are n = visibleColumns.length; for (i = 0; i < n; i++) { // trace("column " + i + " width = " + visibleColumns[i].width); if (visibleColumns[i].resizable) { // trace(" resizable"); if (!isNaN(visibleColumns[i].explicitWidth)) { // trace(" explicit width " + visibleColumns[i].width); fixedWidth += visibleColumns[i].width; } else { // trace(" implicitly resizable"); numResizable++; fixedWidth += visibleColumns[i].minWidth; // trace(" minWidth " + visibleColumns[i].minWidth); } } else { // trace(" not resizable"); fixedWidth += visibleColumns[i].width; } totalWidth += visibleColumns[i].width; } // trace("totalWidth = " + totalWidth); // trace("displayWidth = " + displayWidth); var ratio:Number; var newTotal:Number = displayWidth; var minWidth:Number; if (displayWidth > fixedWidth && numResizable) { // we have flexible columns and room to honor minwidths and non-resizable // trace("have enough room"); // divide and distribute the excess among the resizable n = visibleColumns.length; for (i = 0; i < n; i++) { if (visibleColumns[i].resizable && isNaN(visibleColumns[i].explicitWidth)) { lastColumn = visibleColumns[i]; if (totalWidth > displayWidth) ratio = (lastColumn.width - lastColumn.minWidth)/ (totalWidth - fixedWidth); else ratio = lastColumn.width / totalWidth; newSize = lastColumn.width - (totalWidth - displayWidth) * ratio; minWidth = visibleColumns[i].minWidth; visibleColumns[i].setWidth(newSize > minWidth ? newSize : minWidth); // trace("column " + i + " set to " + visibleColumns[i].width); } newTotal -= visibleColumns[i].width; } if (newTotal && lastColumn) { // trace("excess = " + newTotal); lastColumn.setWidth(lastColumn.width + newTotal); } } else // can't honor minwidth and non-resizables so just scale everybody { // trace("too small or too big"); n = visibleColumns.length; for (i = 0; i < n; i++) { lastColumn = visibleColumns[i]; ratio = lastColumn.width / totalWidth; //totalWidth -= visibleColumns[i].width; newSize = displayWidth * ratio; lastColumn.setWidth(newSize); lastColumn.explicitWidth = NaN; // trace("column " + i + " set to " + visibleColumns[i].width); newTotal -= newSize; } if (newTotal && lastColumn) { // trace("excess = " + newTotal); lastColumn.setWidth(lastColumn.width + newTotal); } } } else // we have or can have an horizontalScrollBar { totalWidth = 0; // drop any that completely overflow n = visibleColumns.length; for (i = 0; i < n; i++) { if (totalWidth > displayWidth) { visibleColumns.splice(i); break; } totalWidth += isNaN(visibleColumns[i].explicitWidth) ? visibleColumns[i].preferredWidth : visibleColumns[i].explicitWidth; } if (visibleColumns.length == 0) return; i = visibleColumns[visibleColumns.length - 1].colNum + 1; // add more if we have room if (totalWidth < displayWidth && i < displayableColumns.length) { n = displayableColumns.length; for (; i < n && totalWidth < displayWidth; i++) { col = displayableColumns[i]; visibleColumns.push(col); totalWidth += isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth; } } else if (totalWidth < displayWidth && horizontalScrollPosition > 0) { while (totalWidth < displayWidth && horizontalScrollPosition > 0) { col = displayableColumns[lockedColumnCount + horizontalScrollPosition - 1]; cw = isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth; if (cw < displayWidth - totalWidth) { visibleColumns.splice(lockedColumnCount, 0, col); super.horizontalScrollPosition--; totalWidth += cw; } else { break; } } } lastColumn = visibleColumns[visibleColumns.length - 1]; cw = isNaN(lastColumn.explicitWidth) ? lastColumn.preferredWidth : lastColumn.explicitWidth; newSize = cw + displayWidth - totalWidth; if (lastColumn == displayableColumns[displayableColumns.length - 1] && lastColumn.resizable && newSize >= lastColumn.minWidth && newSize > cw) { lastColumn.setWidth(newSize); maxHorizontalScrollPosition = displayableColumns.length - visibleColumns.length; } else { if (visibleColumns.length == displayableColumns.length) { // set scrollPosition to zero maxHorizontalScrollPosition = 0; super.horizontalScrollPosition = 0; } else if(lockedColumnCount < visibleColumns.length) { maxHorizontalScrollPosition = displayableColumns.length - visibleColumns.length + 1; } else { maxHorizontalScrollPosition = Math.max(0, displayableColumns.length - lockedColumnCount + 1); super.horizontalScrollPosition = Math.min(horizontalScrollPosition, maxHorizontalScrollPosition); } } } } /** * @private * If there is no horizontal scroll bar, changes the display width of other columns when * one column's width is changed. * @param col column whose width is changed * @param w width of column */ mx_internal function resizeColumn(col:int, w:Number):void { // there's a window of time before we calccolumnsizes // that someone can set width in AS if (!visibleColumns || visibleColumns.length == 0) { columns[col].setWidth(w); columns[col].preferredWidth = w; return; } if (w < columns[col].minWidth) w = columns[col].minWidth; // hScrollBar is present if (_horizontalScrollPolicy == ScrollPolicy.ON || _horizontalScrollPolicy == ScrollPolicy.AUTO) { // adjust the column's width columns[col].setWidth(w); columns[col].explicitWidth = w; columns[col].preferredWidth = w; columnsInvalid = true; } else { // find the columns in the set of visible columns; var n:int = visibleColumns.length; var i:int; for (i = 0; i < n; i++) { if (col == visibleColumns[i].colNum) break; } if (i >= visibleColumns.length) return; col = i; // we want all cols's new widths to the right of this to be in proportion // to what they were before the stretch. // get the original space to the right not taken up by the column var totalSpace:Number = 0; var lastColumn:AdvancedDataGridColumn; var newWidth:Number; //non-resizable columns don't count though var optimumColumns:Array = getOptimumColumns(); for (i = col + 1; i < n; i++) { if (optimumColumns[i].resizable) totalSpace += visibleColumns[i].width; } var newTotalSpace:Number = optimumColumns[col].width - w + totalSpace; if (totalSpace) { optimumColumns[col].setWidth(w); optimumColumns[col].explicitWidth = w; } var totX:Number = 0; // resize the columns to the right proportionally to what they were for (i = col + 1; i < n; i++) { if (optimumColumns[i].resizable) { newWidth = Math.floor(visibleColumns[i].width * newTotalSpace / totalSpace); if (newWidth < visibleColumns[i].minWidth) newWidth = visibleColumns[i].minWidth; optimumColumns[i].setWidth(newWidth); totX += optimumColumns[i].width; lastColumn = optimumColumns[i]; } } if (totX > newTotalSpace) { // if excess then should be taken out only from changing column // cause others would have already gone to their minimum newWidth = optimumColumns[col].width - totX + newTotalSpace; if (newWidth < optimumColumns[col].minWidth) newWidth = optimumColumns[col].minWidth; optimumColumns[col].setWidth(newWidth); } else if (lastColumn) { // if less then should be added in last column // dont need to check for minWidth as we are adding lastColumn.setWidth(lastColumn.width - totX + newTotalSpace); } } itemsSizeChanged = true; updateSubContent(); invalidateDisplayList(); } /** * Draws the background of the headers into the given * UIComponent. The graphics drawn can be scaled horizontally * if the component's width changes, or this method will be * called again to redraw at a different width and/or height * * @param headerBG A UIComponent that will contain the header * background graphics. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function drawHeaderBackground(headerBG:UIComponent):void { var tot:Number = displayWidth; // If we have vScroll only, extend the header over the scrollbar if (verticalScrollBar != null && _horizontalScrollPolicy == ScrollPolicy.OFF && headerVisible) { var bm:EdgeMetrics = borderMetrics; var adjustedWidth:Number = unscaledWidth - (bm.left + bm.right); tot = adjustedWidth; // Need to extend mask too. maskShape.width = adjustedWidth; } var hh:Number = headerRowInfo.length ? headerRowInfo[0].height : headerHeight; var g:Graphics = headerBG.graphics; g.clear(); var colors:Array = getStyle("headerColors"); styleManager.getColorNames(colors); var matrix:Matrix = new Matrix(); matrix.createGradientBox(tot, hh + 1, Math.PI/2, 0, 0); colors = [ colors[0], colors[0], colors[1] ]; var ratios:Array = [ 0, 60, 255 ]; var alphas:Array = [ 1.0, 1.0, 1.0 ]; g.beginGradientFill(GradientType.LINEAR, colors, alphas, ratios, matrix); g.lineStyle(0, 0x000000, 0); g.moveTo(0, 0); g.lineTo(tot, 0); g.lineTo(tot, hh - 0.5); g.lineStyle(0, getStyle("borderColor"), 100); g.lineTo(0, hh - 0.5); g.lineStyle(0, 0x000000, 0); g.endFill(); } private function drawHeaderBackgroundSkin(headerBGSkin:IFlexDisplayObject):void { var tot:Number = displayWidth; // If we have vScroll only, extend the header over the scrollbar if (verticalScrollBar != null && _horizontalScrollPolicy == ScrollPolicy.OFF && headerVisible) { var bm:EdgeMetrics = borderMetrics; var adjustedWidth:Number = unscaledWidth - (bm.left + bm.right); tot = adjustedWidth; // Need to extend mask too. maskShape.width = adjustedWidth; } var hh:Number = headerRowInfo.length ? headerRowInfo[0].height : headerHeight; headerBGSkin.setActualSize(tot,hh); } /** * Draws a row background * at the position and height specified using the * color specified. This implementation creates a Shape as a * child of the input Sprite and fills it with the appropriate color. * This method also uses the backgroundAlpha style property * setting to determine the transparency of the background color. * * @param s A Sprite that will contain a display object * that contains the graphics for that row. * * @param rowIndex The row's index in the set of displayed rows. The * header does not count, the top most visible row has a row index of 0. * This is used to keep track of the objects used for drawing * backgrounds so a particular row can re-use the same display object * even though the index of the item that row is rendering has changed. * * @param y The suggested y position for the background. * * @param height The suggested height for the indicator. * * @param color The suggested color for the indicator. * * @param dataIndex The index of the item for that row in the * data provider. This can be used to color the tenth item differently, * for example. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function drawRowBackground(s:Sprite, rowIndex:int, y:Number, height:Number, color:uint, dataIndex:int):void { var background:Shape; if (rowIndex < s.numChildren) { background = Shape(s.getChildAt(rowIndex)); } else { background = new FlexShape(); background.name = "background"; s.addChild(background); } background.y = y; // Height is usually as tall is the items in the row, but not if // it would extend below the bottom of listContent var height:Number = Math.min(height, listContent.height - y); var g:Graphics = background.graphics; g.clear(); g.beginFill(color, getStyle("backgroundAlpha")); g.drawRect(0, 0, displayWidth, height); g.endFill(); } /** * Draws a column background for a column with the suggested color. * This implementation creates a Shape as a * child of the input Sprite and fills it with the appropriate color. * * @param s A Sprite that will contain a display object * that contains the graphics for that column. * * @param columnIndex The column's index in the set of displayed columns. * The left-most visible column has a column index of 0. * This is used to keep track of the objects used for drawing * backgrounds, so a particular column can re-use the same display object * even though the index of the AdvancedDataGridColumn for that column has changed. * * @param color The suggested color for the indicator. * * @param column The column of the AdvancedDataGrid control that you are drawing the background for. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function drawColumnBackground(s:Sprite, columnIndex:int, color:uint, column:AdvancedDataGridColumn):void { var background:Shape; background = Shape(s.getChildByName(columnIndex.toString())); if (!background) { background = new FlexShape(); s.addChild(background); background.name = columnIndex.toString(); } var g:Graphics = background.graphics; g.clear(); if(columnIndex >= lockedColumnCount && columnIndex < lockedColumnCount + horizontalScrollPosition) return; g.beginFill(color); var lastRow:Object = rowInfo[listItems.length - 1]; var headerInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(getOptimumColumns()[columnIndex]); var xx:Number = headerInfo.headerItem.x; if(columnIndex >= lockedColumnCount) xx = getAdjustedXPos(xx); var yy:Number = headerRowInfo[0].y; if (headerVisible) yy += headerRowInfo[0].height; // Height is usually as tall is the items in the row, but not if // it would extend below the bottom of listContent var height:Number = Math.min(lastRow.y + lastRow.height, listContent.height - yy); g.drawRect(xx, yy, headerInfo.headerItem.width, listContent.height - yy); g.endFill(); } /** * Creates and sizes the horizontalSeparator skins. If none have been specified, then draws the lines using * drawHorizontalLine(). * * @private */ private function drawHorizontalSeparator(s:Sprite, rowIndex:int, color:uint, y:Number):void { var useLockedSeparator:Boolean = false; if (lockedRowCount > 0 && rowIndex == lockedRowCount - 1) { useLockedSeparator = true; } var hSepSkinName:String = "hSeparator" + rowIndex; var hLockedSepSkinName:String = "hLockedSeparator" + rowIndex; var createThisSkinName:String = useLockedSeparator ? hLockedSepSkinName : hSepSkinName; var createThisStyleName:String = useLockedSeparator ? "horizontalLockedSeparatorSkin" : "horizontalSeparatorSkin"; var sepSkin:IFlexDisplayObject; var lockedSepSkin:IFlexDisplayObject; var deleteThisSkin:IFlexDisplayObject; var createThisSkin:IFlexDisplayObject; // Look for separator by name sepSkin = IFlexDisplayObject(s.getChildByName(hSepSkinName)); lockedSepSkin = IFlexDisplayObject(s.getChildByName(hLockedSepSkinName)); createThisSkin = useLockedSeparator ? lockedSepSkin : sepSkin; deleteThisSkin = useLockedSeparator ? sepSkin : lockedSepSkin; if (deleteThisSkin) { s.removeChild(DisplayObject(deleteThisSkin)); //delete deleteThisSkin; } if (!createThisSkin) { var sepSkinClass:Class = Class(getStyle(createThisStyleName)); if (sepSkinClass) { createThisSkin = IFlexDisplayObject(new sepSkinClass()); createThisSkin.name = createThisSkinName; var styleableSkin:ISimpleStyleClient = createThisSkin as ISimpleStyleClient; if (styleableSkin) styleableSkin.styleName = this; s.addChild(DisplayObject(createThisSkin)); } } if (createThisSkin) { var mHeight:Number = !isNaN(createThisSkin.measuredHeight) ? createThisSkin.measuredHeight : 1; createThisSkin.setActualSize(displayWidth, mHeight); createThisSkin.move(0, y); } else // If we still don't have a sepSkin, then we have no skin style defined. Use the default function instead { drawHorizontalLine(s, rowIndex, color, y); } } /** * Draws a line between rows. 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 will contain a display object * that contains the graphics for that row. * * @param rowIndex The row's index in the set of displayed rows. The * header does not count; the top-most visible row has a row index of 0. * This is used to keep track of the objects used for drawing * backgrounds so a particular row can re-use the same display object * even though the index of the item that row is rendering has changed. * * @param color The suggested color for the indicator. * * @param y The suggested y position for the background. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function drawHorizontalLine(s:Sprite, rowIndex:int, color:uint, y:Number):void { var g:Graphics = s.graphics; if (lockedRowCount > 0 && rowIndex == lockedRowCount-1) g.lineStyle(1, 0); else g.lineStyle(1, color); g.moveTo(0, y); g.lineTo(displayWidth, y); } /** * Creates and sizes the verticalSeparator skins. If none have been specified, then draws the lines using * drawVerticalLine(). * * @private */ private function drawVerticalSeparator(s:Sprite, colIndex:int, color:uint, x:Number, y:Number):void { var useLockedSeparator:Boolean = false; if (lockedColumnCount > 0 && colIndex == lockedColumnCount-1) { useLockedSeparator = true; } var vSepSkinName:String = "vSeparator" + colIndex; var vLockedSepSkinName:String = "vLockedSeparator" + colIndex; var createThisSkinName:String = useLockedSeparator ? vLockedSepSkinName : vSepSkinName; var createThisStyleName:String = useLockedSeparator ? "verticalLockedSeparatorSkin" : "verticalSeparatorSkin"; var sepSkin:IFlexDisplayObject; var lockedSepSkin:IFlexDisplayObject; var deleteThisSkin:IFlexDisplayObject; var createThisSkin:IFlexDisplayObject; // Look for separator by name sepSkin = IFlexDisplayObject(s.getChildByName(vSepSkinName)); lockedSepSkin = IFlexDisplayObject(s.getChildByName(vLockedSepSkinName)); createThisSkin = useLockedSeparator ? lockedSepSkin : sepSkin; deleteThisSkin = useLockedSeparator ? sepSkin : lockedSepSkin; if (deleteThisSkin) { s.removeChild(DisplayObject(deleteThisSkin)); //delete deleteThisSkin; } if (!createThisSkin) { var sepSkinClass:Class = Class(getStyle(createThisStyleName)); if (sepSkinClass) { createThisSkin = IFlexDisplayObject(new sepSkinClass()); createThisSkin.name = createThisSkinName; var styleableSkin:ISimpleStyleClient = createThisSkin as ISimpleStyleClient; if (styleableSkin) styleableSkin.styleName = this; s.addChild(DisplayObject(createThisSkin)); } } if (createThisSkin) { var mWidth:Number = !isNaN(createThisSkin.measuredWidth) ? createThisSkin.measuredWidth : 1; createThisSkin.setActualSize(mWidth, listContent.height); createThisSkin.move(x, y); } else // If we still don't have a sepSkin, then we have no skin style defined. Use the default function instead { drawVerticalLine(s, colIndex, color, x); } } /** * Draws lines 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 will contain a display object * 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 suggested color for the indicator. * * @param x The suggested x position for the background. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ 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) { //In case of lockedColumn line, we start it from the top, so that it comes //in the header area as well if(lockedColumnCount > 0 && colIndex == lockedColumnCount - 1) { g.moveTo(x, 1); g.lineTo(x, headerItems[0][colIndex].height); } else tempY = headerItems[0][colIndex].height; } // draw line from tempY to listContent's height g.moveTo(x, tempY); g.lineTo(x, listContent.height); } /** * Draws lines between columns, and column backgrounds. * This implementation calls the drawHorizontalLine(), * drawVerticalLine(), * and drawColumnBackground() methods as needed. * It creates a * Sprite that contains all of these graphics and adds it as a * child of the listContent at the front of the z-order. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function drawLinesAndColumnBackgrounds():void { var lines:Sprite = getLines(); lines.graphics.clear(); var len:uint = getNumColumns(); len = (len != -1)? len : visibleColumns.length; // defend against degenerate case when width == 0 var optimumColumns:Array = getOptimumColumns(); if (len > optimumColumns.length) len = optimumColumns.length; // draw horizontal lines drawHorizontalSeparators(); // draw vertical lines drawVerticalSeparators(); // draw column backgrounds if (headerInfos && hasHeaderItemsCreated(0) && hasHeaderItemsCreated(len-1)) { var colBGs:Sprite = Sprite(listContent.getChildByName("colBGs")); // traverse the columns, set the sizes, draw the column backgrounds var lastChild:int = -1; for (var i:int = 0; i < len; i++) { var col:AdvancedDataGridColumn = optimumColumns[i]; var bgCol:Object; if (enabled) bgCol = col.getStyle("backgroundColor"); else bgCol = col.getStyle("backgroundDisabledColor"); if (bgCol !== null && !isNaN(Number(bgCol))) { if (!colBGs) { colBGs = new FlexSprite(); colBGs.mouseEnabled = false; colBGs.name = "colBGs"; listContent.addChildAt(colBGs, listContent.getChildIndex(listContent.getChildByName("rowBGs")) + 1); } drawColumnBackground(colBGs, i, Number(bgCol), col); lastChild = i; } else if (colBGs) { var background:Shape = Shape(colBGs.getChildByName(i.toString())); if (background) { var g:Graphics = background.graphics; g.clear(); colBGs.removeChild(background); } } } if (colBGs && colBGs.numChildren) { while (colBGs.numChildren) { var bg:DisplayObject = colBGs.getChildAt(colBGs.numChildren - 1); if (parseInt(bg.name) > lastChild) colBGs.removeChild(bg); else break; } } } } /** * @private * * Call drawHorizontalSeparator() to draw horizontal lines */ protected function drawHorizontalSeparators():void { // draw horizontalGridlines if needed. var lineCol:uint = getStyle("horizontalGridLineColor"); var lockedContent:Sprite = getLockedContent(); // draw vertical lines in the locked column area var lockedLinesBody:Sprite = Sprite(lockedContent.getChildByName("lockedHorizontalLines")); if (lockedLinesBody) { lockedLinesBody.graphics.clear(); while (lockedLinesBody.numChildren) { lockedLinesBody.removeChildAt(0); } } //In case of horizontal lines we don't need to care //about column locking a line from 0-displayWidth will do if (getStyle("horizontalGridLines") || lockedRowCount > 0 && lockedRowCount < listItems.length) { if (!lockedLinesBody) { lockedLinesBody = new UIComponent(); lockedLinesBody.name = "lockedHorizontalLines"; lockedContent.addChild(lockedLinesBody); } if (getStyle("horizontalGridLines")) { var n:int = listItems.length; for (var i:int = 0; i < n; i++) { drawHorizontalSeparator(lockedLinesBody, i, lineCol, rowInfo[i].y + rowInfo[i].height); } } else { drawHorizontalSeparator(lockedLinesBody, lockedRowCount - 1 , lineCol, rowInfo[lockedRowCount - 1].y + rowInfo[lockedRowCount - 1].height); } } } /** * @private * * Call drawVerticalSeparator() to draw vertical lines */ protected function drawVerticalSeparators():void { var lines:Sprite = getLines(); var lockedContent:Sprite = getLockedContent(); // draw vertical lines in the locked column area var lockedLinesBody:Sprite = Sprite(lockedContent.getChildByName("lockedVerticalLines")); if (!lockedLinesBody) { lockedLinesBody = new UIComponent(); lockedLinesBody.name = "lockedVerticalLines"; lockedContent.addChild(lockedLinesBody); } // Make sure that the lockedLinesBody are on a higher index in the childList // of lockedContent as compared to resizing separators var child:UIComponent = UIComponent(lockedContent.getChildByName("lockedHeaderLines")); if(child) { var childIndex:int = lockedContent.getChildIndex(DisplayObject(child)); if(childIndex > lockedContent.getChildIndex(DisplayObject(lockedLinesBody))) lockedContent.setChildIndex(lockedLinesBody, childIndex); } lockedLinesBody.graphics.clear(); while (lockedLinesBody.numChildren) { lockedLinesBody.removeChildAt(0); } var yVal:Number = (headerVisible && headerRowInfo && headerRowInfo[0]) ? headerRowInfo[0].height : 0; var len:uint = Math.min((visibleColumns ? visibleColumns.length : 0), Math.max(0,lockedColumnCount)); var vLines:Boolean = getStyle("verticalGridLines"); var lineCol:uint = getStyle("verticalGridLineColor"); if (vLines && len) { for (var i:int = 0; i < len; i++) { drawVerticalSeparator(lockedLinesBody, i, lineCol, getHeaderInfo(visibleColumns[i]).headerItem.x + visibleColumns[i].width, yVal); } } // draw vertical lines in the scrollable area var linesBody:Sprite = getLinesBody(lines, "verticalLines"); // clear the vertical lines and draw them again linesBody.graphics.clear(); while (linesBody.numChildren) { linesBody.removeChildAt(0); } len = visibleColumns.length; // defend against degenerate case when width == 0 if (len > visibleColumns.length) len = visibleColumns.length; vLines = getStyle("verticalGridLines"); lineCol = getStyle("verticalGridLineColor"); if (vLines && headerInfos && hasHeaderItemsCreated(0) && hasHeaderItemsCreated(len - 1)) { //Check against the negative case var lockedColCount:int = Math.max(0, lockedColumnCount); for (i = lockedColCount; i < len - 1; i++) { var headerInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(visibleColumns[i]); drawVerticalSeparator(linesBody, absoluteToVisibleColumnIndex(visibleColumns[i].colNum), lineCol, headerInfo.headerItem.x + visibleColumns[i].width, yVal); } } // is this drawing the vertical locked column indicator? if (!vLines && lockedColumnCount > 0 && lockedColumnCount < len) drawVerticalSeparator(linesBody, lockedColumnCount - 1, lineCol, getHeaderInfo(visibleColumns[lockedColumnCount-1]).headerItem.x + visibleColumns[lockedColumnCount - 1].width, 0); } /** * @private * * Get the 'lockedContent' Sprite */ 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 * * Get the 'lines' Sprite */ private function getLines():Sprite { var lines:Sprite = Sprite(listSubContent.getChildByName("lines")); if (!lines) { lines = new UIComponent(); lines.name = "lines"; lines.cacheAsBitmap = true; lines.mouseEnabled = false; listSubContent.addChild(lines); } listSubContent.setChildIndex(lines, listSubContent.numChildren - 1); return lines; } /** * @private * * Get the Sprite for horizontal/vertical lines */ private function getLinesBody(lines:Sprite, linesBodyName:String):Sprite { var linesBody:Sprite = Sprite(lines.getChildByName(linesBodyName)); if (!linesBody) { linesBody = new UIComponent(); linesBody.name = linesBodyName; lines.addChild(linesBody); } return linesBody; } /** * Removes column header separators that you normally use * to resize columns. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function clearSeparators():void { if (!separators) return; var lines:Sprite = Sprite(listSubContent.getChildByName("lines")); var headerLines:Sprite = Sprite(lines.getChildByName("header")); if (headerLines) { while (headerLines.numChildren) { headerLines.removeChildAt(headerLines.numChildren - 1); separators.pop(); } } var lockedContent:Sprite = getLockedContent(); headerLines = Sprite(lockedContent.getChildByName("lockedHeaderLines")); if (headerLines) { while (headerLines.numChildren) { headerLines.removeChildAt(headerLines.numChildren - 1); lockedSeparators.pop(); } } } /** * Creates and displays the column header separators that the user * normally uses to resize columns. This implementation uses * the same Sprite as the lines and column backgrounds, adds * instances of the headerSeparatorSkin, and attaches mouse * listeners to them in order to know when the user wants * to resize a column. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function drawSeparators():void { var lines:Sprite = getLines(); lines.graphics.clear(); var optimumColumns:Array = getOptimumColumns(); if (headerSepSkinChanged) { headerSepSkinChanged = false; clearSeparators(); } if (!separators) { separators = []; lockedSeparators = []; } lines = Sprite(listSubContent.getChildByName("lines")); var actualLocked:int = 0; var allColsLocked:Boolean = false; var numUnLockedSeparators:int = Math.max(0, optimumColumns.length - 1); var lockedContent:Sprite = getLockedContent(); var lockedHeaderLines:UIComponent = UIComponent(lockedContent.getChildByName("lockedHeaderLines")); var headerLines:UIComponent = UIComponent(lines.getChildByName("header")); if(optimumColumns && optimumColumns.length > 0) { if (lockedColumnCount > 0) { actualLocked = Math.min(lockedColumnCount, optimumColumns.length); allColsLocked = (actualLocked == optimumColumns.length); // -1 because we need one less separator, thus when all columns are // locked, we dont draw separator after last column if(allColsLocked) actualLocked--; //Number of separators left to be drawn in the scrollable area have reduced by actualLocked numUnLockedSeparators = numUnLockedSeparators - actualLocked; // Drawing a separator at lockedColumn boundary is required for resize cursor to appear // when mouse is coming from right if(!allColsLocked) numUnLockedSeparators++; //Create only if needed i.e lockedColumnCount > 0 if (!lockedHeaderLines) { lockedHeaderLines = new UIComponent(); lockedHeaderLines.name = "lockedHeaderLines"; lockedContent.addChild(lockedHeaderLines); } } if(lockedHeaderLines) createHeaderSeparators(actualLocked, lockedSeparators, lockedHeaderLines); if (!headerLines) { headerLines = new UIComponent(); headerLines.name = "header"; lines.addChild(headerLines); } // Create separators for columns which are not locked // -1 because we need one less separator createHeaderSeparators(numUnLockedSeparators, separators, headerLines); } // remove extra locked separators if(lockedHeaderLines) removeExtraSeparators(actualLocked, lockedSeparators, lockedHeaderLines); // remove extra unlocked separators if (headerLines) removeExtraSeparators(numUnLockedSeparators, separators, headerLines); } /** * Returns the header separators between column headers, * and populates the separators Array with the separators returned. * * @param i The number of separators to return. * * @param seperators Array to be populated with the header objects. * * @param headerLines The parent component of the header separators. * Flex calls the headerLines.getChild() method internally to return the separators. * * @return The header separators between column headers. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function getSeparator(i:int, seperators:Array, headerLines:UIComponent):UIComponent { var sep:UIComponent; var sepSkin:IFlexDisplayObject; if (i < headerLines.numChildren) { sep = UIComponent(headerLines.getChildAt(i)); sepSkin = IFlexDisplayObject(sep.getChildAt(0)); } else { var headerSeparatorClass:Class = getStyle("headerSeparatorSkin"); sepSkin = new headerSeparatorClass(); if (sepSkin is ISimpleStyleClient) ISimpleStyleClient(sepSkin).styleName = this; sep = new UIComponent(); sep.addChild(DisplayObject(sepSkin)); headerLines.addChild(sep); DisplayObject(sep).addEventListener( MouseEvent.MOUSE_OVER, columnResizeMouseOverHandler); DisplayObject(sep).addEventListener( MouseEvent.MOUSE_OUT, columnResizeMouseOutHandler); DisplayObject(sep).addEventListener( MouseEvent.MOUSE_DOWN, columnResizeMouseDownHandler); seperators.push(sep); } return sep; } /** * Creates the header separators between column headers, * and populates the separators Array with the separators created. * * @param n The number of separators to create. * * @param seperators Array to be populated with the header objects. * * @param headerLines The parent component of the header separators to which the separators are added. * That is, Flex calls the headerLines.addChild() method internally to add the separators to the display. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function createHeaderSeparators(n:int, seperators:Array, headerLines:UIComponent):void { var optimumColumns:Array = getOptimumColumns(); for (var i:int = 0; i < n; i++) { var headerItemIndex:int = (lockedColumnCount > 0 && seperators != lockedSeparators) ? i+lockedColumnCount - 1 : i; var sep:UIComponent = getSeparator(i, seperators, headerLines); var sepSkin:IFlexDisplayObject = IFlexDisplayObject(sep.getChildAt(0)); if (!headerItems || !headerItems[0] || !headerItems[0][headerItemIndex]) { sep.visible = false; continue; } sep.visible = true; sep.x = headerItems[0][headerItemIndex].x + optimumColumns[headerItemIndex].width - Math.round(sep.measuredWidth / 2 + 0.5); if (i > 0) { sep.x = Math.max(sep.x, seperators[i - 1].x + Math.round(sep.measuredWidth / 2 + 0.5)); } sep.y = 0; sepSkin.setActualSize(sepSkin.measuredWidth, headerRowInfo.length ? headerRowInfo[0].height : headerHeight); // Draw invisible background for separator affordance sep.graphics.clear(); sep.graphics.beginFill(0xFFFFFF, 0); sep.graphics.drawRect(-separatorAffordance, 0, sepSkin.measuredWidth + separatorAffordance , headerHeight); sep.graphics.endFill(); } } /** * @private * removes the extra separators */ private function removeExtraSeparators(n:int, seperators:Array, headerLines:UIComponent):void { while (headerLines.numChildren > n) { headerLines.removeChildAt(headerLines.numChildren - 1); seperators.pop(); } } /** * @private * Update sortIndex and sortDirection based on sort info availabled in * underlying data provider. */ private function updateSortIndexAndDirection():void { // Don't show sort indicator if sortableColumns is false or if the // column sorted on has sortable="false" if (!sortableColumns) { lastSortIndex = sortIndex; sortIndex = -1; if (lastSortIndex != sortIndex) invalidateDisplayList(); return; } if (!dataProvider) return; var view:ICollectionView = ICollectionView(dataProvider); var sort:ISort = view.sort; if (!sort) { sortIndex = lastSortIndex = -1; return; } var fields:Array = sort.fields; if (!fields) return; if (fields.length != 1) { lastSortIndex = sortIndex; sortIndex = -1; if (lastSortIndex != sortIndex) invalidateDisplayList(); return; } // fields.length == 1, so the collection is sorted on a single field. var sortField:ISortField = fields[0]; var n:int = _columns.length; for (var i:int = 0; i < n; i++) { if (_columns[i].dataField == sortField.name) { sortIndex = _columns[i].sortable ? i : -1; sortDirection = sortField.descending ? "DESC" : "ASC"; return; } } } /** * @private */ private function setEditedItemPosition(coord:Object):void { bEditedItemPositionChanged = true; _proposedEditedItemPosition = coord; invalidateDisplayList(); } /** * @private * focus an item renderer in the grid - harder than it looks */ private function commitEditedItemPosition(coord:Object):void { if (!enabled || !editable.length) return; // just give focus back to the itemEditorInstance if (itemEditorInstance && coord && itemEditorInstance is IFocusManagerComponent && _editedItemPosition.rowIndex == coord.rowIndex && _editedItemPosition.columnIndex == coord.columnIndex) { IFocusManagerComponent(itemEditorInstance).setFocus(); return; } // dispose of any existing editor, saving away its data first if (itemEditorInstance) { var reason:String; if (!coord) { reason = AdvancedDataGridEventReason.OTHER; } else { reason = (!editedItemPosition || coord.rowIndex == editedItemPosition.rowIndex) ? AdvancedDataGridEventReason.NEW_COLUMN : AdvancedDataGridEventReason.NEW_ROW; } if (!endEdit(reason) && reason != AdvancedDataGridEventReason.OTHER) return; } // store the value _editedItemPosition = coord; // allow setting of undefined to dispose item editor instance if (!coord) return; if (dontEdit) { return; } var rowIndex:int = coord.rowIndex; var colIndex:int = coord.columnIndex; if (displayableColumns.length != _columns.length) { var n:int = displayableColumns.length; for (var i:int = 0; i < n; i++) { if (displayableColumns[i].colNum >= colIndex) { colIndex = i; break; } } if (i == displayableColumns.length) colIndex = 0; } // trace("commitEditedItemPosition ", coord.rowIndex, selectedIndex); var needChangeEvent:Boolean = false; if (selectedIndex != coord.rowIndex) { commitSelectedIndex(coord.rowIndex); needChangeEvent = true; } var actualLockedRows:int = lockedRowCount; var lastRowIndex:int = verticalScrollPosition + listItems.length - 1; var partialRow:int = (rowInfo[listItems.length - 1].y + rowInfo[listItems.length - 1].height > listContent.height) ? 1 : 0; // actual row/column is the offset into listItems if (rowIndex > actualLockedRows) { // not a locked editable row make sure it is on screen if (rowIndex < verticalScrollPosition + actualLockedRows) verticalScrollPosition = rowIndex - actualLockedRows; else { // variable row heights means that we can't know how far to scroll sometimes so we loop // until we get it right while (rowIndex > lastRowIndex || // we're the last row, and we're partially visible, but we're not // the top scrollable row already (rowIndex == lastRowIndex && rowIndex > verticalScrollPosition + actualLockedRows && partialRow)) { if (verticalScrollPosition == maxVerticalScrollPosition) break; verticalScrollPosition = Math.min(verticalScrollPosition + (rowIndex > lastRowIndex ? rowIndex - lastRowIndex : partialRow), maxVerticalScrollPosition); lastRowIndex = verticalScrollPosition + listItems.length - 1; partialRow = (rowInfo[listItems.length - 1].y + rowInfo[listItems.length - 1].height > listContent.height) ? 1 : 0; } } actualRowIndex = rowIndex - verticalScrollPosition; } else { if (rowIndex == actualLockedRows) verticalScrollPosition = 0; actualRowIndex = rowIndex; } var bm:EdgeMetrics = borderMetrics; var len:uint = /*(headerItems && headerItems[0]) ? headerItems[0].length :*/ visibleColumns.length; var lastColIndex:int = horizontalScrollPosition + len - 1; // TODO with locked columns this won't give correct results? var headerInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(visibleColumns[visibleColumns.length-1]); var partialCol:int = (headerInfo.headerItem.x + headerInfo.column.width > listContent.width) ? 1 : 0; if(colIndex > lockedColumnCount) { if (colIndex < horizontalScrollPosition + lockedColumnCount) { horizontalScrollPosition = colIndex - lockedColumnCount; } else { while (colIndex > lastColIndex || (colIndex == lastColIndex && colIndex > horizontalScrollPosition + lockedColumnCount && partialCol)) { if (horizontalScrollPosition == maxHorizontalScrollPosition) break; horizontalScrollPosition = Math.min(horizontalScrollPosition + (colIndex > lastColIndex ? colIndex - lastColIndex : partialCol), maxHorizontalScrollPosition); lastColIndex = horizontalScrollPosition + visibleColumns.length - 1; headerInfo = getHeaderInfo(visibleColumns[visibleColumns.length - 1]); partialCol = (headerInfo.headerItem && headerInfo.headerItem.x + headerInfo.headerItem.width > listContent.width) ? 1 : 0; } } // Need to get the index in visibleColumns actualColIndex = absoluteToVisibleColumnIndex(displayToAbsoluteColumnIndex(colIndex)); } else { if (colIndex == lockedColumnCount) horizontalScrollPosition = 0; actualColIndex = colIndex; } // get the actual references for the column, row, and item var item:IListItemRenderer; if (listItems[actualRowIndex] && listItems[actualRowIndex][actualColIndex]) item = listItems[actualRowIndex][actualColIndex]; if (!item) { // assume that editing was cancelled commitEditedItemPosition(null); return; } if (needChangeEvent) { var evt:ListEvent = new ListEvent(ListEvent.CHANGE); evt.columnIndex = coord.columnIndex; evt.rowIndex = coord.rowIndex;; evt.itemRenderer = item; dispatchEvent(evt); } var event:AdvancedDataGridEvent = new AdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_EDIT_BEGIN, false, true); // ITEM_EDIT events are cancelable event.columnIndex = displayableColumns[colIndex].colNum; event.rowIndex = _editedItemPosition.rowIndex; event.itemRenderer = item; dispatchEvent(event); lastEditedItemPosition = _editedItemPosition; // user may be trying to change the focused item renderer if (bEditedItemPositionChanged) { bEditedItemPositionChanged = false; commitEditedItemPosition(_proposedEditedItemPosition); _proposedEditedItemPosition = undefined; } if (!itemEditorInstance) { // assume that editing was cancelled commitEditedItemPosition(null); } } /** * Creates the item editor for the item renderer at the * editedItemPosition using the editor * specified by the itemEditor property. * *

This method sets the editor instance as the * itemEditorInstance property.

* *

You may only call this method from within the event listener * for the itemEditBegin event. * To create an editor at other times, set the * editedItemPosition property to generate * the itemEditBegin event.

* * @param colIndex The column index in the data provider of the item to be edited. * * @param rowIndex The row index in the data provider of the item to be edited. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function createItemEditor(colIndex:int, rowIndex:int):void { if (displayableColumns.length != _columns.length) { var n:int = displayableColumns.length; for (var i:int = 0; i < n; i++) { if (displayableColumns[i].colNum >= colIndex) { colIndex = i; break; } } if (i == displayableColumns.length) colIndex = 0; } var col:AdvancedDataGridColumn = displayableColumns[colIndex]; if (rowIndex > lockedRowCount) rowIndex -= verticalScrollPosition; if (colIndex > lockedColumnCount) colIndex -= horizontalScrollPosition; var item:IListItemRenderer; item = listItems[actualRowIndex][actualColIndex]; var rowData:ListRowInfo = rowInfo[actualRowIndex]; // Before the editor opens up, change the label to the original data // and not the label which is (possibly) formatted data. // See AdvancedDataGridColumn.itemToLabel() regarding formatter. if (item is IDropInListItemRenderer) IDropInListItemRenderer(item).listData.label = col.itemToLabel(item.data, false); if (!col.rendererIsEditor) { var dx:Number = 0; var dy:Number = -2; var dw:Number = 0; var dh:Number = 4; // if this isn't implemented, use an input control as editor if (!itemEditorInstance) { var itemEditor:IFactory = col.itemEditor; if (itemEditor == AdvancedDataGridColumn.defaultItemEditorFactory) { // if it is the default factory, see if someone // overrode it with this style var c:Class = getStyle("defaultDataGridItemEditor"); if (c) { var fontName:String = StringUtil.trimArrayElements(col.getStyle("fontFamily"), ","); var fontWeight:String = col.getStyle("fontWeight"); var fontStyle:String = col.getStyle("fontStyle"); var bold:Boolean = (fontWeight == "bold"); var italic:Boolean = (fontStyle == "italic"); var flexModuleFactory:IFlexModuleFactory = getFontContext(fontName, bold, italic); itemEditor = col.itemEditor = new ContextualClassFactory( c, flexModuleFactory); } } dx = col.editorXOffset; dy = col.editorYOffset; dw = col.editorWidthOffset; dh = col.editorHeightOffset; itemEditorInstance = itemEditor.newInstance(); itemEditorInstance.owner = this; itemEditorInstance.styleName = col; addRendererToContentArea(itemEditorInstance, col); } itemEditorInstance.parent.setChildIndex(DisplayObject(itemEditorInstance), itemEditorInstance.parent.numChildren - 1); // give it the right size, look and placement itemEditorInstance.visible = true; var itemXPos:Number = item.x + dx; itemEditorInstance.move(itemXPos, rowData.y + dy); // Original code: /* itemEditorInstance.setActualSize(Math.min(col.width + dw, listContent.width - listContent.x - itemXPos), Math.min(rowData.height + dh, listContent.height - listContent.y - itemEditorInstance.y)); DisplayObject(itemEditorInstance).addEventListener(FocusEvent.FOCUS_OUT, itemEditorFocusOutHandler); listContent.width - listContent.x - itemXPos), Math.min(rowData.height + dh, listContent.height - listContent.y - itemEditorInstance.y)); */ // To support column spanning: itemEditorInstance.setActualSize(editedItemRenderer.width + dw, Math.min(rowData.height + dh, listContent.height - listContent.y - itemEditorInstance.y)); DisplayObject(itemEditorInstance).addEventListener(FocusEvent.FOCUS_OUT, itemEditorFocusOutHandler); // Commenting to show the item (with disclosure icon) behind the item editor //item.visible = false; layoutItemEditor(); } else { // if the item renderer is also the editor, we'll use it itemEditorInstance = item; } // listen for keyStrokes on the itemEditorInstance (which lets the grid supervise for ESC/ENTER) DisplayObject(itemEditorInstance).addEventListener(KeyboardEvent.KEY_DOWN, editorKeyDownHandler); // we disappear on any mouse down outside the editor systemManager.getSandboxRoot(). addEventListener(MouseEvent.MOUSE_DOWN, editorMouseDownHandler, true, 0, true); systemManager.getSandboxRoot(). addEventListener(SandboxMouseEvent.MOUSE_DOWN_SOMEWHERE, editorMouseDownHandler, false, 0, true); // we disappear if stage is resized systemManager.addEventListener(Event.RESIZE, editorStageResizeHandler, true, 0, true); } /** * @private * Determines the next item renderer to navigate to using the Tab key. * If the item renderer to be focused falls out of range (the end or beginning * of the grid) then move focus outside the grid. */ private function findNextItemRenderer(shiftKey:Boolean):Boolean { if (!lastEditedItemPosition) return false; if (!editable.length) { loseFocus(); return false; } // some other thing like a collection change has changed the // position, so bail and wait for commit to reset the editor. if (_proposedEditedItemPosition !== undefined) return true; _editedItemPosition = lastEditedItemPosition; var index:int = _editedItemPosition.rowIndex; var colIndex:int = _editedItemPosition.columnIndex; var found:Boolean = false; var incr:int = shiftKey ? -1 : 1; var maxIndex:int = collection.length - 1; var itemRenderer:IListItemRenderer; // cycle till we find something worth focusing, or the end of the grid while (!found) { // go to next column colIndex += incr; if (colIndex >= _columns.length || colIndex < 0) { // if we fall off the end of the columns, wrap around colIndex = (colIndex < 0) ? _columns.length - 1 : 0; // and increment/decrement the row index index += incr; if (index > maxIndex || index < 0) { if (endEdit(AdvancedDataGridEventReason.NEW_ROW)) { // if we've fallen off the rows, we need to leave the grid. get rid of the editor setEditedItemPosition(null); // set focus back to the grid so default handler will move it to the next component deferFocus(); return false; } return true; } } // We have to skip cells where the item renderer is invisible so // that we handle column spanning i.e. we should not open editors // where column spanning is applied and the column should not be // considered. // TODO here we are checking for // existence even before scrolling. var visibleCoords:Object = absoluteToVisibleIndices(index, colIndex); var visibleRowIndex:int = visibleCoords.rowIndex; var visibleColIndex:int = visibleCoords.columnIndex; if (visibleColIndex > -1) // column.visible=false { // Assumption that item renderer is never invisible in the // first column! i.e. When in last row, skip to the new last row's // first column directly without checking the item renderer's // visibility. if (visibleRowIndex == listItems.length) // last row last column -> tab visibleRowIndex -= 1; else if (visibleRowIndex == -1) // first row first column -> shift-tab visibleRowIndex = 0; itemRenderer = null; if (listItems[visibleRowIndex] && listItems[visibleRowIndex][visibleColIndex]) itemRenderer = listItems[visibleRowIndex][visibleColIndex]; if (itemRenderer && !itemRenderer.visible) // handle column-spanning continue; } var newData:Object = rowNumberToData(index); if (newData == null) return true; if (!isDataEditable(newData)) continue; // if we find a visible and editable column, move to it // if the item is visible, then only create item editor for it if (_columns[colIndex].editable && _columns[colIndex].visible) { found = true; // kill the old edit session var reason:String; reason = index == _editedItemPosition.rowIndex ? AdvancedDataGridEventReason.NEW_COLUMN : AdvancedDataGridEventReason.NEW_ROW; if (!itemEditorInstance || endEdit(reason)) { // send event to create the new one var advancedDataGridEvent:AdvancedDataGridEvent = new AdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_EDIT_BEGINNING, false, true); // ITEM_EDIT events are cancelable advancedDataGridEvent.columnIndex = colIndex; advancedDataGridEvent.dataField = _columns[colIndex].dataField; advancedDataGridEvent.rowIndex = index; dispatchEvent(advancedDataGridEvent); } } } return found; } private function loseFocus():void { // if we've fallen off the rows, we need to leave the grid. get rid of the editor setEditedItemPosition(null); // set focus back to the grid so default handler will move it to the next component losingFocus = true; setFocus(); } /** * This method closes an item editor currently open on an item renderer. * You typically call this method only from within the event listener * for the itemEditEnd event, after * you have already called the preventDefault() method to * prevent the default event listener from executing. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function destroyItemEditor():void { // trace("destroyItemEditor"); if (itemEditorInstance) { DisplayObject(itemEditorInstance).removeEventListener(KeyboardEvent.KEY_DOWN, editorKeyDownHandler); systemManager.getSandboxRoot(). removeEventListener(MouseEvent.MOUSE_DOWN, editorMouseDownHandler, true); systemManager.getSandboxRoot(). removeEventListener(SandboxMouseEvent.MOUSE_DOWN_SOMEWHERE, editorMouseDownHandler); systemManager.removeEventListener(Event.RESIZE, editorStageResizeHandler, true); var event:AdvancedDataGridEvent = new AdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_FOCUS_OUT); event.columnIndex = _editedItemPosition.columnIndex; event.rowIndex = _editedItemPosition.rowIndex; event.itemRenderer = itemEditorInstance; dispatchEvent(event); if (! _columns[_editedItemPosition.columnIndex].rendererIsEditor) { // FocusManager.removeHandler() does not find // itemEditors in focusableObjects[] array // and hence does not remove the focusRectangle if (itemEditorInstance && itemEditorInstance is UIComponent) UIComponent(itemEditorInstance).drawFocus(false); // setfocus back to us so something on stage has focus deferFocus(); // must call removeChild() so FocusManager.lastFocus becomes null itemEditorInstance.parent.removeChild(DisplayObject(itemEditorInstance)); // we are not setting the item renderer's visibility to false while creating an editor, // then why set its visibility to true // setting it visible will display the invisible item renderer in case of Custom Rows //editedItemRenderer.visible = true; } itemEditorInstance = null; _editedItemPosition = null; } } /** * @private * When the user finished editing an item, this method is called. * It dispatches the AdvancedDataGridEvent.ITEM_EDIT_END event to start the process * of copying the edited data from * the itemEditorInstance to the data provider and hiding the itemEditorInstance. * returns true if nobody called preventDefault. */ protected function endEdit(reason:String):Boolean { // this happens if the renderer is removed asynchronously ususally with FDS if (!editedItemRenderer) return true; inEndEdit = true; var advancedDataGridEvent:AdvancedDataGridEvent = new AdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_EDIT_END, false, true); // ITEM_EDIT events are cancelable advancedDataGridEvent.columnIndex = editedItemPosition.columnIndex; advancedDataGridEvent.dataField = _columns[editedItemPosition.columnIndex].dataField; advancedDataGridEvent.rowIndex = editedItemPosition.rowIndex; advancedDataGridEvent.itemRenderer = editedItemRenderer; advancedDataGridEvent.reason = reason; dispatchEvent(advancedDataGridEvent); // set a flag to not open another edit session if the item editor is still up // this means somebody wants the old edit session to stay. dontEdit = itemEditorInstance != null; // trace("dontEdit", dontEdit); if (!dontEdit && reason == AdvancedDataGridEventReason.CANCELLED) { losingFocus = true; setFocus(); } inEndEdit = false; return !(advancedDataGridEvent.isDefaultPrevented()) } /** * @private * Sets focus back to the grid so default handler will move it to the * next component. */ private function deferFocus():void { losingFocus = true; setFocus(); losingFocus = false; } /** * @private */ mx_internal function columnRendererChanged(c:AdvancedDataGridColumn):void { var item:IListItemRenderer; var factory:IFactory = columnItemRendererFactory(c,true,null); if (measuringObjects) { item = measuringObjects[factory]; if (item) { item.parent.removeChild(DisplayObject(item)); measuringObjects[factory] = null; } // TODO - set valid item to be passed factory = columnItemRendererFactory(c,false,null); item = measuringObjects[factory]; if (item) { item.parent.removeChild(DisplayObject(item)); measuringObjects[factory] = null; } } if(freeItemRenderersTable[c]) { // remove item renderers var freeRenderers:Array = freeItemRenderersTable[c][c.itemRenderer] as Array; if (freeRenderers) { while (freeRenderers.length) { item = freeRenderers.pop(); item.parent.removeChild(DisplayObject(item)); } } // remove header renderers freeRenderers = freeItemRenderersTable[c][c.headerRenderer ? c.headerRenderer : headerRenderer] as Array; if (freeRenderers) { while (freeRenderers.length) { item = freeRenderers.pop(); item.parent.removeChild(DisplayObject(item)); } } } rendererChanged = true; invalidateDisplayList(); } /** * @private */ protected function getPossibleDropPositions(val:AdvancedDataGridColumn):Array { var n:int = visibleColumns ? visibleColumns.length : 0; var dropPositions:Array = []; for ( var i:int = 0; i < n; i++) { dropPositions.push(getHeaderInfo(visibleColumns[i])); } return dropPositions; } /** * @private */ protected function hasHeaderItemsCreated(index:int=-1):Boolean { if(index == -1) return (headerItems && headerItems[0] && headerItems[0][0]); return (headerItems && headerItems[0] && headerItems[0][index]); } /** * @private */ protected function columnDraggingMouseMoveHandler(event:MouseEvent):void { if (!event.buttonDown) { columnDraggingMouseUpHandler(event); return; } var item:IListItemRenderer; var c:AdvancedDataGridColumn = movingColumn; var s:Sprite; var i:int = 0; var n:int; if (isNaN(startX)) { // If startX is not a number, dragging has just started. // Initialise and return without actually moving anything. startX = event.stageX; // Set this to null so sort doesn't happen. lastItemDown = null; // Create and position proxy. // passing data as null, as it is used for header renderer var proxy:IListItemRenderer = columnItemRenderer(c, true, null); proxy.name = "headerDragProxy"; var rowData:AdvancedDataGridListData = AdvancedDataGridListData(makeListData(c, null, 0, c.colNum, c)); if (proxy is IDropInListItemRenderer) IDropInListItemRenderer(proxy).listData = rowData; listContent.addChild(DisplayObject(proxy)); n = orderedHeadersList.length; for (i = 0; i < n; i++) { item = orderedHeadersList[i].headerItem; if (item && item.data == movingColumn) break; } var h:Number = item.height + cachedPaddingBottom + cachedPaddingTop; var w:Number = item.getExplicitOrMeasuredWidth(); var x:Number = item.x; //In case we have scrolled the "selection" shown need to be shifted if(orderedHeadersList[i].actualColNum >= lockedColumnCount) { x = getAdjustedXPos(item.x); // In case of column grouping, it may be partially visible, so need to get the visible width as well as the //x pos from which it is visible if(horizontalScrollPosition > 0 && orderedHeadersList[i].actualColNum - horizontalScrollPosition < lockedColumnCount) { var lockedWidth:Number = 0; if(lockedColumnCount > 0) { var lastLockedInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(columns[lockedColumnCount-1]); lockedWidth = lastLockedInfo.headerItem.x + columns[lockedColumnCount - 1].width; } else lockedWidth = 0; w -= (lockedWidth - x); x = lockedWidth; } } proxy.data = c; proxy.styleName = getStyle("headerDragProxyStyleName"); UIComponentGlobals.layoutManager.validateClient(proxy, true); proxy.setActualSize(w, _explicitHeaderHeight ? headerHeight : proxy.getExplicitOrMeasuredHeight()); proxy.move(x, item.y); // Create, position and draw column overlay. s = new FlexSprite(); s.name = "columnDragOverlay"; s.alpha = 0.6; listContent.addChildAt(s, listContent.getChildIndex(selectionLayer)); var vm:EdgeMetrics = viewMetrics; s.x = x; s.y = item.y - cachedPaddingTop;; if (w > 0) { var g:Graphics = s.graphics; g.beginFill(getStyle("disabledColor")); g.drawRect(0, 0, w, unscaledHeight - vm.bottom - s.y); g.endFill(); } s = Sprite(selectionLayer.getChildByName("headerSelection")); if (s) s.width = w;//movingColumn.width; if (!listContent.mask) { // Clip the contents so the header drag proxy doesn't show // outside the list. var bm:EdgeMetrics = borderMetrics; listContent.scrollRect = new Rectangle(0, 0, unscaledWidth - bm.left - bm.right, unscaledHeight - bm.top - bm.bottom); } return; } // Global coordinates. var deltaX:Number = event.stageX - startX; // If the mouse pointer over the right (layoutDirection=”ltr”) or // left (layoutDirection=”rtl”) half of the column, the drop indicator // should be shown before the next column. var deltaXInLocalCoordinates:Number = (layoutDirection == LayoutDirection.LTR ? +deltaX : -deltaX); // Move header selection. s = Sprite(selectionLayer.getChildByName("headerSelection")); if (s) s.x += deltaXInLocalCoordinates; // Move header proxy. item = IListItemRenderer(listContent.getChildByName("headerDragProxy")); if (item) item.move(item.x + deltaXInLocalCoordinates, item.y); startX += deltaX; var pt:Point = new Point(event.stageX, event.stageY); pt = listContent.globalToLocal(pt); lastPt = pt; var headerSearchArray:Array = getPossibleDropPositions(movingColumn); n = headerSearchArray.length; var headerInfo:AdvancedDataGridHeaderInfo; var columnXPos:Number = headerSearchArray[0].headerItem.x; var ww:Number = columnXPos; var notLocked:Boolean = false; dropIndexFound = false; for (var k:int = 0; k < n; ++k) { headerInfo = headerSearchArray[k]; //Is the column getting checked is locked or not? if(headerInfo.actualColNum >= lockedColumnCount) notLocked = true; ww += headerInfo.column.width; //We are not interested in columns hidden in the left because of scrolling // interested in visibleColumns only if(notLocked && headerInfo.actualColNum + headerInfo.columnSpan - horizontalScrollPosition <= lockedColumnCount) { columnXPos = ww; continue; } if(notLocked) columnXPos = getAdjustedXPos(columnXPos); if (pt.x >= columnXPos && pt.x < columnXPos + headerInfo.column.width) { dropIndexFound = true; isHeaderDragOutside = false; // If the mouse pointer over the right (ltr) or left (rtl) half // of the column, the drop indicator should be shown before the next column. if (pt.x > (columnXPos + headerInfo.column.width/2) || //Column groups which are partially visible should //show drag indicator at the right end only notLocked && headerInfo.actualColNum - horizontalScrollPosition < lockedColumnCount) { columnXPos += headerInfo.column.width; ++k; } if (dropColumnIndex != k) { dropColumnIndex = k; if (!columnDropIndicator) { var dropIndicatorClass:Class = getStyle("columnDropIndicatorSkin"); if (!dropIndicatorClass) dropIndicatorClass = DataGridColumnDropIndicator; columnDropIndicator = IFlexDisplayObject( new dropIndicatorClass()); if (columnDropIndicator is ISimpleStyleClient) ISimpleStyleClient(columnDropIndicator).styleName = this; listContent.addChild( DisplayObject(columnDropIndicator)); } listContent.setChildIndex( DisplayObject(columnDropIndicator), listContent.numChildren - 1); columnDropIndicator.x = columnXPos - 2; columnDropIndicator.y = item.y; columnDropIndicator.setActualSize(3, listContent.height - item.y); } columnDropIndicator.visible = true; break; } columnXPos = ww; } // dispatch a headerDragOutside event if we have moved out // Need not dispatch if we are already out if(!dropIndexFound && isHeaderDragOutside == false) { isHeaderDragOutside = true; var advancedDataGridEvent:AdvancedDataGridEvent = new AdvancedDataGridEvent( AdvancedDataGridEvent.HEADER_DRAG_OUTSIDE, false, true); var movingColumnInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(movingColumn); advancedDataGridEvent.column = movingColumn; advancedDataGridEvent.columnIndex = -1; advancedDataGridEvent.itemRenderer = movingColumnInfo.headerItem; advancedDataGridEvent.triggerEvent = event; dispatchEvent(advancedDataGridEvent); } } /** * @private */ protected function columnDraggingMouseUpHandler(event:Event):void { if (!movingColumn) return; var origIndex:int = movingColumn.colNum; if (dropColumnIndex >= 0) { if (dropColumnIndex >= visibleColumns.length) { dropColumnIndex = visibleColumns.length - 1; } else { if (origIndex < visibleColumns[dropColumnIndex].colNum) dropColumnIndex--; } // dropColumnIndex is actually the index into the visibleColumns // array. Get the corresponding index into the _columns array. dropColumnIndex = visibleColumns[dropColumnIndex].colNum; } // Shift columns. shiftColumns(origIndex, dropColumnIndex, event as MouseEvent); unsetColumnDragParameters(); } /** * @private */ protected function unsetColumnDragParameters():void { var sbRoot:DisplayObject = systemManager.getSandboxRoot(); sbRoot.removeEventListener(MouseEvent.MOUSE_MOVE, columnDraggingMouseMoveHandler, true); sbRoot.removeEventListener(MouseEvent.MOUSE_UP, columnDraggingMouseUpHandler, true); sbRoot.removeEventListener(SandboxMouseEvent.MOUSE_UP_SOMEWHERE, columnDraggingMouseUpHandler); systemManager.deployMouseShields(false); var proxy:IListItemRenderer = IListItemRenderer(listContent.getChildByName("headerDragProxy")); if (proxy) listContent.removeChild(DisplayObject(proxy)); var s:Sprite = Sprite(selectionLayer.getChildByName("headerSelection")); if (s) selectionLayer.removeChild(s); if (columnDropIndicator) columnDropIndicator.visible = false; s = Sprite(listContent.getChildByName("columnDragOverlay")); if (s) listContent.removeChild(s); listContent.scrollRect = null; // Add the mask which was present before column dragging addClipMask(false); startX = NaN; movingColumn = null; dropColumnIndex = -1; } /** * Checks if dragging is allowed for a particular column or not. * * @param draggedColumn The column being dragged. * * @return true if dragging is allowed for the column. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function isDraggingAllowed(draggedColumn:AdvancedDataGridColumn):Boolean { return draggedColumn.draggable; } /** * Returns a SortInfo instance containing sorting information for the column. * * @param column The column index. * * @return A SortInfo instance. * * @see mx.controls.advancedDataGridClasses.SortInfo * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function getFieldSortInfo(column:AdvancedDataGridColumn):SortInfo { if (column && collection && collection.sort) { var colUID:String; //In case there is no dataField we will use the unique column uid to identify if the column is sorted if (!column.dataField) colUID = itemToUID(column); var n:int = collection.sort.fields.length; for (var i:int = 0; i < n; i++) { if (column.dataField && collection.sort.fields[i].name == column.dataField || colUID && collection.sort.fields[i].name == colUID) { // return 1-based, not 0-based sequence number return new SortInfo(i + 1, collection.sort.fields[i].descending); } } } return null; } /** * Checks if editing is allowed for a group or summary row. * * @param data Data provider Object for the row. * * @return true if editing is allowed for the group or summary row. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function isDataEditable(data:Object):Boolean { return true; } /** * @private * * Invalidate everything (properties, size, displaylist) for an IListItemRenderer if it is * IInvalidating. */ protected function invalidateRenderer(renderer:IListItemRenderer):void { var i:IInvalidating = renderer as IInvalidating; if (i) { i.invalidateProperties(); i.invalidateSize(); i.invalidateDisplayList(); } } /** * @private * * Reset the headers. */ protected function invalidateHeaders():void { // Refresh the headers so that the separator line is removed or added var n:int = orderedHeadersList.length; for (var i:int = 0; i < n; i++) { invalidateRenderer(orderedHeadersList[i].headerItem); } } /** * @private * Given a row number, get the corresponding data in the dataProvider. */ protected function rowNumberToData(rowNumber:int):Object { var iterator:IViewCursor = collection.createCursor(); iterator.seek(CursorBookmark.FIRST, rowNumber); if (iterator.afterLast) return null; return iterator.current; } /** * @private * find the next item renderer down from the currently edited item renderer, and focus it. */ private function findNextEnterItemRenderer(event:KeyboardEvent):void { // some other thing like a collection change has changed the // position, so bail and wait for commit to reset the editor. if (_proposedEditedItemPosition !== undefined) return; _editedItemPosition = lastEditedItemPosition; var rowIndex:int = _editedItemPosition.rowIndex; var columnIndex:int = _editedItemPosition.columnIndex; var newIndex:int = rowIndex; do { // modify direction with SHIFT (up or down) newIndex += (event.shiftKey ? -1 : 1); // only move if we're within range if (newIndex < collection.length && newIndex >= 0) { rowIndex = newIndex; } else { setEditedItemPosition(null); return; } var newData:Object = rowNumberToData(newIndex); if (newData == null) { setEditedItemPosition(null); return; } if (isDataEditable(newData)) break; } while (true); // send event to create the new one var advancedDataGridEvent:AdvancedDataGridEvent = new AdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_EDIT_BEGINNING, false, true); // ITEM_EDIT events are cancelable advancedDataGridEvent.columnIndex = columnIndex; advancedDataGridEvent.dataField = _columns[columnIndex].dataField; advancedDataGridEvent.rowIndex = rowIndex; dispatchEvent(advancedDataGridEvent); } /** * Returns the column index corresponding to the field name of a sortable field. * * @param name The name of a sortable field of the data provider, as defined by * an instance of the SortField class. * * @return The column index of the sortable field. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function findSortField(name:String):int { if (collection && collection.sort) { var n:int = collection.sort.fields.length; for (var i:int = 0; i < n; i++) { if (collection.sort.fields[i]["name"] == name) return i; } } return -1; } /** * Adds a data field to the list of sort fields. * Indicate the data field by specifying its column location. * * @param columnName The name of the column that corresponds to the data field. * * @param columnNumber The column index in the AdvancedDataGrid control. * * @param collection The data collection that contains the data field. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function addSortField(columnName:String, columnNumber:int, collection:ICollectionView):void { var column:AdvancedDataGridColumn = columns[columnNumber]; if (!column.sortable) return; var headerInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(column); if(headerInfo && headerInfo.internalLabelFunction != null && column.sortCompareFunction == null) return; var desc:Boolean = column.sortDescending; var singleColumnSort:Boolean = false; if (!collection.sort || !collection.sort.fields) { singleColumnSort = true; var sort:ISort = new Sort(); sort.fields = []; collection.sort = sort; } else if (collection.sort.fields.length == 0) { singleColumnSort = true; } if (singleColumnSort) { lastSortIndex = sortIndex; sortIndex = columnNumber; sortColumn = column; var dir:String = (desc) ? "DESC" : "ASC"; sortDirection = dir; } else { lastSortIndex = -1; sortIndex = -1; sortColumn = null; sortDirection = null; } column.sortDescending = desc; var field:ISortField = new SortField(columnName); // name field.descending = desc; // field.name = column.dataField; if (column.sortCompareFunction != null) field.compareFunction = column.sortCompareFunction; collection.sort.fields.push(field); } /** * Removes a data field from the list of sort fields. * Indicate the data field by specifying its column location. * * @param columnName The name of the column that corresponds to the data field. * * @param columnNumber The column index in the AdvancedDataGrid control. * * @param collection The data collection that contains the data field. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function removeSortField(columnName:String, columnNumber:int, collection:ICollectionView):void { var column:AdvancedDataGridColumn = columns[columnNumber]; if (!collection || !collection.sort || !collection.sort.fields || !collection.sort.fields.length) return; var columnNumberToRemove:int = -1; var n:int = collection.sort.fields.length; for (var i:int = 0; i < n; i++) { if (collection.sort.fields[i].name == column.dataField) { columnNumberToRemove = i; break; } } if (columnNumberToRemove != -1) collection.sort.fields.splice(columnNumberToRemove, 1); } /** * Flip the order from ascending <-> descending for the given column name * in the sort fields list * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ private function flipSortOrder(columnName:String, columnNumber:int, collection:ICollectionView):String { if (collection.sort) { var column:AdvancedDataGridColumn = columns[columnNumber]; collection.sort.fields[findSortField(columnName)]["descending"] = ! collection.sort.fields[findSortField(columnName)]["descending"]; if (collection.sort.fields[findSortField(columnName)]["descending"]) { column.sortDescending = true; return "DESC"; } else { column.sortDescending = false; return "ASC"; } } return null; } /** * A helper method to determine which item renderer is under the mouse. * * @private */ private function findRenderer(pt:Point,items:Array,info:Array,yy:Number = 0):IListItemRenderer { var r:IListItemRenderer; var ww:Number = 0; var m:int = 0; var n:int = items.length; var optimumColumns:Array = getOptimumColumns(); for (var i:int = 0; i < n; i++) { if (items[i].length) { if (pt.y < yy + info[i].height) { m = items[i].length; if (m == 1) { r = items[i][0]; break; } ww = 0; for (var j:int = 0; j < m; j++) { ww += optimumColumns[j].width; if (pt.x < ww) { r = items[i][j]; break; } } if (r) break; } } yy += info[i].height; } return r; } /** * A helper method to determine which item renderer is under the mouse. * * @private */ private function findHeaderRenderer(pt:Point):IListItemRenderer { var r:IListItemRenderer; var yy:Number = 0; var ww:Number = 0; var m:int = 0; var n:int = headerItems.length; var optimumColumns:Array = getOptimumColumns(); for (var i:int = 0; i < n; i++) { if (headerItems[i].length) { if (pt.y < yy + headerRowInfo[i].height) { m = headerItems[i].length; if (m == 1) { r = headerItems[i][0]; break; } ww = 0; for (var j:int = 0; j < lockedColumnCount; j++) { ww += optimumColumns[j].width; if (pt.x < ww) { r = headerItems[i][j]; break; } } if (r) break; for (j=lockedColumnCount + horizontalScrollPosition; j < m; j++) { ww += optimumColumns[j].width; if (pt.x < ww) { r = headerItems[i][j]; break; } } } } yy += headerRowInfo[i].height; } return r; } /** * @private */ mx_internal function getSeparators():Array { return separators; } /** * @private */ mx_internal function getLockedSeparators():Array { return lockedSeparators; } /** * @private */ private function measureItems():void { if (itemsNeedMeasurement) { itemsNeedMeasurement = false; // fetch the itemRenderer so that it gets initialized var obj:Object = itemRenderer; if (isNaN(explicitRowHeight)) { if (iterator && columns.length > 0) { if (!measuringObjects) measuringObjects = new Dictionary(false); //set AdvancedDataGridBase.visibleColumns to the set of //all columns visibleColumns = columns; columnsInvalid = true; var paddingTop:Number = getStyle("paddingTop"); var paddingBottom:Number = getStyle("paddingBottom"); var data:Object = iterator.current; var item:IListItemRenderer; var c:AdvancedDataGridColumn; var ch:Number = 0; var n:int = columns.length; for (var i:int = 0; i < n; i++) { c = columns[i]; if (!c.visible) continue; item = getMeasuringRenderer(c, false,data); setupRendererFromData(c, item, data); ch = Math.max(ch, item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop); } // unless specified otherwise, rowheight defaults to 20 setRowHeight(Math.max(ch, 20)); } else setRowHeight(20); } } } /** * @private * Set the itemEditor instance position according to the indentation of the item it is representing. */ protected function layoutItemEditor():void { } /** * Moves focus to the specified column header. * * @param columnIndex The index of the column to receive focus. * If you specify an invalid column index, the method returns without moving focus. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function moveFocusToHeader(columnIndex:int = -1):void { if (!headerVisible || headerIndex != -1) return; if (visibleColumns.length > 0) { if (columnIndex == -1) columnIndex = visibleColumns[0].colNum; selectedHeaderInfo = getHeaderInfo(columns[columnIndex]); headerIndex = columnIndex; selectColumnHeader(headerIndex); } } /** * Selects the specified column header. * * @param columnNumber The index of the column to receive focus. * If you specify an invalid column index, the method returns without moving focus. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function selectColumnHeader(columnNumber:int):void { var visibleColumnNumber:int = -1; var n:int = visibleColumns.length; for (var i:int = 0; i < n; i++) { if (visibleColumns[i].colNum == columnNumber) { visibleColumnNumber = i; break; } } // For example, if a column header is selected but we have horizontally // scrolled such that it is not visible, then we select the first visible header item if (visibleColumnNumber == -1) { visibleColumnNumber = 0; headerIndex = visibleColumns[0].colNum; } var s:Sprite = Sprite( selectionLayer.getChildByName("headerKeyboardSelection") ); // Copied from function mouseOverHandler if (! s) { s = new FlexSprite(); s.name = "headerKeyboardSelection"; selectionLayer.addChild(s); } var r:IListItemRenderer = selectedHeaderInfo.headerItem; if (r) { var g:Graphics = s.graphics; g.clear(); g.beginFill( (isPressed || isKeyPressed) ? getStyle("selectionColor") : getStyle("rollOverColor") ); g.drawRect(0, 0, visibleColumns[visibleColumnNumber].width, r.height+cachedPaddingTop+cachedPaddingBottom - 0.5); g.endFill(); s.x = getAdjustedXPos(r.x); s.y = r.y - cachedPaddingTop; // Make sure other selection is removed caretIndex = -1; isPressed = false; selectItem(selectedHeaderInfo.headerItem, false, false); } } /** * Deselects the specified column header. * * @param columnNumber The index of the column. * If you specify an invalid column index, the method does nothing. * * @param completely If true, clear the caretIndex property * and selects the first column header in the control. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function unselectColumnHeader(columnNumber:int, completely:Boolean=false):void { var s:Sprite = Sprite( selectionLayer.getChildByName("headerKeyboardSelection") ); if (s) selectionLayer.removeChild(s); selectedHeaderInfo = null; if (completely) { caretIndex = 0; isPressed = false; selectItem(listItems[caretIndex][0], false, false); } } /** * Helper function to figure out if the item renderer is renderering a * header. * * @private */ protected function isHeaderItemRenderer(item:IListItemRenderer):Boolean { // data is set to AdvancedDataGridColumn for header items if (item != null && item.data is AdvancedDataGridColumn) return true; return false; } /** * Converts an absolute column index to the corresponding index in the * displayed columns. Because users can reorder columns, the * absolute column index may be different from the index of the * displayed column. * * @param columnIndex Absolute index of the column. * * @return The index of the column as it is currently displayed, * or -1 if columnIndex is not found. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function absoluteToDisplayColumnIndex(columnIndex:int):int { var n:int = displayableColumns.length; for (var i:int = 0; i < n; i++) { if (displayableColumns[i].colNum == columnIndex) return i; } return -1; } /** * Converts the current display column index of a column to * its corresponding absolute index. * Because users can reorder columns, the * absolute column index may be different from the index of the * displayed column. * * @param columnIndex Index of the column as it is currently displayed by the control. * * @return The absolute index of the column. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function displayToAbsoluteColumnIndex(columnIndex:int):int { return displayableColumns[columnIndex].colNum; } /** * Converts an absolute column index to the corresponding index in the * visible columns. Because users can reorder columns, the * absolute column index may be different from the index of the * visible column. * * @param columnIndex Absolute index of the column. * * @return The index of the column as it is currently visible, * or -1 if columnIndex is not currently visible. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function absoluteToVisibleColumnIndex(columnIndex:int):int { var optimumColumns:Array = getOptimumColumns(); var n:int = optimumColumns.length; for (var i:int = 0; i < n; i++) { if (optimumColumns[i].colNum == columnIndex) return i; } return -1; } /** * Converts the current visible column index of a column to * its corresponding absolute index. * Because users can reorder columns, the * absolute column index may be different from the index of the * visible column. * * @param columnIndex Index of a currently visible column in the control. * * @return The absolute index of the column. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function visibleToAbsoluteColumnIndex(columnIndex:int):int { var optimumColumns:Array = getOptimumColumns(); return optimumColumns[columnIndex].colNum; } /** * Returns true if the specified row in a column is visible. * * @param columnIndex The column index. * * @param rowIndex A row index in the column. If omitted, the method uses the * current value of the verticalScrollPosition property. * * @return true if the specified row in the column is visible. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function isColumnFullyVisible(columnIndex:int, rowIndex:int = -1):Boolean { if (rowIndex == -1) rowIndex = verticalScrollPosition; var visibleCoords:Object = absoluteToVisibleIndices(rowIndex, columnIndex); var visibleRowIndex:int = visibleCoords.rowIndex; var visibleColIndex:int = visibleCoords.columnIndex; if (visibleRowIndex < 0) return false; // First, check for presence in visibleColumns var isFullyVisible:Boolean = (visibleColIndex != -1); if (isFullyVisible) { if (listItems.length >= 1 && visibleColumns.length >= 1) { var adjustedX:Number = listItems[visibleRowIndex][visibleColIndex].x; if(getOptimumColumns() == displayableColumns && visibleColIndex > lockedColumnCount) adjustedX = getAdjustedXPos(adjustedX); // Second, check if it is fully visible // (a valid check if it is the last column) if (adjustedX + listItems[visibleRowIndex][visibleColIndex].width > listContent.width) isFullyVisible = false; } } return isFullyVisible; } /** * Figure out which visible column is available at an offset from the * current visible column. * * Use with care, because it scrolls the new column into view. * * @private */ protected function viewDisplayableColumnAtOffset(columnIndex:int, offset:int, rowIndex:int=-1, scroll:Boolean=true) :int { var displayColumnIndex:int = absoluteToDisplayColumnIndex(columnIndex); if (displayColumnIndex == -1) return -1; var n:int = displayableColumns.length; for (var newDisplayColumnIndex:int = displayColumnIndex + offset; newDisplayColumnIndex >= 0 && newDisplayColumnIndex <= n-1; newDisplayColumnIndex += offset) { if (rowIndex > -1) { // If rowIndex is given and item renderer is present, // then it must be visible var visibleCoord:Object = absoluteToVisibleIndices(rowIndex, displayToAbsoluteColumnIndex(newDisplayColumnIndex)); var listItem:IListItemRenderer; if (listItems[visibleCoord.rowIndex]) listItem = listItems[visibleCoord.rowIndex][visibleCoord.columnIndex]; if (listItem && !listItem.visible) continue; } var newAbsoluteColumnIndex:int = displayToAbsoluteColumnIndex(newDisplayColumnIndex); if (newAbsoluteColumnIndex < 0 || newAbsoluteColumnIndex > columns.length-1) return -1; if (scroll) { if (!isColumnFullyVisible(newAbsoluteColumnIndex)) scrollToViewColumn(newAbsoluteColumnIndex, columnIndex); } return newAbsoluteColumnIndex; } return -1; } /** * Changes the value of the horizontalScrollPosition property * to make the specified column visible. * This method is useful when all columns of the control are not currently visible. * * @param newColumnIndex The desired index of the column in the currently displayed columns. * * @param columnIndex The index of the column to display. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function scrollToViewColumn(newColumnIndex:int, columnIndex:int):void { var i:int, n:int; if (newColumnIndex == columnIndex) return; var newDisplayColumnIndex:int = absoluteToDisplayColumnIndex(newColumnIndex); var displayColumnIndex:int = absoluteToDisplayColumnIndex(columnIndex); var delta:int = newDisplayColumnIndex - displayColumnIndex; var newHorizontalScrollPosition:int = Math.max(0,horizontalScrollPosition + delta); // If moving from locked column area to unlocked column area, then // change horizontal scroll position to zero so that we can bring the // first unlocked column to view. if (lockedColumnCount > 0 && columnIndex == lockedColumnCount-1) newHorizontalScrollPosition = 0; var scrollEvent:ScrollEvent = new ScrollEvent(ScrollEvent.SCROLL); scrollEvent.detail = ScrollEventDetail.THUMB_POSITION; scrollEvent.direction = ScrollEventDirection.HORIZONTAL; scrollEvent.delta = delta; scrollEvent.position = newHorizontalScrollPosition; dispatchEvent(scrollEvent); horizontalScrollPosition = newHorizontalScrollPosition; } /** * Convert an absolute row index and column index into the corresponding * row index and column index of the item as it is currently displayed by the control. * * @param rowIndex An absolute row index. * * @param columnIndex An absolute column index. * * @return An Object containing two fields, rowIndex and columnIndex, * that contain the row index and column index of the item as it is currently displayed by the control. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function absoluteToVisibleIndices(rowIndex:int, columnIndex:int):Object { var visibleRowIndex:int = -1; var visibleColIndex:int = -1; // Check row display if ( (rowIndex < lockedRowCount || rowIndex >= verticalScrollPosition) && rowIndex <= verticalScrollPosition + (listItems.length ? listItems.length - 1 : 0)) { if (rowIndex >= lockedRowCount && rowIndex >= verticalScrollPosition) visibleRowIndex = rowIndex - verticalScrollPosition; else visibleRowIndex = rowIndex; } // Check column display (optimization: calculate only if row is valid) if (visibleRowIndex > -1) { var columnsOnScreen:Array = visibleColumns; if (columnsOnScreen && columnsOnScreen.length > 0) { if (columnIndex >= columnsOnScreen[0].colNum && columnIndex <= columnsOnScreen[columnsOnScreen.length-1].colNum) { if (columnIndex >= lockedColumnCount) visibleColIndex = absoluteToVisibleColumnIndex(columnIndex); else visibleColIndex = columnIndex; } } } return { rowIndex : visibleRowIndex, columnIndex : visibleColIndex }; } /** * Returns the index of a column as it is currently displayed. * This method is useful when all columns of the control are not currently visible. * * @param colNum Absolute index of the column. * * @return The index of the column as it is currently displayed, * or -1 if colNum is not found. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function colNumToIndex(colNum:int):int { if (getOptimumColumns() == visibleColumns) return absoluteToVisibleColumnIndex(colNum); else if (getOptimumColumns() == displayableColumns) return absoluteToDisplayColumnIndex(colNum); else return -1; } /** * Returns the column number of a currently displayed column * as it is currently displayed. * This method is useful when all columns of the control are not currently visible. * * @param columnIndex The index of the column as it is currently displayed. * * @return The column number of the displayed column in the control, * or -1 if columnIndex is not found. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function indexToColNum(columnIndex:int):int { if (getOptimumColumns() == visibleColumns) return visibleToAbsoluteColumnIndex(columnIndex); else if (getOptimumColumns() == displayableColumns) return displayToAbsoluteColumnIndex(columnIndex); else return -1; } //-------------------------------------------------------------------------- // // 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 the iterator is null that indicates we havent been validated yet so we'll bail. if (iterator == null) return; if (event is CollectionEvent) { var ceEvent:CollectionEvent = CollectionEvent(event) if (ceEvent.kind == CollectionEventKind.mx_internal::EXPAND) { //we ignore expand in list/tree event.stopPropagation(); } if (ceEvent.kind == CollectionEventKind.UPDATE) { //this prevents listbase from invalidating the displaylist too early. event.stopPropagation(); //we only want to update the displaylist if an updated item was visible //but dont have a sufficient test for that yet itemsSizeChanged = true; invalidateDisplayList(); } if (ceEvent.kind == CollectionEventKind.RESET) { if (generatedColumns) generateCols(); updateSortIndexAndDirection(); } else if (ceEvent.kind == CollectionEventKind.REFRESH && !manualSort) { updateSortIndexAndDirection(); } else { // if we get a remove while editing adjust the editPosition if (ceEvent.kind == CollectionEventKind.REMOVE) { if (editedItemPosition) { if (collection.length == 0) { if (itemEditorInstance) endEdit(AdvancedDataGridEventReason.CANCELLED); setEditedItemPosition(null); // nothing left to edit } else if (ceEvent.location <= editedItemPosition.rowIndex) { var curEditedItemPosition:Object = editedItemPosition; // if the editor is up on the item going away, cancel the session if (ceEvent.location == editedItemPosition.rowIndex && itemEditorInstance) endEdit(AdvancedDataGridEventReason.CANCELLED); if (inEndEdit) _editedItemPosition = { columnIndex : editedItemPosition.columnIndex, rowIndex : Math.max(0, editedItemPosition.rowIndex - ceEvent.items.length)}; else setEditedItemPosition({ columnIndex : curEditedItemPosition.columnIndex, rowIndex : Math.max(0, curEditedItemPosition.rowIndex - ceEvent.items.length)}); } } } else if (ceEvent.kind == CollectionEventKind.REPLACE) { if (editedItemPosition) { // if the editor is up on the item going away, cancel the session if (ceEvent.location == editedItemPosition.rowIndex && itemEditorInstance) endEdit(AdvancedDataGridEventReason.CANCELLED); } } } } super.collectionChangeHandler(event); if (event is CollectionEvent) { // trace("ListBase collectionEvent"); var ce:CollectionEvent = CollectionEvent(event); if (ce.kind == CollectionEventKind.ADD) { // added first item, generate columns for it if needed if (collection.length == 1) if (generatedColumns) generateCols(); } } // if (event.eventName != "sort" && bRowsChanged) // invInitHeaders = true; } /** * @private */ override protected function mouseOverHandler(event:MouseEvent):void { if (movingColumn) return; if (!enabled || !selectable) return; var r:IListItemRenderer; var n:int; if (enabled && headerVisible && getNumColumns() //headerItems.length && !isPressed) { r = mouseEventToItemRenderer(event); n = orderedHeadersList.length; var headerItem:IListItemRenderer; var headerInfo:AdvancedDataGridHeaderInfo; var i:int; for( i = 0; i < n && r; i++) { headerItem = orderedHeadersList[i].headerItem; if(headerItem == r) { headerInfo = orderedHeadersList[i]; if(orderedHeadersList[i].column.sortable) { var s:Sprite = Sprite( selectionLayer.getChildByName("headerSelection")); if (!s) { s = new FlexSprite(); s.name = "headerSelection"; selectionLayer.addChild(s); } var h:Number = r.height + cachedPaddingBottom + cachedPaddingTop; var w:Number = r.getExplicitOrMeasuredWidth(); var x:Number = r.x; //In case we have scrolled the "selection" shown need to be shifted if(headerInfo.actualColNum >= lockedColumnCount) { x = getAdjustedXPos(r.x); // In case of column grouping, it may be partially visible, so need to get the visible width as well as the //x pos from which it is visible if(horizontalScrollPosition > 0 && headerInfo.actualColNum - horizontalScrollPosition < lockedColumnCount) { var lockedWidth:Number = 0; if(lockedColumnCount > 0) { var lastLockedInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(columns[lockedColumnCount-1]); lockedWidth = lastLockedInfo.headerItem.x + columns[lockedColumnCount - 1].width; } else lockedWidth = 0; w -= (lockedWidth - x); x = lockedWidth; } } var g:Graphics = s.graphics; g.clear(); g.beginFill(getStyle("rollOverColor")); g.drawRect(0, 0, w, h - 0.5); g.endFill(); s.x = x; s.y = r.y - cachedPaddingTop; } return; } } } if (event.buttonDown) lastItemDown = r; else lastItemDown = null; super.mouseOverHandler(event); } /** * @private */ override protected function mouseOutHandler(event:MouseEvent):void { if (movingColumn) return; var r:IListItemRenderer; var optimumColumns:Array = getOptimumColumns(); var n:int; if (enabled && headerVisible && listItems.length) { r = mouseEventToItemRenderer(event); if(!r) { n = optimumColumns.length; for (var i:int = 0; i < n; i++) { if(optimumColumns[i].colNum == sortIndex) r = getHeaderInfo(optimumColumns[i]).headerItem; } } n = orderedHeadersList.length; var headerItem:IListItemRenderer; for( i = 0; i < n && r; i++) { headerItem = orderedHeadersList[i].headerItem; if(headerItem == r) { if(orderedHeadersList[i].column.sortable) { var s:Sprite = Sprite( selectionLayer.getChildByName("headerSelection")); if (s) selectionLayer.removeChild(s); } return; } } } if (event.buttonDown) lastItemDown = r; else lastItemDown = null; super.mouseOutHandler(event); } /** * @private */ override protected function mouseDownHandler(event:MouseEvent):void { // trace(">>mouseDownHandler"); var r:IListItemRenderer; var s:Sprite; r = mouseEventToItemRenderer(event); var optimumColumns:Array = getOptimumColumns(); // if headers are visible and clickable for sorting if (enabled && (sortableColumns || draggableColumns) && headerVisible && hasHeaderItemsCreated()) { // find out if we clicked on a header var n:int = orderedHeadersList.length; var headerItem:IListItemRenderer; for( var i:int = 0; i < n && r; i++) { headerItem = orderedHeadersList[i].headerItem; // if we did click on a header if(headerItem == r) { var headerInfo:AdvancedDataGridHeaderInfo = orderedHeadersList[i]; // dispose the editor if (itemEditorInstance) endEdit(AdvancedDataGridEventReason.OTHER); var c:AdvancedDataGridColumn = orderedHeadersList[i].column; if (sortableColumns && c.sortable) { lastItemDown = r; s = Sprite(selectionLayer.getChildByName("headerSelection")); if (!s) { s = new FlexSprite(); s.name = "headerSelection"; selectionLayer.addChild(s); } var h:Number = r.height + cachedPaddingBottom + cachedPaddingTop; var w:Number = r.getExplicitOrMeasuredWidth(); var x:Number = r.x; //In case we have scrolled the "selection" shown need to be shifted if(headerInfo.actualColNum >= lockedColumnCount) { x = getAdjustedXPos(r.x); // In case of column grouping, it may be partially visible, so need to get the visible width as well as the //x pos from which it is visible if(horizontalScrollPosition > 0 && headerInfo.actualColNum - horizontalScrollPosition < lockedColumnCount) { var lockedWidth:Number = 0; if(lockedColumnCount > 0) { var lastLockedInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(columns[lockedColumnCount-1]); lockedWidth = lastLockedInfo.headerItem.x + columns[lockedColumnCount - 1].width; } else { lockedWidth = 0; } w -= (lockedWidth - x); x = lockedWidth; } } var g:Graphics = s.graphics; g.clear(); g.beginFill(getStyle("selectionColor")); g.drawRect(0, 0, w, h - 0.5); g.endFill(); s.x = x; s.y = r.y - cachedPaddingTop; } isPressed = true; // begin column dragging if (draggableColumns && isDraggingAllowed(c)) { startX = NaN; var sbRoot:DisplayObject = systemManager.getSandboxRoot(); sbRoot.addEventListener(MouseEvent.MOUSE_MOVE, columnDraggingMouseMoveHandler, true); sbRoot.addEventListener(MouseEvent.MOUSE_UP, columnDraggingMouseUpHandler, true); sbRoot.addEventListener(SandboxMouseEvent.MOUSE_UP_SOMEWHERE, columnDraggingMouseUpHandler); systemManager.deployMouseShields(true); movingColumn = c; } return; } } } lastItemDown = null; var isItemEditor:Boolean = itemRendererContains(itemEditorInstance, DisplayObject(event.target)); // If it isn't an item renderer, or an item editor do default behavior if (!isItemEditor) { var pos:Point; if (r && r.data) { lastItemDown = r; pos = itemRendererToIndices(r); var bEndedEdit:Boolean = true; if (itemEditorInstance) { //for header renderers pos would be null if (pos == null || displayableColumns[pos.x].editable == false) bEndedEdit = endEdit(AdvancedDataGridEventReason.OTHER); else bEndedEdit = endEdit(editedItemPosition.rowIndex == pos.y ? AdvancedDataGridEventReason.NEW_COLUMN : AdvancedDataGridEventReason.NEW_ROW); } // if we didn't end edit session, don't do default behavior (call super) if (!bEndedEdit) return; } else { // trace("end edit?"); if (itemEditorInstance) endEdit(AdvancedDataGridEventReason.OTHER); } // Move focus out of header if mouse pressed on any list item if (headerIndex != -1) { var pt:Point = itemRendererToIndices(r); if (pt) { unselectColumnHeader(headerIndex, true); headerIndex = -1; caretIndex = pt.y; } } super.mouseDownHandler(event); if (r) { if (pos && displayableColumns[pos.x].rendererIsEditor) resetDragScrolling(); } } else resetDragScrolling(); // trace("<= 0 && !dontEdit) { if (displayableColumns[pos.x].editable) { advancedDataGridEvent = new AdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_EDIT_BEGINNING, false, true); // ITEM_EDIT events are cancelable advancedDataGridEvent.columnIndex = displayableColumns[pos.x].colNum; advancedDataGridEvent.dataField = displayableColumns[pos.x].dataField; advancedDataGridEvent.rowIndex = pos.y; advancedDataGridEvent.itemRenderer = r; dispatchEvent(advancedDataGridEvent); } else { // if the item is not editable, set lastPosition to it anyways // so future tabbing starts from there lastEditedItemPosition = { columnIndex: displayableColumns[pos.x].colNum, rowIndex: pos.y }; } } } else if (lastItemDown && lastItemDown != itemEditorInstance) { pos = itemRendererToIndices(lastItemDown); if (pos && pos.y >= 0 && editable && !dontEdit) { if (displayableColumns[pos.x].editable) { advancedDataGridEvent = new AdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_EDIT_BEGINNING, false, true); // ITEM_EDIT events are cancelable advancedDataGridEvent.columnIndex = displayableColumns[pos.x].colNum; advancedDataGridEvent.dataField = displayableColumns[pos.x].dataField; advancedDataGridEvent.rowIndex = pos.y; advancedDataGridEvent.itemRenderer = lastItemDown; dispatchEvent(advancedDataGridEvent); } else { // if the item is not editable, set lastPosition to it any // so future tabbing starts from there lastEditedItemPosition = { columnIndex: pos.x, rowIndex: pos.y }; } } } lastItemDown = null; } /** * @private * when the grid gets focus, focus an item renderer */ override protected function focusInHandler(event:FocusEvent):void { // trace(">>DGFocusIn ", selectedIndex); if (losingFocus) { losingFocus = false; // trace("losing focus via tab"); // trace("<>DGFocusOut " + itemEditorInstance + " " + event.relatedObject, event.target); if (event.target == this) super.focusOutHandler(event); // just leave if item editor is losing focus back to grid. Usually happens // when someone clicks out of the editor onto a new item renderer. if (event.relatedObject == this && itemRendererContains(itemEditorInstance, DisplayObject(event.target))) return; // just leave if the cell renderer is losing focus to nothing while its editor exists. // this happens when we make the cell renderer invisible as we put up the editor // if the renderer can have focus. if (event.relatedObject == null && itemRendererContains(editedItemRenderer, DisplayObject(event.target))) return; // just leave if item editor is losing focus to nothing. Usually happens // when someone clicks out of the textfield if (event.relatedObject == null && itemRendererContains(itemEditorInstance, DisplayObject(event.target))) return; // however, if we're losing focus to anything other than the editor or the grid // hide the editor; if (itemEditorInstance && (!event.relatedObject || !itemRendererContains(itemEditorInstance, event.relatedObject))) { // trace("call endEdit from focus out"); endEdit(AdvancedDataGridEventReason.OTHER); removeEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler); removeEventListener(MouseEvent.MOUSE_DOWN, mouseFocusChangeHandler); } // trace("< 0 && target.parent == UIComponent(getLines().getChildByName("header"))) index += (lockedColumnCount - 1); var optimumColumns:Array = getOptimumColumns(); if (!optimumColumns[index].resizable) return; if (itemEditorInstance) endEdit(AdvancedDataGridEventReason.OTHER); startX = DisplayObject(event.target).x; lastPt = new Point(event.stageX, event.stageY); lastPt = listContent.globalToLocal(lastPt); /* var n:int = separators.length; for (var i:int = 0; i < n; i++) { if (separators[i] == event.target) { resizingColumn = optimumColumns[i]; break; } } if (!resizingColumn) return; */ resizingColumn = optimumColumns[index]; var headerItem:IListItemRenderer = getHeaderInfo(optimumColumns[index]).headerItem; if (index > lockedColumnCount) { minX = getAdjustedXPos(headerItem.x); startX = getAdjustedXPos(startX); } else { minX = headerItem.x; } minX += resizingColumn.minWidth; isPressed = true; var sbRoot:DisplayObject = systemManager.getSandboxRoot(); sbRoot.addEventListener(MouseEvent.MOUSE_MOVE, columnResizingHandler, true); sbRoot.addEventListener(MouseEvent.MOUSE_UP, columnResizeMouseUpHandler, true); sbRoot.addEventListener(SandboxMouseEvent.MOUSE_UP_SOMEWHERE, columnResizeMouseUpHandler); systemManager.deployMouseShields(true); var resizeSkinClass:Class = getStyle("columnResizeSkin"); resizeGraphic = new resizeSkinClass(); listContent.addChild(DisplayObject(resizeGraphic)); var pt:Point = new Point(event.stageX, event.stageY); pt = listContent.globalToLocal(pt); resizeGraphic.move(pt.x, target.y); resizeGraphic.setActualSize(resizeGraphic.measuredWidth, unscaledHeight-target.y); } /** * @private */ private function columnResizingHandler(event:MouseEvent):void { if (!MouseEvent(event).buttonDown) { columnResizeMouseUpHandler(event); // return from here, as the resizingColumn // set to null. return; } var vsw:int = verticalScrollBar ? verticalScrollBar.width : 0; var pt:Point = new Point(event.stageX, event.stageY); pt = listContent.globalToLocal(pt); lastPt = pt; var separatorWidth:Number = 0; if (lockedSeparators && lockedSeparators.length > 0) separatorWidth = lockedSeparators[0].width; else if (separators && separators.length > 0) separatorWidth = separators[0].width; // substract the separators width, // so that separator will be visible after column resizing // TODO - we should substract the resized column separatos's width var maxWidth:Number = unscaledWidth - separatorWidth - vsw ; var index:int; if(getOptimumColumns() == visibleColumns) index = absoluteToVisibleColumnIndex(resizingColumn.colNum); else index = absoluteToDisplayColumnIndex(resizingColumn.colNum); resizeGraphic.move(Math.min(Math.max(minX, pt.x), maxWidth), resizeGraphic.y); } /** * @private * Determines how much to resize the column. */ private function columnResizeMouseUpHandler(event:Event):void { if (!enabled || !resizableColumns) return; isPressed = false; var sbRoot:DisplayObject = systemManager.getSandboxRoot(); sbRoot.removeEventListener(MouseEvent.MOUSE_MOVE, columnResizingHandler, true); sbRoot.removeEventListener(MouseEvent.MOUSE_UP, columnResizeMouseUpHandler, true); sbRoot.removeEventListener(SandboxMouseEvent.MOUSE_UP_SOMEWHERE, columnResizeMouseUpHandler); systemManager.deployMouseShields(false); listContent.removeChild(DisplayObject(resizeGraphic)); cursorManager.removeCursor(resizeCursorID); var c:AdvancedDataGridColumn = resizingColumn; resizingColumn = null; // need to find the visible column index here. // var n:int = displayableColumns.length; // var i:int; // for (i = 0; i < n; i++) // { // if (c == displayableColumns[i]) // break; // } // if (i >= displayableColumns.length) // return; var vsw:int = verticalScrollBar ? verticalScrollBar.width : 0; var mouseEvent:MouseEvent = event as MouseEvent; var pt:Point; if (mouseEvent) { pt = new Point(mouseEvent.stageX, mouseEvent.stageY); pt = listContent.globalToLocal(pt); } else { pt = lastPt; } var separatorWidth:Number = 0; if (lockedSeparators && lockedSeparators.length > 0) separatorWidth = lockedSeparators[0].width; else if (separators && separators.length > 0) separatorWidth = separators[0].width; // substract the separators width, // so that separator will be visible after column resizing // TODO - we should substract the resized column separatos's width var maxWidth:Number = unscaledWidth - separatorWidth - vsw ; // resize the column var widthChange:Number = Math.min(Math.max(minX, pt.x), maxWidth) - startX; resizeColumn(c.colNum, Math.floor(c.width + widthChange)); // event var advancedDataGridEvent:AdvancedDataGridEvent = new AdvancedDataGridEvent(AdvancedDataGridEvent.COLUMN_STRETCH); advancedDataGridEvent.columnIndex = c.colNum; advancedDataGridEvent.dataField = c.dataField; advancedDataGridEvent.localX = pt.x; dispatchEvent(advancedDataGridEvent); } /** * @private */ private function editorMouseDownHandler(event:Event):void { if(event is MouseEvent && owns(DisplayObject(event.target))) return; endEdit(AdvancedDataGridEventReason.OTHER); } /** * @private */ protected function editorKeyDownHandler(event:KeyboardEvent):void { // ESC just kills the editor, no new data if (event.keyCode == Keyboard.ESCAPE) { endEdit(AdvancedDataGridEventReason.CANCELLED); } else if (event.ctrlKey && event.charCode == 46) { // Check for Ctrl-. endEdit(AdvancedDataGridEventReason.CANCELLED); } else if (event.charCode == Keyboard.ENTER && event.keyCode != 229) { // multiline editors can take the enter key. if (columns[_editedItemPosition.columnIndex].editorUsesEnterKey) return; // Enter edits the item, moves down a row // The 229 keyCode is for IME compatability. When entering an IME expression, // the enter key is down, but the keyCode is 229 instead of the enter key code. // Thanks to Yukari for this little trick... if (endEdit(AdvancedDataGridEventReason.NEW_ROW) && !dontEdit) findNextEnterItemRenderer(event); } } /** * @private */ private function editorStageResizeHandler(event:Event):void { if (event.target is DisplayObjectContainer && DisplayObjectContainer(event.target).contains(this)) endEdit(AdvancedDataGridEventReason.OTHER); } /** * @private * This gets called when the tab key is hit. */ private function mouseFocusChangeHandler(event:MouseEvent):void { // trace("mouseFocus handled by " + this); if (itemEditorInstance && !event.isDefaultPrevented() && itemRendererContains(itemEditorInstance, DisplayObject(event.target))) { event.preventDefault(); } } /** * @private * This gets called when the tab key is hit. */ private function keyFocusChangeHandler(event:FocusEvent):void { // trace("tabHandled by " + this); if (event.keyCode == Keyboard.TAB && ! event.isDefaultPrevented() && findNextItemRenderer(event.shiftKey)) { event.preventDefault(); } } /** * @private * Hides the itemEditorInstance. */ private function itemEditorFocusOutHandler(event:FocusEvent):void { // trace("itemEditorFocusOut " + event.relatedObject); if (event.relatedObject && contains(event.relatedObject)) return; // ignore textfields losing focus on mousedowns if (!event.relatedObject) return; // trace("endEdit from itemEditorFocusOut"); if (itemEditorInstance) endEdit(AdvancedDataGridEventReason.OTHER); } /** * @private */ private function itemEditorItemEditBeginningHandler(event:AdvancedDataGridEvent):void { // trace("itemEditorItemEditBeginningHandler"); if (!event.isDefaultPrevented()) setEditedItemPosition({columnIndex: event.columnIndex, rowIndex: event.rowIndex}); else if (!itemEditorInstance) { _editedItemPosition = null; setFocus(); } } /** * @private * focus an item renderer in the grid - harder than it looks */ private function itemEditorItemEditBeginHandler(event:AdvancedDataGridEvent):void { // weak reference for deactivation if (root) systemManager.addEventListener(Event.DEACTIVATE, deactivateHandler, false, 0, true); // if not prevented and if data is not null (might be from dataservices) if (!event.isDefaultPrevented() && listItems[actualRowIndex][actualColIndex].data != null) { createItemEditor(event.columnIndex, event.rowIndex); if (editedItemRenderer is IDropInListItemRenderer && itemEditorInstance is IDropInListItemRenderer) IDropInListItemRenderer(itemEditorInstance).listData = IDropInListItemRenderer(editedItemRenderer).listData; // if rendererIsEditor, don't apply the data as the data may have already changed in some way. // This can happen if clicking on a checkbox rendererIsEditor as the checkbox will try to change // its value as we try to stuff in an old value here. if (!columns[event.columnIndex].rendererIsEditor) itemEditorInstance.data = editedItemRenderer.data; if (itemEditorInstance is IInvalidating) IInvalidating(itemEditorInstance).validateNow(); if (itemEditorInstance is IIMESupport) IIMESupport(itemEditorInstance).imeMode = (columns[event.columnIndex].imeMode == null) ? _imeMode : columns[event.columnIndex].imeMode; var fm:IFocusManager = focusManager; // trace("setting focus to item editor"); if (itemEditorInstance is IFocusManagerComponent) fm.setFocus(IFocusManagerComponent(itemEditorInstance)); fm.defaultButtonEnabled = false; var event:AdvancedDataGridEvent = new AdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_FOCUS_IN); event.columnIndex = _editedItemPosition.columnIndex; event.rowIndex = _editedItemPosition.rowIndex; event.itemRenderer = itemEditorInstance; dispatchEvent(event); } } /** * @private */ private function itemEditorItemEditEndHandler(event:AdvancedDataGridEvent):void { if (!event.isDefaultPrevented()) { var bChanged:Boolean = false; if (event.reason == AdvancedDataGridEventReason.NEW_COLUMN) { if (!collectionUpdatesDisabled) { collection.disableAutoUpdate(); collectionUpdatesDisabled = true; } } else { if (collectionUpdatesDisabled) { collection.enableAutoUpdate(); collectionUpdatesDisabled = false; } } if (itemEditorInstance && event.reason != AdvancedDataGridEventReason.CANCELLED) { var newData:Object = itemEditorInstance[_columns[event.columnIndex].editorDataField]; var property:String = _columns[event.columnIndex].dataField; var data:Object = event.itemRenderer.data; var typeInfo:String = ""; for each(var variable:XML in describeType(data).variable) { if (property == variable.@name.toString()) { typeInfo = variable.@type.toString(); break; } } if (typeInfo == "String") { if (!(newData is String)) newData = newData.toString(); } else if (typeInfo == "uint") { if (!(newData is uint)) newData = uint(newData); } else if (typeInfo == "int") { if (!(newData is int)) newData = int(newData); } else if (typeInfo == "Number") { if (!(newData is int)) newData = Number(newData); } if (data[property] != newData) { bChanged = true; data[property] = newData; } if (bChanged && !(data is IPropertyChangeNotifier)) { collection.itemUpdated(data, property); } if (event.itemRenderer is IDropInListItemRenderer) { var listData:AdvancedDataGridListData = AdvancedDataGridListData(IDropInListItemRenderer(event.itemRenderer).listData); listData.label = _columns[event.columnIndex].itemToLabel(data); IDropInListItemRenderer(event.itemRenderer).listData = listData; } event.itemRenderer.data = data; } } else { if (event.reason != AdvancedDataGridEventReason.OTHER) { if (itemEditorInstance && _editedItemPosition) { // edit session is continued so restore focus and selection if (selectedIndex != _editedItemPosition.rowIndex) selectedIndex = _editedItemPosition.rowIndex; var fm:IFocusManager = focusManager; // trace("setting focus to itemEditorInstance", selectedIndex); if (itemEditorInstance is IFocusManagerComponent) fm.setFocus(IFocusManagerComponent(itemEditorInstance)); } } } if (event.reason == AdvancedDataGridEventReason.OTHER || !event.isDefaultPrevented()) { destroyItemEditor(); } } /** * @private */ protected function headerReleaseHandler(event:AdvancedDataGridEvent):void { if (! event.isDefaultPrevented()) { if (itemEditorInstance) endEdit(AdvancedDataGridEventReason.OTHER); var advancedDataGridEvent:AdvancedDataGridEvent = new AdvancedDataGridEvent(AdvancedDataGridEvent.SORT, false, true); advancedDataGridEvent.columnIndex = event.columnIndex; advancedDataGridEvent.dataField = event.dataField; advancedDataGridEvent.triggerEvent = event.triggerEvent; if (event.triggerEvent) { var mouseEvent:MouseEvent = event.triggerEvent as MouseEvent; if (mouseEvent) { advancedDataGridEvent.multiColumnSort = mouseEvent.ctrlKey; advancedDataGridEvent.removeColumnFromSort = mouseEvent.shiftKey; } } dispatchEvent(advancedDataGridEvent); } } /** * @private */ protected function sortHandler(event:AdvancedDataGridEvent):void { var columnName:String = event.dataField; var columnNumber:int = event.columnIndex; var sortFields:Array; var sort:ISort; if (!sortableColumns || !columns[columnNumber].sortable) return; //In case there is no dataField we will use the unique column uid to identify if the column is sorted if (columnName == null) columnName = itemToUID(columns[columnNumber]); // If normal click for single column sort // or // If ctrl+click when there is no previous sorting if (!event.multiColumnSort) { if (collection.sort && collection.sort.fields.length == 1 && (columnName && findSortField(columnName) > -1)) { // 1. Flipping order of single column sort // // Not allowed in default UI. You can't flip the sort order of a single // column sort using the header text part (i.e. multiColumnSort==false). // You can only flip by clicking on the icon part // (i.e. multiColumnSort==true), see below. if (sortExpertMode == true) sortDirection = flipSortOrder(columnName, columnNumber, collection); } else { // 2. Single column sort collection.sort = null; addSortField(columnName, columnNumber, collection); } } else { if (event.removeColumnFromSort) { removeSortField(columnName, columnNumber, collection); } // Ctrl+click without any previous sort is same as single column sort // Or New column added to multi column sort else if (findSortField(columnName) == -1) { addSortField(columnName, columnNumber, collection); } else if (findSortField(columnName) > -1) // Flipping order in multi column sort { if (collection.sort.fields.length == 1) { // 4. Flipping the order of a column in single column sort sortDirection = flipSortOrder(columnName, columnNumber, collection); } else { // 5. Flipping the order of a column in multi column sort // descending <-> ascending flipSortOrder(columnName, columnNumber, collection); sortDirection = null; } } } collection.refresh(); // 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 (headerIndex != -1) { selectedHeaderInfo = getHeaderInfo(columns[event.columnIndex]); headerIndex = event.columnIndex; selectColumnHeader(headerIndex); } invalidateHeaders(); } /** * @private */ private function deactivateHandler(event:Event):void { // if stage losing activation, set focus to DG so when we get it back // we popup an editor again if (itemEditorInstance) { endEdit(AdvancedDataGridEventReason.OTHER); losingFocus = true; setFocus(); } } /** * @private */ protected function headerNavigationHandler(event:KeyboardEvent):void { if (headerIndex == -1) return; // If rtl layout, need to swap LEFT and RIGHT so correct action // is done. var keyCode:uint = mapKeycodeForLayoutDirection(event); var newColumnIndex:int; if (keyCode == Keyboard.DOWN) { unselectColumnHeader(headerIndex, true); headerIndex = -1; } else if (keyCode == Keyboard.LEFT) { newColumnIndex = viewDisplayableColumnAtOffset(headerIndex, -1); if (newColumnIndex != -1) { unselectColumnHeader(headerIndex); selectedHeaderInfo = getHeaderInfo(columns[newColumnIndex]); headerIndex = newColumnIndex; selectColumnHeader(headerIndex); } } else if (keyCode == Keyboard.RIGHT) { newColumnIndex = viewDisplayableColumnAtOffset(headerIndex, +1); if (newColumnIndex != -1) { unselectColumnHeader(headerIndex); selectedHeaderInfo = getHeaderInfo(columns[newColumnIndex]); headerIndex = newColumnIndex; selectColumnHeader(headerIndex); } } else if (keyCode == Keyboard.SPACE) { if (sortableColumns && columns[headerIndex].sortable) { isKeyPressed = true; selectedHeaderInfo = getHeaderInfo(columns[headerIndex]); selectColumnHeader(headerIndex); var advancedDataGridEvent:AdvancedDataGridEvent = new AdvancedDataGridEvent(AdvancedDataGridEvent.SORT, false, true); advancedDataGridEvent.columnIndex = headerIndex; advancedDataGridEvent.dataField = columns[headerIndex].dataField; advancedDataGridEvent.multiColumnSort = event.ctrlKey; advancedDataGridEvent.removeColumnFromSort = event.shiftKey; 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); } event.stopPropagation(); } } }