////////////////////////////////////////////////////////////////////////////////
//
//  ADOBE SYSTEMS INCORPORATED
//  Copyright 2003-2007 Adobe Systems Incorporated
//  All Rights Reserved.
//
//  NOTICE: Adobe permits you to use, modify, and distribute this file
//  in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////

package mx.controls
{

import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
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.Point;
import flash.ui.Keyboard;
import flash.utils.Dictionary;
import flash.utils.describeType;

import mx.collections.CursorBookmark;
import mx.collections.ICollectionView;
import mx.collections.ISort;
import mx.collections.ISortField;
import mx.collections.ItemResponder;
import mx.collections.Sort;
import mx.collections.SortField;
import mx.collections.errors.ItemPendingError;
import mx.controls.dataGridClasses.DataGridBase;
import mx.controls.dataGridClasses.DataGridColumn;
import mx.controls.dataGridClasses.DataGridDragProxy;
import mx.controls.dataGridClasses.DataGridHeader;
import mx.controls.dataGridClasses.DataGridItemRenderer;
import mx.controls.dataGridClasses.DataGridListData;
import mx.controls.listClasses.IDropInListItemRenderer;
import mx.controls.listClasses.IListItemRenderer;
import mx.controls.listClasses.ListBaseContentHolder;
import mx.controls.listClasses.ListBaseSeekPending;
import mx.controls.listClasses.ListRowInfo;
import mx.controls.scrollClasses.ScrollBar;
import mx.core.ContextualClassFactory;
import mx.core.EdgeMetrics;
import mx.core.EventPriority;
import mx.core.FlexShape;
import mx.core.FlexSprite;
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.IRectangularBorder;
import mx.core.IUIComponent;
import mx.core.ScrollPolicy;
import mx.core.UIComponent;
import mx.core.UIComponentGlobals;
import mx.core.mx_internal;
import mx.events.CollectionEvent;
import mx.events.CollectionEventKind;
import mx.events.DataGridEvent;
import mx.events.DataGridEventReason;
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.managers.IFocusManager;
import mx.managers.IFocusManagerComponent;
import mx.skins.halo.ListDropIndicator;
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 DataGrid control or within the DataGrid control, 
 *  or in any other way attempts to edit an item.
 *
 *  @eventType mx.events.DataGridEvent.ITEM_EDIT_BEGINNING
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
[Event(name="itemEditBeginning", type="mx.events.DataGridEvent")]

/**
 *  Dispatched when the <code>editedItemPosition</code> property has been set
 *  and the item can be edited.
 *
 *  @eventType mx.events.DataGridEvent.ITEM_EDIT_BEGIN
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
[Event(name="itemEditBegin", type="mx.events.DataGridEvent")]

/**
 *  Dispatched when the item editor has just been instantiated.
 *
 *  @eventType mx.events.DataGridEvent.ITEM_EDITOR_CREATE
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
[Event(name="itemEditorCreate", type="mx.events.DataGridEvent")]

/**
 *  Dispatched when an item editing session ends for any reason.
 *
 *  @eventType mx.events.DataGridEvent.ITEM_EDIT_END
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
[Event(name="itemEditEnd", type="mx.events.DataGridEvent")]

/**
 *  Dispatched when an item renderer gets focus, which can occur if the user
 *  clicks on an item in the DataGrid control or navigates to the item using
 *  a keyboard.  Only dispatched if the item is editable.
 *
 *  @eventType mx.events.DataGridEvent.ITEM_FOCUS_IN
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
[Event(name="itemFocusIn", type="mx.events.DataGridEvent")]

/**
 *  Dispatched when an item renderer loses focus, which can occur if the user
 *  clicks another item in the DataGrid control or clicks outside the control,
 *  or uses the keyboard to navigate to another item in the DataGrid control
 *  or outside the control.
 *  Only dispatched if the item is editable.
 *
 *  @eventType mx.events.DataGridEvent.ITEM_FOCUS_OUT
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
[Event(name="itemFocusOut", type="mx.events.DataGridEvent")]

/**
 *  Dispatched when a user changes the width of a column, indicating that the 
 *  amount of data displayed in that column may have changed.
 *  If <code>horizontalScrollPolicy</code> is <code>"off"</code>, other
 *  columns shrink or expand to compensate for the columns' resizing,
 *  and they also dispatch this event.
 *
 *  @eventType mx.events.DataGridEvent.COLUMN_STRETCH
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
[Event(name="columnStretch", type="mx.events.DataGridEvent")]

/**
 *  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 DataGrid control has a default handler for this event that implements
 *  a single-column sort.  Multiple-column sort can be implemented by calling the 
 *  <code>preventDefault()</code> method to prevent the single column sort and setting 
 *  the <code>sort</code> property of the data provider.
 * <p>
 * <b>Note</b>: The sort arrows are defined by the default event handler for
 * the headerRelease event. If you call the <code>preventDefault()</code> method
 * in your event handler, the arrows are not drawn.
 * </p>
 *
 *  @eventType mx.events.DataGridEvent.HEADER_RELEASE
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
[Event(name="headerRelease", type="mx.events.DataGridEvent")]

/**
 *  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 DataGrids instead of having to
 *  set each one individually.  If you set the DataGridColumn'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 or its header.  This is a way to set
 *  an itemRenderer for a group of DataGrids instead of having to
 *  set each one individually.  If you set the DataGrid'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 <code>true</code>, shows vertical grid lines.
 *  If <code>false</code>, 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 <code>true</code>, shows horizontal grid lines.
 *  If <code>false</code>, 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.
 *  
 *  The default value for the Halo theme is <code>0xCCCCCC</code>.
 *  The default value for the Spark theme is <code>0x696969</code>.
 *  
 *  @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.
 *  @default 0xF7F7F7
 *  
 *  @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", theme="halo")]

/**
 *  The color of the row background when the user rolls over the row.
 *  
 *  The default value for the Halo theme is <code>0xB2E1FF</code>.
 *  The default value for the Spark theme is <code>0xCEDBEF</code>.
 *  
 *  @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.
 * 
 *  The default value for the Halo theme is <code>0x7FCEFF</code>.
 *  The default value for the Spark theme is <code>0xA8C6EE</code>.
 *  
 *  @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 DataGrid control.
 *  The default value for the Halo theme is <code>mx.skins.halo.DataGridHeaderBackgroundSkin</code>.
 *  The default value for the Spark theme is <code>mx.skins.spark.DataGridHeaderBackgroundSkin</code>.
 *  
 *  @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 DataGrid control.
 *  The default value for the Halo theme is <code>mx.skins.halo.DataGridHeaderSeparator</code>.
 *  The default value for the Spark theme is <code>mx.skins.spark.DataGridHeaderSeparatorSkin</code>.
 *  
 *  @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 rows in a DataGrid control. 
 *  By default, the DataGrid control uses the 
 *  <code>drawHorizontalLine()</code> and <code>drawVerticalLine()</code> 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 DataGrid control.
 *  By default, the DataGrid control uses the 
 *  <code>drawHorizontalLine()</code> and <code>drawVerticalLine()</code> 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 DataGrid control.
 *  By default, the DataGrid control uses the 
 *  <code>drawHorizontalLine()</code> and <code>drawVerticalLine()</code> 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 DataGrid control.
 *  By default, the DataGrid control uses the 
 *  <code>drawHorizontalLine()</code> and <code>drawVerticalLine()</code> 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 arrow that indicates the column sort 
 *  direction.
 *  The default value for the Halo theme is <code>mx.skins.halo.DataGridSortArrow</code>.
 *  The default value for the Spark theme is <code>mx.skins.spark.DataGridSortArrow</code>.
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
[Style(name="sortArrowSkin", type="Class", inherit="no")]

/**
 *  The class to use as the skin for the cursor that indicates that a column
 *  can be resized.
 *  The default value is the "cursorStretch" symbol from the Assets.swf file.
 *  
 *  @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="columnWidth", kind="property")]
[Exclude(name="iconField", kind="property")]
[Exclude(name="iconFunction", 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="showDataTips", kind="property")]
[Exclude(name="cornerRadius", kind="style")]

//--------------------------------------
//  Other metadata
//--------------------------------------

[AccessibilityClass(implementation="mx.accessibility.DataGridAccImpl")]

[DataBindingInfo("acceptedTypes", "{ dataProvider: &quot;String&quot; }")]

[DefaultBindingProperty(source="selectedItem", destination="dataProvider")]

[DefaultProperty("dataProvider")]

[DefaultTriggerEvent("change")]

[IconFile("DataGrid.png")]

[RequiresDataBinding(true)]

[Alternative(replacement="spark.components.DataGrid", since="4.5")]

/**
 *  The <code>DataGrid</code> control is like a List except that it can 
 *  show more than one column of data making it suited for showing 
 *  objects with multiple properties.
 *  <p>
 *  The DataGrid control provides the following features:
 *  <ul>
 *  <li>Columns of different widths or identical fixed widths</li>
 *  <li>Columns that the user can resize at runtime </li>
 *  <li>Columns that the user can reorder at runtime </li>
 *  <li>Optional customizable column headers</li>
 *  <li>Ability to use a custom item renderer for any column to display 
 *      data 
 *  other than text</li>
 *  <li>Support for sorting the data by clicking on a column</li>
 *  </ul>
 *  </p>
 *  The DataGrid control is intended for viewing data, and not as a
 *  layout tool like an HTML table.
 *  The mx.containers package provides those layout tools.
 *  
 *  <p>The DataGrid control has the following default sizing 
 *     characteristics:</p>
 *     <table class="innertable">
 *        <tr>
 *           <th>Characteristic</th>
 *           <th>Description</th>
 *        </tr>
 *        <tr>
 *           <td>Default size</td>
 *           <td>If the columns are empty, the default width is 300 
 *               pixels. If the columns contain information but define 
 *               no explicit widths, the default width is 100 pixels 
 *               per column. The DataGrid width is sized to fit the 
 *               width of all columns, if possible. 
 *               The default number of displayed rows, including the 
 *               header is 7, and each row, by default, is 20 pixels 
 *               high.
 *           </td>
 *        </tr>
 *        <tr>
 *           <td>Minimum size</td>
 *           <td>0 pixels.</td>
 *        </tr>
 *        <tr>
 *           <td>Maximum size</td>
 *           <td>5000 by 5000.</td>
 *        </tr>
 *     </table>
 *
 *  @mxml
 *  <p>
 *  The <code>&lt;mx:DataGrid&gt;</code> tag inherits all of the tag 
 *  attributes of its superclass, except for <code>labelField</code>, 
 *  <code>iconField</code>, and <code>iconFunction</code>, and adds the 
 *  following tag attributes:
 *  </p>
 *  <pre>
 *  &lt;mx:DataGrid
 *    <b>Properties</b>
 *    columns="<i>From dataProvider</i>"
 *    draggableColumns="true|false"
 *    editable="false|true"
 *    editedItemPosition="<code>null</code>"
 *    horizontalScrollPosition="null"
 *    imeMode="null"
 *    itemEditorInstance="null"
 *    minColumnWidth="<code>NaN</code>"
 *    resizableColumns="true|false"
 *    sortableColumns="true|false"
 *    
 *    <b>Styles</b>
 *    backgroundDisabledColor="0xEFEEEF"
 *    columnDropIndicatorSkin="DataGridColumnDropIndicator"
 *    columnResizeSkin="DataGridColumnResizeSkin"
 *    disabledIconColor="0x999999"
 *    headerColors="[#FFFFFF, #E6E6E6]"
 *    headerDragProxyStyleName="headerDragProxyStyle"
 *    headerSeparatorSkin="DataGridHeaderSeparator"
 *    headerStyleName="dataGridStyles"
 *    horizontalGridLineColor="0xF7F7F7"
 *    horizontalGridLines="false|true"
 *    horizontalLockedSeparatorSkin="undefined"
 *    horizontalSeparatorSkin="undefined"
 *    iconColor="0x111111"
 *    rollOverColor="0xB2E1FF"
 *    selectionColor="0x7FCEFF"
 *    sortArrowSkin="DataGridSortArrow"
 *    stretchCursor="<i>"cursorStretch" symbol from the Assets.swf file</i>"
 *    verticalGridLineColor="0xCCCCCC"
 *    verticalGridLines="false|true"
 *    verticalLockedSeparatorSkin="undefined"
 *    verticalSeparatorSkin="undefined"
 *     
 *    <b>Events</b>
 *    columnStretch="<i>No default</i>"
 *    headerRelease="<i>No default</i>"
 *    headerShift="<i>No default</i>"
 *    itemEditBegin="<i>No default</i>"
 *    itemEditBeginning="<i>No default</i>" 
 *    itemEditEnd="<i>No default</i>"
 *    itemFocusIn="<i>No default</i>"
 *    itemFocusOut="<i>No default</i>"
 *  /&gt;
 *   
 *  <b>The following DataGrid code sample specifies the column order:</b>
 *  &lt;mx:DataGrid&gt;
 *    &lt;mx:dataProvider&gt;
 *        &lt;mx:Object Artist="Pavement" Price="11.99"
 *          Album="Slanted and Enchanted"/&gt;
 *        &lt;mx:Object Artist="Pavement"
 *          Album="Brighten the Corners" Price="11.99"/&gt;
 *    &lt;/mx:dataProvider&gt;
 *    &lt;mx:columns&gt;
 *        &lt;mx:DataGridColumn dataField="Album"/&gt;
 *        &lt;mx:DataGridColumn dataField="Price"/&gt;
 *    &lt;/mx:columns&gt;
 *  &lt;/mx:DataGrid&gt;
 *  </pre>
 *  </p>
 *
 *  @see mx.controls.dataGridClasses.DataGridItemRenderer
 *  @see mx.controls.dataGridClasses.DataGridColumn
 *  @see mx.controls.dataGridClasses.DataGridDragProxy
 *  @see mx.events.DataGridEvent
 *
 *  @includeExample examples/SimpleDataGrid.mxml
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
public class DataGrid extends DataGridBase implements IIMESupport
{
    include "../core/Version.as";

    //--------------------------------------------------------------------------
    //
    //  Class mixins
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     *  Placeholder for mixin by DataGridAccImpl.
     */
    mx_internal static var createAccessibilityImplementation:Function;

    //--------------------------------------------------------------------------
    //
    //  Constructor
    //
    //--------------------------------------------------------------------------

    /**
     *  Constructor.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function DataGrid()
    {
        super();

        _columns = [];

        // pick a default row height
        setRowHeight(20);

        // Register default handlers for item editing and sorting events.

        addEventListener(DataGridEvent.ITEM_EDIT_BEGINNING,
                        itemEditorItemEditBeginningHandler,
                        false, EventPriority.DEFAULT_HANDLER);

        addEventListener(DataGridEvent.ITEM_EDIT_BEGIN,
                         itemEditorItemEditBeginHandler,
                         false, EventPriority.DEFAULT_HANDLER);

        addEventListener(DataGridEvent.ITEM_EDIT_END,
                         itemEditorItemEditEndHandler,
                         false, EventPriority.DEFAULT_HANDLER);

        addEventListener(DataGridEvent.HEADER_RELEASE,
                         headerReleaseHandler,
                         false, EventPriority.DEFAULT_HANDLER);
                         
        addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);                         
    }

    //--------------------------------------------------------------------------
    //
    //  Variables
    //
    //--------------------------------------------------------------------------

    [Inspectable(environment="none")]

    /**
     *  A reference to the currently active instance of the item editor, 
     *  if it exists.
     *
     *  <p>To access the item editor instance and the new item value when an 
     *  item is being edited, you use the <code>itemEditorInstance</code> 
     *  property. The <code>itemEditorInstance</code> property
     *  is not valid until after the event listener for
     *  the <code>itemEditBegin</code> event executes. Therefore, you typically
     *  only access the <code>itemEditorInstance</code> property from within 
     *  the event listener for the <code>itemEditEnd</code> event.</p>
     *
     *  <p>The <code>DataGridColumn.itemEditor</code> property defines the
     *  class of the item editor
     *  and, therefore, the data type of the item editor instance.</p>
     *
     *  <p>You do not set this property in MXML.</p>
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public var itemEditorInstance:IListItemRenderer;

    /**
     *  A reference to the item renderer
     *  in the DataGrid control whose item is currently being edited.
     *
     *  <p>From within an event listener for the <code>itemEditBegin</code>
     *  and <code>itemEditEnd</code> events,
     *  you can access the current value of the item being edited
     *  using the <code>editedItemRenderer.data</code> property.</p>
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function get editedItemRenderer():IListItemRenderer
    {
        if (!itemEditorInstance) return null;

        return actualContentHolder.listItems[actualRowIndex][actualColIndex];
    }

    /**
     *  @private
     *  true if we want to skip updating the headers during adjustListContent
     */
    private var skipHeaderUpdate:Boolean = false;

    /**
     *  @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
     *  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;

    /**
     *  @private
     *  The index of the column being sorted.
     */
    mx_internal var sortIndex:int = -1;

    /**
     *  @private
     *  The column being sorted.
     */
    private var sortColumn:DataGridColumn;

    /**
     *  @private
     *  The direction of the sort
     */
    mx_internal var sortDirection:String;

    /**
     *  @private
     *  The index of the last column being sorted on.
     */
    mx_internal var lastSortIndex:int = -1;

    /**
     *  @private
     */
    private var lastItemDown:IListItemRenderer;

    /**
     *  @private
     */
    private var lastItemFocused:DisplayObject;

    /**
     *  @private
     */
    private var displayWidth:Number;

    /**
     *  @private
     */
    private var lockedColumnWidth:Number = 0;

    /**
     *  @private
     *  The column that is being moved.
     */
    mx_internal var movingColumn:DataGridColumn;

    /**
     *  @private
     *  The column that is being resized.
     */
    mx_internal var resizingColumn:DataGridColumn;

    /**
     *  @private
     *  Columns with visible="true"
     */
    private 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
     */
    private var generatedColumns:Boolean = true;

    // last known position of item editor instance
    private var actualRowIndex:int;
    private var actualColIndex:int;
    private var actualContentHolder:ListBaseContentHolder;

    /**
     *  @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;


    //--------------------------------------------------------------------------
    //
    //  Overridden properties
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  baselinePosition
    //----------------------------------

    /**
     *  @private
     *  The baselinePosition of a DataGrid is calculated
     *  for its first column header.
     *  If the headers aren't shown, it is calculated as for List.
     */
    override public function get baselinePosition():Number
    {
        if (!validateBaselinePosition())
            return NaN;

        if (!showHeaders)
            return super.baselinePosition;
        
        var header0:IUIComponent = DataGridHeader(header).rendererArray[0];
        if (!header0)
            return super.baselinePosition;
            
        return header.y + header0.y + header0.baselinePosition;
    }

    /**
     *  @private
     *  Number of columns that can be displayed.
     *  Some may be offscreen depending on horizontalScrollPolicy
     *  and the width of the DataGrid.
     */
    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 (header)
            header.enabled = value;

        if (itemEditorInstance)
            endEdit(DataGridEventReason.OTHER);

        invalidateDisplayList();
    }

    //----------------------------------
    //  headerHeight
    //----------------------------------

    /**
     *  @private
     */
    override public function set headerHeight(value:Number):void
    {
        super.headerHeight = value;
        _originalHeaderHeight = isNaN(value) ? 22 : value;
        _originalExplicitHeaderHeight = !isNaN(value);        
    }

    //----------------------------------
    //  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 a DataGrid control. 
     *
     *  The DataGrid scrolls by columns so the value of the 
     *  <code>horizontalScrollPosition</code> 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 that scrolls by pixels.  The DataGrid control always aligns the left edge
     *  of a column with the left edge of the DataGrid 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;

        // columns have variable width so we need to recalc scroll parms
        scrollAreaChanged = true;

        columnsInvalid = true;
        calculateColumnSizes();

        // we are going to get a full repaint so don't repaint now
        if (itemsSizeChanged)
            return;

        if (oldValue != value)
        {
            removeClipMask();

            var bookmark:CursorBookmark;

            if (iterator)
                bookmark = iterator.bookmark;

            clearIndicators();
            clearVisibleData();
            //if we scrolled more than the number of scrollable columns
            makeRowsAndColumns(0, 0, listContent.width, listContent.height, 0, 0);
            if (lockedRowCount)
            {           
                var cursorPos:CursorBookmark;
                cursorPos = lockedRowContent.iterator.bookmark;
                makeRows(lockedRowContent, 0, 0, unscaledWidth, unscaledHeight, 0, 0, true, lockedRowCount);
                if (iteratorValid)
                    lockedRowContent.iterator.seek(cursorPos, 0);
            }

            if (headerVisible && header)
            {
                header.visibleColumns = visibleColumns;
                header.headerItemsChanged = true;
                header.invalidateSize();
                header.validateNow();
            }

            if (iterator && bookmark)
                iterator.seek(bookmark, 0);

            invalidateDisplayList();

            addClipMask(false);
        }
    }

    //----------------------------------
    //  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();
    }

    //----------------------------------
    //  itemRenderer
    //----------------------------------

    /**
     *  @private
     */
    override public function set itemRenderer(value:IFactory):void
    {
        super.itemRenderer = value;
        if (columns)
        {
            var n:int = columns.length;
            for (var i:int = 0; i < n; i++)
            {
                columns[i].resetFactories();
            }
        }
    }

    /**
     *  @private
     */
    override public function set verticalScrollPosition(value:Number):void
    {
        skipHeaderUpdate = true;

        var oldValue:Number = super.verticalScrollPosition;
        super.verticalScrollPosition = value;
        if (oldValue != value)
        {
            if (lockedColumnContent)
                drawRowGraphics(lockedColumnContent)
        }
        skipHeaderUpdate = false;
    }

    /**
     *  @private
     *  
     */
    override protected function createChildren():void
    {
        super.createChildren();

        if (!header)
        {
            header = new headerClass();
            header.styleName = this;
            addChild(header);
        }
    }

    //----------------------------------
    //  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 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.
     *
     * <p>The flash.system.IMEConversionMode class defines constants for the
     *  valid values for this property.
     *  You can also specify <code>null</code> to specify no IME.</p>
     *
     *  @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;
    }

    //----------------------------------
    //  itemRenderer
    //----------------------------------
    
    /**
     *  @private
     * 
     *  Defer creations 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 = DataGridItemRenderer;
            super.itemRenderer = new ContextualClassFactory(
                c, flexModuleFactory);
        }
        
        return super.itemRenderer;
    }
    
    //----------------------------------
    //  minColumnWidth
    //----------------------------------

    /**
     *  @private
     */
    private var _minColumnWidth:Number;

    /**
     *  @private
     */
    private var minColumnWidthInvalid:Boolean = false;

    [Inspectable(defaultValue="NaN")]

    /**
     *  The minimum width of the columns, in pixels.  If not NaN,
     *  the DataGrid 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();
    }

    //--------------------------------------------------------------------------
    //
    //  Properties
    //
    //--------------------------------------------------------------------------

    //----------------------------------
    //  columns
    //----------------------------------

    /**
     *  @private
     */
    private var _columns:Array; // the array of our DataGridColumns

    [Bindable("columnsChanged")]
    [Inspectable(category="General", arrayType="mx.controls.dataGridClasses.DataGridColumn")]

    /**
     *  An array of DataGridColumn objects, one for each column that
     *  can be displayed.  If not explicitly set, the DataGrid control 
     *  attempts to examine the first data provider item to determine the
     *  set of properties and display those properties in alphabetic
     *  order.
     *
     *  <p>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 DataGrid control returned a new copy of the array of columns and therefore
     *  did not notice the changes.</p>
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    override public function get columns():Array
    {
        return _columns.slice(0);
    }

    /**
     *  @private
     */
    override public function set columns(value:Array):void
    {
        var n:int;
        var i:int;

        n = _columns.length;
        for (i = 0; i < n; i++)
        {
            columnRendererChanged(_columns[i]);
        }
        
        freeItemRenderersTable = new Dictionary(false);
        columnMap = {};

        _columns = value.slice(0);
        columnsInvalid = true;
        generatedColumns = false;

        n = value.length;
        for (i = 0; i < n; i++)
        {
            var column:DataGridColumn = _columns[i];
            column.owner = this;
            column.colNum = i;
            if (column.cachedHeaderRenderer)
            {
                var item:DisplayObject = column.cachedHeaderRenderer as DisplayObject
                if (item.parent)
                    item.parent.removeChild(item);
                column.cachedHeaderRenderer = null;
            }
        }

        updateSortIndexAndDirection();

        fontContextChanged = true;
        invalidateProperties();
        itemsSizeChanged = true;
        invalidateDisplayList();
        dispatchEvent(new Event("columnsChanged"));
    }

    //----------------------------------
    //  draggableColumns
    //----------------------------------

    /**
     *  @private
     *  Storage for the draggableColumns property.
     */
    private var _draggableColumns:Boolean = true;

    [Inspectable(defaultValue="true")]

    /**
     *  A flag that indicates whether the user is allowed to reorder columns.
     *  If <code>true</code>, the user can reorder the columns
     *  of the DataGrid 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;
    }

    //----------------------------------
    //  editable
    //----------------------------------

    /**
     *  @private
     *  Storage for the draggableColumns property.
     */
    private var _editable:Boolean = false;
    
    [Inspectable(category="General")]

    /**
     *  A flag that indicates whether or not the user can edit
     *  items in the data provider.
     *  If <code>true</code>, the item renderers in the control are editable.
     *  The user can click on an item renderer to open an editor.
     *
     *  <p>You can turn off editing for individual columns of the
     *  DataGrid control using the <code>DataGridColumn.editable</code> property,
     *  or by handling the <code>itemEditBeginning</code> and
     *  <code>itemEditBegin</code> events</p>
     *
     *  @default false
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function get editable():Boolean
    {
        return _editable;
    }
    
    /**
     *  @private
     */
    public function set editable(value:Boolean):void
    {
        _editable = value;
    }

    //----------------------------------
    //  editedItemPosition
    //----------------------------------

    /**
     *  @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 and the last
     *  position where editing was attempted if editing
     *  was cancelled.  We restore editing
     *  to this point if we get focus from the TAB key
     */
    private var lastEditedItemPosition:*;

    /**
     *  @private
     */
    private var _editedItemPosition:Object;

    /**
     *  @private
     */
    private var itemEditorPositionChanged:Boolean = false;


    [Bindable("itemFocusIn")]

    /**
     *  The column and row index of the item renderer for the
     *  data provider item being edited, if any.
     *
     *  <p>This Object has two fields, <code>columnIndex</code> and 
     *  <code>rowIndex</code>,
     *  the zero-based column and row indexes of the item.
     *  For example: {columnIndex:2, rowIndex:3}</p>
     *
     *  <p>Setting this property scrolls the item into view and
     *  dispatches the <code>itemEditBegin</code> event to
     *  open an item editor on the specified item renderer.</p>
     *
     *  @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);
    }


    //----------------------------------
    //  resizableColumns
    //----------------------------------

    [Inspectable(category="General")]

    /**
     *  A flag that indicates whether the user can change the size of the
     *  columns.
     *  If <code>true</code>, the user can stretch or shrink the columns of 
     *  the DataGrid control by dragging the grid lines between the header cells.
     *  If <code>true</code>, individual columns must also have their 
     *  <code>resizable</code> properties set to <code>false</code> 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 <code>true</code>, the user can sort the data provider items by
     *  clicking on a column header cell. 
     *  The <code>DataGridColumn.dataField</code> property of the column
     *  or the <code>DataGridColumn.sortCompareFunction</code> 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 <code>true</code>, individual columns can be made to not respond
     *  to a click on a header by setting the column's <code>sortable</code>
     *  property to <code>false</code>.
     *
     *  <p>When a user releases the mouse button over a header cell, the DataGrid
     *  control dispatches a <code>headerRelease</code> event if both
     *  this property and the column's sortable property are <code>true</code>.  
     *  If no handler calls the <code>preventDefault()</code> method on the event, the 
     *  DataGrid sorts using that column's <code>DataGridColumn.dataField</code> or  
     *  <code>DataGridColumn.sortCompareFunction</code> properties.</p>
     * 
     *  @default true
     *
     *  @see mx.controls.dataGridClasses.DataGridColumn#dataField
     *  @see mx.controls.dataGridClasses.DataGridColumn#sortCompareFunction
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public var sortableColumns:Boolean = true;

    //--------------------------------------------------------------------------
    //
    //  Overridden methods
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     */
    override public function invalidateDisplayList():void
    {
        super.invalidateDisplayList();
        if (header)
        {
            header.headerItemsChanged = true;
            header.invalidateSize();
            header.invalidateDisplayList();
        }
        if (lockedColumnHeader)
        {
            lockedColumnHeader.headerItemsChanged = true;
            lockedColumnHeader.invalidateSize();
            lockedColumnHeader.invalidateDisplayList();
        }
    }

    [Inspectable(category="Data", defaultValue="undefined")]

    /**
     *  @private
     */
    override public function set dataProvider(value:Object):void
    {
        if (itemEditorInstance)
            endEdit(DataGridEventReason.OTHER);

        lastEditedItemPosition = null;

        super.dataProvider = value;
    }

    /**
     *  @private
     */
    override protected function initializeAccessibility():void
    {
        if (DataGrid.createAccessibilityImplementation != null)
            DataGrid.createAccessibilityImplementation(this);
    }

    /**
     *  @private
     *  Measures the DataGrid based on its contents,
     *  summing the total of the visible column widths.
     */
    override protected function measure():void
    {
        super.measure();

        if (explicitRowCount != -1)
        {
            measuredHeight += _explicitHeaderHeight ? headerHeight : header.getExplicitOrMeasuredHeight();
            measuredMinHeight += _explicitHeaderHeight ? headerHeight : header.getExplicitOrMeasuredHeight();
        }

        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 DataGrid.
     */
    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.

        // trace(">>updateDisplayList");
        if (displayWidth != unscaledWidth - viewMetrics.right - viewMetrics.left)
        {
            displayWidth = unscaledWidth - viewMetrics.right - viewMetrics.left;
            columnsInvalid = true;
        }

        calculateColumnSizes();

        if (itemEditorPositionChanged)
        {
            itemEditorPositionChanged = 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)
                scrollToEditedItem(editedItemPosition.rowIndex, editedItemPosition.colIndex);
        }

        super.updateDisplayList(unscaledWidth, unscaledHeight);

        if (collection && collection.length)
        {
            setRowCount(listItems.length);

            if (listItems.length)
                setColumnCount(listItems[0].length);
            else
                setColumnCount(0);
        }

        // If we have a vScroll only, we want the scrollbar to be below
        // the header.
        if (verticalScrollBar != null && verticalScrollBar.visible &&
           (horizontalScrollBar == null || !horizontalScrollBar.visible) && 
           headerVisible)
        {
            var hh:Number = header.height;
            var bm:EdgeMetrics = borderMetrics;
            
            if (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 = true;
                headerMask.width += verticalScrollBar.getExplicitOrMeasuredWidth();
                
                if (!DataGridHeader(header).needRightSeparator)
                {
                    header.invalidateDisplayList();
                    DataGridHeader(header).needRightSeparator = true;
                }
            }
            else
            {
                if (DataGridHeader(header).needRightSeparator)
                {
                    header.invalidateDisplayList();
                    DataGridHeader(header).needRightSeparator = false;
                }
            }
        }
        else
        {
            if (DataGridHeader(header).needRightSeparator)
            {
                header.invalidateDisplayList();
                DataGridHeader(header).needRightSeparator = false;
            }
        }

        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;
        }

        drawRowBackgrounds();
        drawLinesAndColumnBackgrounds();

        if (lockedRowCount && lockedRowContent)
        {
            drawRowGraphics(lockedRowContent);
            drawLinesAndColumnGraphics(lockedRowContent, visibleColumns, { bottom: 1});
            if (lockedColumnCount)
            {
                drawRowGraphics(lockedColumnAndRowContent);
                drawLinesAndColumnGraphics(lockedColumnAndRowContent, visibleLockedColumns, { right: 1, bottom: 1});
            }
        }
        if (lockedColumnCount)
        {
            drawRowGraphics(lockedColumnContent)
            drawLinesAndColumnGraphics(lockedColumnContent, visibleLockedColumns, { right: 1})
        }

        // trace("<<updateDisplayList");
    }

    /**
     *  @private
     */
    override protected function makeRowsAndColumns(left:Number, top:Number,
                                                right:Number, bottom:Number,
                                                firstCol:int, firstRow:int,
                                                byCount:Boolean = false, rowsNeeded:uint = 0):Point
    {
        allowItemSizeChangeNotification = false;

        var pt:Point = super.makeRowsAndColumns(left, top, right, bottom,
                                                firstCol, firstRow, byCount, rowsNeeded);
        if (itemEditorInstance)
        {
            actualContentHolder.setChildIndex(DisplayObject(itemEditorInstance),
                                      actualContentHolder.numChildren - 1);
            var col:DataGridColumn;
            if (lockedColumnCount && editedItemPosition.columnIndex <= visibleLockedColumns[lockedColumnCount - 1].colNum)
                col = visibleLockedColumns[actualColIndex];
            else
                col = visibleColumns[actualColIndex];

            var item:IListItemRenderer = actualContentHolder.listItems[actualRowIndex][actualColIndex];
            var rowData:ListRowInfo = actualContentHolder.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, actualContentHolder.width - itemEditorInstance.x),
                                         Math.min(rowData.height + dh, actualContentHolder.height - itemEditorInstance.y));
                item.visible = false;

            }

            var lines:Sprite = Sprite(actualContentHolder.getChildByName("lines"));
            if (lines)
                actualContentHolder.setChildIndex(lines, actualContentHolder.numChildren - 1);
        }

        allowItemSizeChangeNotification = variableRowHeight;
        return pt;
    }

   /**
     *  @private
     */
    override protected function commitProperties():void
    {
        var c:DataGridColumn;
        var n:int;
        var j:int;

        super.commitProperties();

        if (fontContextChanged)
        {
            fontContextChanged = false;
            n = columns.length;
            for (j = 0; j < n; j++)
            {
                c = columns[j];
                c.determineFontContext();
            }
        }

        if (itemsNeedMeasurement)
        {
            itemsNeedMeasurement = false;
            if (isNaN(explicitRowHeight))
            {
                if (iterator && columns.length > 0)
                {
                    //set DataGridBase.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 ch:Number = 0;
                    n = columns.length;
                    for (j = 0; j < n; j++)
                    {
                        c = columns[j];

                        if (!c.visible)
                            continue;

                        item = c.getMeasuringRenderer(false, data);
                        if (DisplayObject(item).parent == null)
                            listContent.addChild(DisplayObject(item));
                        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);
                }
                    
            // If we don't have data yet or any column info we 
            // want to make sure to make another measurement pass when
            // we eventually do have valid data.
            if (!collection || collection.length == 0 || columns.length == 0)
                itemsNeedMeasurement = true;
            }
        }
    }

    /**
     *  @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
     */

    mx_internal function setupRendererFromData(c:DataGridColumn, item:IListItemRenderer, data:Object):void
    {
        var rowData:DataGridListData = DataGridListData(makeListData(data, itemToUID(data), 0, c.colNum, c));
        if (item is IDropInListItemRenderer)
            IDropInListItemRenderer(item).listData = data ? rowData : null;
        item.data = data;
        item.explicitWidth = getWidthOfItem(item, c);
        UIComponentGlobals.layoutManager.validateClient(item, true);
    }

    /**
     *  @private
     */
    override public function measureHeightOfItems(index:int = -1, count:int = 0):Number
    {
        return measureHeightOfItemsUptoMaxHeight(index, count);
    }

    /**
     *  @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:DataGridColumn;
        var ch:Number = 0;
        var n:int;
        var j:int;

        var paddingTop:Number = getStyle("paddingTop");
        var paddingBottom:Number = getStyle("paddingBottom");

        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);
            count --;
            index = 0;
        }

        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)
        {
            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 = c.getMeasuringRenderer(false, data);
                    if (DisplayObject(item).parent == null)
                        listContent.addChild(DisplayObject(item));
                    setupRendererFromData(c, item, data);
                        ch = Math.ceil(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 = (headerVisible) ? i + 1 : 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
     */
    mx_internal function calculateHeaderHeight():Number
    {
        if (!columns.length)
            return rowHeight;

        // block bad behavior from PDG
        if (!listContent)
            return rowHeight;

        var item:IListItemRenderer;
        var c:DataGridColumn;
        var rowData:DataGridListData;
        var ch:Number = 0;
        var n:int;
        var j:int;

        var paddingTop:Number = getStyle("paddingTop");
        var paddingBottom:Number = getStyle("paddingBottom");

        if (showHeaders)
        {
            ch = 0;
            n = columns.length;

            if (_headerWordWrapPresent)
            {
                _headerHeight = _originalHeaderHeight;
                _explicitHeaderHeight = _originalExplicitHeaderHeight;
            }

            for (j = 0; j < n; j++)
            {
                c = columns[j];

                if (!c.visible)
                    continue;

                item = c.cachedHeaderRenderer;
                if (!item)
                {
                    item = createColumnItemRenderer(c, true, c);
                    item.styleName = c;
                    c.cachedHeaderRenderer = item;
                }
                if (DisplayObject(item).parent == null)
                {
                    listContent.addChild(DisplayObject(item));
                    item.visible = false;
                }
                rowData = DataGridListData(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;
                _explicitHeaderHeight = true;
            }
        }
        return ch;
    }

    private var _headerWordWrapPresent:Boolean = false;
    private var _originalExplicitHeaderHeight:Boolean = false;
    private var _originalHeaderHeight:Number = 0;

    /**
     *  @private
     */
    override protected function calculateRowHeight(data:Object, hh:Number, skipVisible:Boolean = false):Number
    {
        var item:IListItemRenderer;
        var c:DataGridColumn;

        var n:int = columns.length;
        var j:int;
        var k:int = 0;
        var l:int = visibleLockedColumns.length;

        if (skipVisible && visibleColumns.length == _columns.length)
            return hh;

        var paddingTop:Number = getStyle("paddingTop");
        var paddingBottom:Number = getStyle("paddingBottom");

        for (j = 0; j < n; j++)
        {
            // skip any columns that are visible
            if (skipVisible && k < l && visibleLockedColumns[k].colNum == columns[j].colNum)
            {
                k++;
                continue;
            }
            if (skipVisible && k - l < visibleColumns.length && visibleColumns[k - l].colNum == columns[j].colNum)
            {
                k++;
                continue;
            }
            c = columns[j];

            if (!c.visible)
                continue;

            item = c.getMeasuringRenderer(false, data);
            if (DisplayObject(item).parent == null)
                listContent.addChild(DisplayObject(item));
            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(DataGridEventReason.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 function displayingPartialRow():Boolean
    {
        var index:int = listItems.length - 1 - offscreenExtraRowsBottom;
        if (rowInfo[index].y + rowInfo[index].height > listContent.heightExcludingOffsets - listContent.topOffset)
            return true;
        return false;
    }

    /**
     *  @private
     */
    override protected function configureScrollBars():void
    {
        if (columnsInvalid)
            return;

        if (!displayableColumns)
            return;

        // for purposes of computing rows, we need to accomodate
        // the case where all the visible columns are locked columns
        var countableContentListItems:Array = this.listItems;
        if (visibleColumns && !visibleColumns.length && visibleLockedColumns && visibleLockedColumns.length)
            countableContentListItems = lockedColumnContent.listItems;
            
        var oldHorizontalScrollBar:Object = horizontalScrollBar;
        var oldVerticalScrollBar:Object = verticalScrollBar;

        var rowCount:int = countableContentListItems.length;
        if (rowCount == 0)
        {
            // Get rid of any existing scrollbars.
            if (oldHorizontalScrollBar || oldVerticalScrollBar)
                // protect against situation where the scrollbars
                // cause re-layout and the listContent is sized
                // to zero because of number of lockedRowCount
                if (listContent.height) 
                    setScrollBarProperties(0, 0, 0, 0);

            return;
        }

        // partial last rows don't count
        if (rowCount > 1 && displayingPartialRow()) 
            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;
        // don't count filler rows at the bottom either.
        var fillerRows:int = 0;
        while (rowCount && countableContentListItems[rowCount - 1].length == 0)
        {
            // as long as we're past the end of the collection, add up
            // fillerRows
            if (collection && rowCount + offset >= collection.length - lockedRowCount)
            {
                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)
        {
            var bookmark:CursorBookmark = iterator.bookmark;
            var rowIndex:int = bookmark.getViewIndex();
            if (verticalScrollPosition != rowIndex - lockedRowCount)
            {
                // we got totally out of sync, probably because a filter
                // removed or added rows
                super.verticalScrollPosition = Math.max(rowIndex - lockedRowCount, 0);
            }

            if (adjustVerticalScrollPositionDownward(Math.max(rowCount, 1)))
                return;
        }

        rowCount -= (offscreenExtraRowsTop + offscreenExtraRowsBottom);

        var collectionHasRows:Boolean = collection && collection.length > 0;

        var colCount:int = (collectionHasRows && rowCount > 0) ? listItems[0].length : visibleColumns.length;

        // 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 (collectionHasRows && rowCount > 0 && colCount > 1 && 
            listItems[0][colCount - 1].x + 
            visibleColumns[colCount - 1].width > (displayWidth - listContent.x + viewMetrics.left))
            colCount--;
        else if (colCount > 1 && !collectionHasRows)
        {
            // the slower computation requires adding up the previous columns
            var colX:int = 0;
            for (var i:int = 0; i < visibleColumns.length; i++)
            {
                colX += visibleColumns[i].width;
            }
            if (colX > (displayWidth - listContent.x + viewMetrics.left))
                colCount--;
        }

        // trace("configureSB", verticalScrollPosition);

        setScrollBarProperties(displayableColumns.length - lockedColumnCount, Math.max(colCount, 1),
                            collection ? collection.length - lockedRowCount : 0,
                            Math.max(rowCount, 1));

        if ((!verticalScrollBar || !verticalScrollBar.visible) && collection &&
            collection.length - lockedRowCount > rowCount)
            maxVerticalScrollPosition = collection.length - lockedRowCount - rowCount;
        if ((!horizontalScrollBar || !horizontalScrollBar.visible) && 
            displayableColumns.length - lockedColumnCount  > colCount - lockedColumnCount)
            maxHorizontalScrollPosition = displayableColumns.length - lockedColumnCount - colCount;
    }

    /**
     *  @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:DataGridColumn;
        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.heightExcludingOffsets - listContent.topOffset - 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 = c.getMeasuringRenderer(false, data);
                        if (DisplayObject(item).parent == null)
                            listContent.addChild(DisplayObject(item));
                        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
     */
    override public function calculateDropIndex(event:DragEvent = null):int
    {
        if (event)
        {
            var item:IListItemRenderer;
            var lastItem: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 (listItems[i][0])
                    lastItem = listItems[i][0];
                    
                if (rowInfo[i].y <= pt.y && pt.y < rowInfo[i].y + rowInfo[i].height)
                {
                    item = listItems[i][0];
                    break;
                }
            }
            if (!item && lockedRowContent)
            {
                pt = listContent.localToGlobal(pt);
                pt = lockedRowContent.globalToLocal(pt);
                n = lockedRowContent.listItems.length;
                for (i = 0; i < n; i++)
                {
                    if (lockedRowContent.rowInfo[i].y <= pt.y && pt.y < lockedRowContent.rowInfo[i].y + lockedRowContent.rowInfo[i].height)
                    {
                        item = lockedRowContent.listItems[i][0];
                        break;
                    }
                }
            }


            if (item)
                lastDropIndex = itemRendererToIndex(item);
            else
            {
                if (lastItem)
                    lastDropIndex = itemRendererToIndex(lastItem) + 1;
                else
                    lastDropIndex = collection ? collection.length : 0;
            }
        }

        return lastDropIndex;
    }
    
    /**
     *  @private
     */
    override protected function drawRowBackgrounds():void
    {
        drawRowGraphics(listContent);
    }

    /**
     *  @private
     */
    protected function drawRowGraphics(contentHolder:ListBaseContentHolder):void
    {
        var rowBGs:Sprite = Sprite(contentHolder.getChildByName("rowBGs"));
        if (!rowBGs)
        {
            rowBGs = new FlexSprite();
            rowBGs.mouseEnabled = false;
            rowBGs.name = "rowBGs";
            contentHolder.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)
        {
            while (rowBGs.numChildren > n)
            {
                rowBGs.removeChildAt(rowBGs.numChildren - 1);
            }
            return;
        }

        styleManager.getColorNames(colors);

        var curRow:int = 0;

        var i:int = 0;
        var actualRow:int = verticalScrollPosition;
        var n:int = contentHolder.listItems.length;

        while (curRow < n)
        {
            drawRowBackground(rowBGs, i++, contentHolder.rowInfo[curRow].y, contentHolder.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;

        r = super.mouseEventToItemRenderer(event);

        return r == itemEditorInstance ? null : r;
    }

    /**
     *  @private
     */
    override protected function get dragImage():IUIComponent
    {
        var image:DataGridDragProxy = new DataGridDragProxy();
        image.owner = this;
        image.moduleFactory = moduleFactory;
        return image;
    }

    //--------------------------------------------------------------------------
    //
    //  Methods
    //
    //--------------------------------------------------------------------------


    /**
     *  @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
    {
        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:DataGridColumn = _columns[i];
                _columns[i] = _columns[j];
                _columns[j] = c;
                _columns[i].colNum = i;
                _columns[j].colNum = j;
            }

            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;
            invalidateDisplayList();
            if (lockedColumnHeader)
                lockedColumnHeader.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:DataGridColumn;
            var newCols:Array = [];
            var cols:Array;
            if (dataProvider)
            {
                try
                {
                    iterator.seek(CursorBookmark.FIRST);
                    if (!iteratorValid)
                    {
                        iteratorValid = true;
                        lastSeekPending = null;
                    }
                }
                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 DataGridColumn();
                        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 DataGridColumn();
                    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
     */
    private function calculateColumnSizes():void
    {
        var delta:Number;
        var n:int;
        var i:int;
        var totalWidth:Number = 0;
        var col:DataGridColumn;
        var cw:Number;

        if (columns.length == 0)
        {
            visibleColumns = [];
            visibleLockedColumns = [];
            lockedColumnWidth = 0;
            columnsInvalid = false;
            return;
        }

        // no columns are visible so figure out which ones
        // to make visible
        if (columnsInvalid)
        {
            columnsInvalid = false;
            visibleColumns = [];
            visibleLockedColumns = [];
            lockedColumnWidth = 0;

            if (minColumnWidthInvalid)
            {
                n = columns.length;
                for (i = 0; i < n; i++)
                {
                    columns[i].minWidth = minColumnWidth;
                }
                minColumnWidthInvalid = false;
            }

            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 k:int = 0; k < i; k++)
                        displayableColumns[k] = _columns[k];
                }
            }

            // If there are no hidden columns, displayableColumns points to
            // _columns (we don't need a duplicate copy of _columns).
            if (!displayableColumns)
                displayableColumns = _columns;

            // if no hscroll, then pack columns in available space
            if (horizontalScrollPolicy == ScrollPolicy.OFF)
            {
                n = displayableColumns.length;
                for (i = 0; i < n; i++)
                {
                    col = displayableColumns[i];

                    if (i < lockedColumnCount)
                    {
                        visibleLockedColumns.push(col);
                    }
                    else
                        visibleColumns.push(col);
                }
            }
            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)
                    {
                        if (i < lockedColumnCount)
                        {
                            lockedColumnWidth += Math.max(isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth, col.minWidth);
                            visibleLockedColumns.push(col);
                        }
                        else
                            visibleColumns.push(col);
                        totalWidth += Math.max(isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth, col.minWidth);
                        if (col.width != col.preferredWidth)
                            col.setWidth(col.preferredWidth);
                    }
                    else
                    {
                        if (visibleColumns.length == 0)
                            visibleColumns.push(displayableColumns[0]);
                        break;
                    }
                }
            }
        }

        var lastColumn:DataGridColumn;
        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 && !visibleColumns[i].newlyVisible)
                {
                    // 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;
            }
            n = visibleLockedColumns.length;
            for (i = 0; i < n; i++)
            {
                // trace("column " + i + " width = " + visibleLockedColumns[i].width);
                if (visibleLockedColumns[i].resizable && !visibleLockedColumns[i].newlyVisible)
                {
                    // trace("    resizable");
                    if (!isNaN(visibleLockedColumns[i].explicitWidth))
                    {
                        // trace("    explicit width " + visibleLockedColumns[i].width);
                        fixedWidth += visibleLockedColumns[i].width;
                    }
                    else
                    {
                        // trace("    implicitly resizable");
                        numResizable++;
                        fixedWidth += visibleLockedColumns[i].minWidth;
                        // trace("    minWidth " + visibleLockedColumns[i].minWidth);
                    }
                }
                else
                {
                    // trace("    not resizable");
                    fixedWidth += visibleLockedColumns[i].width;
                }

                totalWidth += visibleLockedColumns[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 = visibleLockedColumns.length;
                for (i = 0; i < n; i++)
                {
                    if (visibleLockedColumns[i].resizable && !visibleLockedColumns[i].newlyVisible && isNaN(visibleLockedColumns[i].explicitWidth))
                    {
                        lastColumn = visibleLockedColumns[i];
                        if (totalWidth > displayWidth)
                            ratio = (lastColumn.width - lastColumn.minWidth)/ (totalWidth - fixedWidth);
                        else
                            ratio = lastColumn.width / totalWidth;
                        newSize = Math.floor(lastColumn.width - (totalWidth - displayWidth) * ratio);
                        minWidth = visibleLockedColumns[i].minWidth;
                        visibleLockedColumns[i].setWidth(newSize > minWidth ? newSize : minWidth);
                        // trace("column " + i + " set to " + visibleLockedColumns[i].width);
                    }
                    newTotal -= visibleLockedColumns[i].width;
                    visibleLockedColumns[i].newlyVisible = false;
                }
                n = visibleColumns.length;
                for (i = 0; i < n; i++)
                {
                    if (visibleColumns[i].resizable && !visibleColumns[i].newlyVisible && isNaN(visibleColumns[i].explicitWidth))
                    {
                        lastColumn = visibleColumns[i];
                        if (totalWidth > displayWidth)
                            ratio = (lastColumn.width - lastColumn.minWidth)/ (totalWidth - fixedWidth);
                        else
                            ratio = lastColumn.width / totalWidth;
                        newSize = Math.floor(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;
                    visibleColumns[i].newlyVisible = false;
                }
                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 = visibleLockedColumns.length;
                for (i = 0; i < n; i++)
                {
                    lastColumn = visibleLockedColumns[i];
                    ratio = lastColumn.width / totalWidth;
                    //totalWidth -= visibleLockedColumns[i].width;
                    newSize = Math.floor(displayWidth * ratio);
                    lastColumn.setWidth(newSize);
                    lastColumn.explicitWidth = NaN;
                    // trace("column " + i + " set to " + visibleLockedColumns[i].width);
                    newTotal -= newSize;
                }
                n = visibleColumns.length;
                for (i = 0; i < n; i++)
                {
                    lastColumn = visibleColumns[i];
                    ratio = lastColumn.width / totalWidth;
                    //totalWidth -= visibleColumns[i].width;
                    newSize = Math.floor(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 - lockedColumnWidth)
                {
                    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 - lockedColumnWidth && i < displayableColumns.length)
            {
                n = displayableColumns.length;
                for (; i < n && totalWidth < displayWidth - lockedColumnWidth; i++)
                {
                    col = displayableColumns[i];

                    visibleColumns.push(col);
                    totalWidth += isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth;
                }
            }
            else if (totalWidth < displayWidth - lockedColumnWidth && horizontalScrollPosition > 0)
            {
                while (totalWidth < displayWidth - lockedColumnWidth && horizontalScrollPosition > 0)
                {
                    col = displayableColumns[lockedColumnCount + horizontalScrollPosition - 1];
                    cw = isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth;
                    if (cw < displayWidth - lockedColumnWidth - totalWidth)
                    {
                        visibleColumns.splice(0, 0, col);
                        super.horizontalScrollPosition--;
                        totalWidth += cw;
                    }
                    else
                        break;
                }
            }

            lastColumn = visibleColumns[visibleColumns.length - 1];
            cw = isNaN(lastColumn.explicitWidth) ? lastColumn.preferredWidth : lastColumn.explicitWidth;
            newSize = cw + displayWidth - lockedColumnWidth - 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 == 1 && 
                    lastColumn == displayableColumns[displayableColumns.length - 1])
            {
                maxHorizontalScrollPosition =
                    displayableColumns.length - visibleColumns.length;
            }
            else
            {
                maxHorizontalScrollPosition =
                    displayableColumns.length - visibleColumns.length + 1;
            }
        }
        lockedColumnWidth = 0;
        if (visibleLockedColumns.length)
        {
            n = visibleLockedColumns.length;
            for (i = 0; i < n; i++)
            {
                col = visibleLockedColumns[i];
                lockedColumnWidth += col.width;
            }
        }
    }

    /**
     *  @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
     */
    override 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) && (!visibleLockedColumns || visibleLockedColumns.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 = _columns.length;
            var i:int;
            for (i = 0; i < n; i++)
            {
                if (col == _columns[i].colNum)
                    break;
            }
            if (i >= _columns.length - 1)   // no resize of right most column
                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:DataGridColumn;
            var newWidth:Number;
            //non-resizable columns don't count though
            for (i = col + 1; i < n; i++)
            {
                if (_columns[i].visible)
                    if (_columns[i].resizable)
                        totalSpace += _columns[i].width;
            }

            var newTotalSpace:Number = _columns[col].width - w + totalSpace;
            if (totalSpace)
            {
                _columns[col].setWidth(w);
                _columns[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 (_columns[i].visible)
                    if (_columns[i].resizable)
                    {
                        newWidth = Math.floor(_columns[i].width
                                                    * newTotalSpace / totalSpace);
                        if (newWidth < _columns[i].minWidth)
                            newWidth = _columns[i].minWidth;
                        _columns[i].setWidth(newWidth);
                        totX += _columns[i].width;
                        lastColumn = _columns[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 = _columns[col].width - totX + newTotalSpace;
                if (newWidth < _columns[col].minWidth)
                    newWidth = _columns[col].minWidth;
                _columns[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

        invalidateDisplayList();
    }

    /**
     *  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 <code>backgroundAlpha</code> 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 10th 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 contentHolder:ListBaseContentHolder = ListBaseContentHolder(s.parent);

        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,
                                     contentHolder.height -
                                     y);

        var g:Graphics = background.graphics;
        g.clear();
        g.beginFill(color, getStyle("backgroundAlpha"));
        g.drawRect(0, 0, contentHolder.width, 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 DataGridColumn for that column has changed.
     *
     *  @param color The suggested color for the indicator
     * 
     *  @param column The column of the DataGrid 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:DataGridColumn):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();
        g.beginFill(color);

        var lastRow:Object = rowInfo[listItems.length - 1];
        var columnHeader:DataGridHeader = (s.parent == lockedColumnContent) ? 
                                        DataGridHeader(lockedColumnHeader) : 
                                        DataGridHeader(header);
            
        var xx:Number = columnHeader.rendererArray[columnIndex].x
        var yy:Number = rowInfo[0].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(lastRow.y + lastRow.height,
                                     listContent.height - yy);

        g.drawRect(xx, yy, columnHeader.visibleColumns[columnIndex].width,
                   listContent.height - yy);
        g.endFill();
    }

    /**
     *  Creates and sizes the horizontalSeparator skins. If none have been specified, then draws the lines using
     *  drawHorizontalLine(). 
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    private function drawHorizontalSeparator(s:Sprite, rowIndex:int, color:uint, y:Number, useLockedSeparator:Boolean = false):void
    {
        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 - lockedColumnWidth, 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 contentHolder:ListBaseContentHolder = s.parent.parent as ListBaseContentHolder;
        var g:Graphics = s.graphics;

        g.lineStyle(1, color);
        g.moveTo(0, y);
        g.lineTo(contentHolder.width, y);
    }
    
    /**
     *  Creates and sizes the verticalSeparator skins. If none have been specified, then draws the lines using
     *  drawVerticalLine(). 
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    private function drawVerticalSeparator(s:Sprite, colIndex:int, color:uint, x:Number, useLockedSeparator:Boolean = false):void
    {
        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, s.parent.parent.height); 
            createThisSkin.move(x - Math.round(mWidth / 2), 0);      
        }
        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);
        }
        
    }

    /**
     *  Draw 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
    {
        var contentHolder:ListBaseContentHolder = s.parent.parent as ListBaseContentHolder;
        //draw our vertical lines
        var g:Graphics = s.graphics;
        g.lineStyle();
        g.beginFill(color);
        g.drawRect(x, headerVisible ? 0 : 1, 1, contentHolder.height);
        g.endFill();
    }
    
    /**
     *  Draw lines between columns, and column backgrounds.
     *  This implementation calls the <code>drawHorizontalLine()</code>, 
     *  <code>drawVerticalLine()</code>,
     *  and <code>drawColumnBackground()</code> 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
    {
        drawLinesAndColumnGraphics(listContent, visibleColumns, {});
    }

    /**
     *  Draw lines between columns, and column backgrounds.
     *  This implementation calls the <code>drawHorizontalLine()</code>, 
     *  <code>drawVerticalLine()</code>,
     *  and <code>drawColumnBackground()</code> 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.
     * 
     *  @param contentHolder A container of all of the DataGrid's item renderers and item editors.
     *  @param visibleColumns An array of the visible columns in the DataGrid.
     *  @param separators An object that defines the top, bottom, left, and right lines that separate the columns and rows.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    protected function drawLinesAndColumnGraphics(contentHolder:ListBaseContentHolder, visibleColumns:Array, separators:Object):void
    {
        var lines:Sprite = Sprite(contentHolder.getChildByName("lines"));
        if (!lines)
        {
            lines = new UIComponent();
            lines.name = "lines";
            lines.cacheAsBitmap = true;
            lines.mouseEnabled = false;
            contentHolder.addChild(lines);
        }
        contentHolder.setChildIndex(lines, contentHolder.numChildren - 1);
        var rowInfo:Array = contentHolder.rowInfo;

        lines.graphics.clear();

        var linesBody:Sprite = Sprite(lines.getChildByName("body"));
        
        if (!linesBody)
        {
            linesBody = new UIComponent();
            linesBody.name = "body";
            linesBody.mouseEnabled = false;
            lines.addChild(linesBody);
        }
        
        linesBody.graphics.clear();
        while (linesBody.numChildren)
        {
            linesBody.removeChildAt(0);
        }


        var lineCol:uint;

        var i:int;

        var len:uint = visibleColumns ? visibleColumns.length : 0;
        var rowlen:uint = contentHolder.listItems.length

        // draw horizontalGridlines if needed.
        lineCol = getStyle("horizontalGridLineColor");
        if (getStyle("horizontalGridLines"))
        {
            for (i = 0; i < rowlen; i++)
            {
                var yy:Number = rowInfo[i].y + rowInfo[i].height;
                if (yy < contentHolder.height)
                    drawHorizontalSeparator(linesBody, i, lineCol, yy);
            }
        }
        if (separators.top)
            drawHorizontalSeparator(linesBody, i++, 0, rowInfo[0].y, true);
        if (separators.bottom && rowlen > 0)
            drawHorizontalSeparator(linesBody, i++, 0, rowInfo[rowlen - 1].y + rowInfo[rowlen - 1].height, true);

        var vLines:Boolean = getStyle("verticalGridLines");
        lineCol = getStyle("verticalGridLineColor");

        if (len)
        {
            var colBGs:Sprite = Sprite(contentHolder.getChildByName("colBGs"));
            // traverse the columns, set the sizes, draw the column backgrounds
            var lastChild:int = -1;
            var xx:Number = 0;
            for (i = 0; i < len; i++)
            {
                // only draw the vertical separator for the ones in the middle (not beginning and not end)
                if (vLines && i < (len - 1))
                    drawVerticalSeparator(linesBody, i, lineCol, xx + visibleColumns[i].width);

                var col:DataGridColumn = visibleColumns[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";
                        contentHolder.addChildAt(colBGs, contentHolder.getChildIndex(contentHolder.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);
                    }
                }
                xx += visibleColumns[i].width;
            }
            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;
                }
            }

        }

        if (separators.right && visibleColumns && visibleColumns.length)
        {
            if (contentHolder.listItems.length && contentHolder.listItems[0].length)
                drawVerticalSeparator(linesBody, i++, 0, contentHolder.listItems[0][len - 1].x + visibleColumns[len - 1].width, true);
            else
            {
                xx = 0;
                for (i = 0; i < len; i++)
                {
                    xx += visibleColumns[i].width;
                }
                drawVerticalSeparator(linesBody, i++, 0, xx, true);
            }
        }
        if (separators.left)
            drawVerticalSeparator(linesBody, i++, 0, 0, true);

    }

    mx_internal function _drawHeaderBackground(headerBG:UIComponent):void
    {
        drawHeaderBackground(headerBG);
    }

    /**
     *  Draws the background of the headers into the given 
     *  UIComponent.  The graphics drawn may 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
    {
        DataGridHeader(headerBG.parent)._drawHeaderBackground(headerBG);
    }

    mx_internal function _clearSeparators():void
    {
        clearSeparators();
    }

    /**
     *  Removes column header separators that the user normally uses
     *  to resize columns.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    protected function clearSeparators():void
    {
        DataGridHeader(header)._clearSeparators();
        if (lockedColumnHeader)
            DataGridHeader(lockedColumnHeader)._clearSeparators();
    }

    mx_internal function _drawSeparators():void
    {
        drawSeparators();
    }

    /**
     *  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 and adds
     *  instances of the <code>headerSeparatorSkin</code> 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
    {
        DataGridHeader(header)._drawSeparators();
        if (lockedColumnHeader)
            DataGridHeader(lockedColumnHeader)._drawSeparators();
    }

    /**
     *  @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;
        sortIndex = -1;
        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;
            }
        }
    }

    mx_internal function _placeSortArrow():void
    {
        placeSortArrow();
    }

    /**
     *  Draws the sort arrow graphic on the column that is the current sort key.
     *  This implementation creates or reuses an instance of the skin specified
     *  by <code>sortArrowSkin</code> style property and places 
     *  it in the appropriate column header.  It
     *  also shrinks the size of the column header if the text in the header
     *  would be obscured by the sort arrow.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    protected function placeSortArrow():void
    {
        DataGridHeader(header)._placeSortArrow();
        if (lockedColumnHeader)
            DataGridHeader(lockedColumnHeader)._placeSortArrow();
    }

    /**
     *  @private
     */
    private function sortByColumn(index:int):void
    {
        var c:DataGridColumn = columns[index];
        var desc:Boolean = c.sortDescending;

        // do the sort if we're allowed to
        if (c.sortable)
        {
            var s:ISort = collection.sort;
            var f:ISortField;
            if (s)
            {
                s.compareFunction = null;
                // analyze the current sort to see what we've been given
                var sf:Array = s.fields;
                if (sf)
                {
                    for (var i:int = 0; i < sf.length; i++)
                    {

                        if (sf[i].name == c.dataField)
                        {
                            // we're part of the current sort
                            f = sf[i]
                            // flip the logic so desc is new desired order
                            desc = !f.descending;
                            break;
                        }
                    }
                }
            }
            else
                s = new Sort;

            if (!f)
                f = new SortField(c.dataField);


            c.sortDescending = desc;
            var dir:String = (desc) ? "DESC" : "ASC";
            sortDirection = dir;

            // set the grid's sortIndex
            lastSortIndex = sortIndex;
            sortIndex = index;
            sortColumn = c;

            // if you have a labelFunction you must supply a sortCompareFunction
            f.name = c.dataField;
            if (c.sortCompareFunction != null)
            {
                f.compareFunction = c.sortCompareFunction;
            }
            else
            {
                f.compareFunction = null;
            }
            f.descending = desc;
            s.fields = [f];
        }
        collection.sort = s;
        collection.refresh();

    }

    /**
     *  @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)
            return;

        if (!collection || collection.length == 0)
            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 = DataGridEventReason.OTHER;
            }
            else
            {
                reason = (!editedItemPosition || coord.rowIndex == editedItemPosition.rowIndex) ?
                         DataGridEventReason.NEW_COLUMN :
                         DataGridEventReason.NEW_ROW;
            }
            if (!endEdit(reason) && reason != DataGridEventReason.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)
        {
            for (var i:int = 0; i < displayableColumns.length; 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;
        }

        scrollToEditedItem(rowIndex, colIndex);

        // get the actual references for the column, row, and item
        var item:IListItemRenderer = actualContentHolder.listItems[actualRowIndex][actualColIndex];
        if (!item)
        {
            // assume that editing was cancelled
            commitEditedItemPosition(null);
            return;
        }
        if (!isItemEditable(item.data))
        {
            // 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:DataGridEvent =
            new DataGridEvent(DataGridEvent.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);
        }
    }

    // computes actualRowIndex, actualColIndex and actualContentHolder by
    // taking inputs for rowIndex, colIndex and scrolling to the right
    // place
    private function scrollToEditedItem(rowIndex:int, colIndex:int):void
    {
        actualContentHolder = listContent;
        var listItems:Array  = actualContentHolder.listItems;

        var lastRowIndex:int = verticalScrollPosition + listItems.length - 1 + lockedRowCount;
        var partialRow:int = (rowInfo[listItems.length - 1].y + rowInfo[listItems.length - 1].height > listContent.height) ? 1 : 0;

        // actual row/column is the offset into one of the containers
        if (rowIndex > lockedRowCount)
        {
            // not a locked editable row make sure it is on screen
            if (rowIndex < verticalScrollPosition + lockedRowCount)
                verticalScrollPosition = rowIndex - lockedRowCount;
            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 + lockedRowCount &&
                        partialRow))
                {
                    if (verticalScrollPosition == maxVerticalScrollPosition)
                        break;
                    verticalScrollPosition = Math.min(verticalScrollPosition + (rowIndex > lastRowIndex ? rowIndex - lastRowIndex : partialRow), maxVerticalScrollPosition);
                    lastRowIndex = verticalScrollPosition + listItems.length - 1 + lockedRowCount;
                    partialRow = (rowInfo[listItems.length - 1].y + rowInfo[listItems.length - 1].height > listContent.height) ? 1 : 0;
                }
            }

            actualRowIndex = rowIndex - verticalScrollPosition - lockedRowCount;

        }
        else
        {
            if (rowIndex == lockedRowCount)
            {
                verticalScrollPosition = 0;
                actualRowIndex = rowIndex - lockedRowCount;
            }
            else
            {
                if (lockedRowCount)
                    actualContentHolder = lockedRowContent;

                actualRowIndex = rowIndex;
            }
        }

        // reset since actualContentHolder could have changed
        listItems = actualContentHolder.listItems;

        var len:uint = (listItems && listItems[0]) ? listItems[0].length : visibleColumns.length;
        var lastColIndex:int = horizontalScrollPosition + len - 1 + lockedColumnCount;

        if (colIndex > lockedColumnCount)
        {
            var partialCol:int = (listItems[0][len - 1].x + listItems[0][len - 1].width > listContent.width) ? 1 : 0;
            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);
                    len = (listItems && listItems[0]) ? listItems[0].length : visibleColumns.length;
                    lastColIndex = horizontalScrollPosition + len - 1 + lockedColumnCount;
                    partialCol = (listItems[0][len - 1].x + listItems[0][len - 1].width > listContent.width) ? 1 : 0;
                }
            }
            actualColIndex = colIndex - horizontalScrollPosition - lockedColumnCount;
        }
        else
        {
            if (colIndex == lockedColumnCount)
            {
                horizontalScrollPosition = 0;
                actualColIndex = colIndex - lockedColumnCount;
            }
            else
            {
                if (lockedColumnCount)
                {
                    if (actualContentHolder == lockedRowContent)
                        actualContentHolder = lockedColumnAndRowContent;
                    else
                        actualContentHolder = lockedColumnContent;
                }

                actualColIndex = colIndex;
            }
        }
    }

    /**
     *  Creates the item editor for the item renderer at the
     *  <code>editedItemPosition</code> using the editor
     *  specified by the <code>itemEditor</code> property.
     *
     *  <p>This method sets the editor instance as the 
     *  <code>itemEditorInstance</code> property.</p>
     *
     *  <p>You may only call this method from within the event listener
     *  for the <code>itemEditBegin</code> event. 
     *  To create an editor at other times, set the
     *  <code>editedItemPosition</code> property to generate 
     *  the <code>itemEditBegin</code> event.</p>
     *
     *  @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)
        {
            for (var i:int = 0; i < displayableColumns.length; i++)
            {
                if (displayableColumns[i].colNum >= colIndex)
                {
                    colIndex = i;
                    break;
                }
            }
            if (i == displayableColumns.length)
                colIndex = 0;
        }

        var col:DataGridColumn = displayableColumns[colIndex];
        if (rowIndex >= lockedRowCount)
            rowIndex -= verticalScrollPosition + lockedRowCount;

        if (colIndex >= lockedColumnCount)
            colIndex -= horizontalScrollPosition + lockedColumnCount;

        var item:IListItemRenderer = actualContentHolder.listItems[rowIndex][colIndex];
        var rowData:ListRowInfo = actualContentHolder.rowInfo[rowIndex];

        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 == DataGridColumn.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;
                actualContentHolder.addChild(DisplayObject(itemEditorInstance));
            }
            actualContentHolder.setChildIndex(DisplayObject(itemEditorInstance), actualContentHolder.numChildren - 1);
            // give it the right size, look and placement
            itemEditorInstance.visible = true;
            itemEditorInstance.move(item.x + dx, rowData.y + dy);
            itemEditorInstance.setActualSize(
                            Math.min(col.width + dw, 
                                     actualContentHolder.width - 1 - itemEditorInstance.x),
                            Math.min(rowData.height + dh, 
                                     actualContentHolder.height - itemEditorInstance.y));
            DisplayObject(itemEditorInstance).addEventListener(FocusEvent.FOCUS_OUT, itemEditorFocusOutHandler);
            item.visible = false;

        }
        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);
        if (focusManager)
            focusManager.defaultButtonEnabled = false;
        // 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 or our grid is resized
        systemManager.addEventListener(Event.RESIZE, editorAncestorResizeHandler);
        addEventListener(Event.RESIZE, editorAncestorResizeHandler);

    // Dispatch our item editor created event
        var event:DataGridEvent =
                new DataGridEvent(DataGridEvent.ITEM_EDITOR_CREATE, false, true, colIndex, null, rowIndex);
        dispatchEvent(event);
    }

    /**
     *  @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;

        // 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;

        // 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(DataGridEventReason.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;
                }
            }
            // if we find a visible and editable column, move to it
            if (_columns[colIndex].editable && _columns[colIndex].visible)
            {
                found = true;
                // kill the old edit session
                var reason:String;
                reason = index == _editedItemPosition.rowIndex ?
                         DataGridEventReason.NEW_COLUMN :
                         DataGridEventReason.NEW_ROW;
                if (!itemEditorInstance || endEdit(reason))
                {
                    // send event to create the new one
                    beginningEdit(colIndex, index);
                }
            }
        }
        return found;
    }

    /**
     *  This method closes an item editor currently open on an item renderer. 
     *  You typically only call this method from within the event listener 
     *  for the <code>itemEditEnd</code> event, after
     *  you have already called the <code>preventDefault()</code> 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);
            if (focusManager)
                focusManager.defaultButtonEnabled = true;
            systemManager.getSandboxRoot().
                removeEventListener(MouseEvent.MOUSE_DOWN, editorMouseDownHandler, true);
            systemManager.getSandboxRoot().
                removeEventListener(SandboxMouseEvent.MOUSE_DOWN_SOMEWHERE, editorMouseDownHandler);
            systemManager.removeEventListener(Event.RESIZE, editorAncestorResizeHandler);
            removeEventListener(Event.RESIZE, editorAncestorResizeHandler);

            var event:DataGridEvent =
                new DataGridEvent(DataGridEvent.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();
                
                // defer focus can cause focusOutHandler to destroy the editor
                // and make itemEditorInstance null
                if (itemEditorInstance)
                {
                    // must call removeChild() so FocusManager.lastFocus becomes null
                    actualContentHolder.removeChild(DisplayObject(itemEditorInstance));
                    editedItemRenderer.visible = true;
                }
            }
            itemEditorInstance = null;
            _editedItemPosition = null;
        }
    }

    /**
     *  @private
     *  dispatch an ITEM_EDIT_BEGINNING event;
     */
    private function beginningEdit(columnIndex:int, rowIndex:int, itemRenderer:IListItemRenderer = null):void
    {
        var dataGridEvent:DataGridEvent =
            new DataGridEvent(DataGridEvent.ITEM_EDIT_BEGINNING, false, true);
            // ITEM_EDIT events are cancelable
        dataGridEvent.columnIndex = Math.min(columnIndex, columns.length - 1);
        dataGridEvent.dataField = _columns[columnIndex].dataField;
        dataGridEvent.rowIndex = Math.min(rowIndex, collection.length - 1);
        dataGridEvent.itemRenderer = itemRenderer;
        if (!dispatchEvent(dataGridEvent))
            lastEditedItemPosition = { columnIndex: columnIndex, rowIndex: rowIndex };

    }

    /**
     *  @private
     *  When the user finished editing an item, this method is called.
     *  It dispatches the DataGridEvent.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.
     */
    private function endEdit(reason:String):Boolean
    {
        // this happens if the renderer is removed asynchronously ususally with FDS
        if (!editedItemRenderer)
            return true;

        inEndEdit = true;

        var dataGridEvent:DataGridEvent =
            new DataGridEvent(DataGridEvent.ITEM_EDIT_END, false, true);
            // ITEM_EDIT events are cancelable
        dataGridEvent.columnIndex = editedItemPosition.columnIndex;
        dataGridEvent.dataField = _columns[editedItemPosition.columnIndex].dataField;
        dataGridEvent.rowIndex = editedItemPosition.rowIndex;
        dataGridEvent.itemRenderer = editedItemRenderer;
        dataGridEvent.reason = reason;
        dispatchEvent(dataGridEvent);
        // 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 == DataGridEventReason.CANCELLED)
            deferFocus();

        inEndEdit = false;

        return !(dataGridEvent.isDefaultPrevented())
    }

    /**
     *  Determines whether to allow editing of a dataprovider item on a per-row basis. 
     *  The default implementation of this method only checks the <code>editable</code> property 
     *  of the DataGrid and returns <code>false</code> if <code>editable</code> is <code>false</code>
     *  or if the dataprovider item is <code>null</code>.
     * 
     *  <p>This method can be overridden to allow fine-grained control of which dataprovider items 
     *  are editable. For example, if you want to disallow editing of grouping rows or summary rows
     *  you would override this method with custom logic to this behavior.</p>
     *
     *  @param data The data provider item. The default implementation of this method returns 
     *  <code>false</code> if the data object is <code>null</code>.
     * 
     *  @return The default behavior is to return <code>true</code> if the DataGrid's <code>editable</code> property is 
     *  <code>true</code> and the data object is not <code>null</code>.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public function isItemEditable(data:Object):Boolean
    {
        if (!editable)
            return false;

        if (!data)
            return false;

        return true;
    }

    /**
     *  @private
     */
    override mx_internal function columnRendererChanged(c:DataGridColumn):void
    {
        var item:DisplayObject;
        
        var factories:Dictionary = c.measuringObjects;
        if (factories)
        {
            for (var p:* in factories)
            {
                var factory:IFactory = IFactory(p);
                item = c.measuringObjects[factory];
                if (item)
                {
                    item.parent.removeChild(item);
                    c.measuringObjects[factory] = null;
                }

                if (c.freeItemRenderersByFactory && c.freeItemRenderersByFactory[factory])
                {
                    var d:Dictionary = c.freeItemRenderersByFactory[factory];
                    for (var q:* in d)
                    {
                        item = DisplayObject(q);
                        item.parent.removeChild(item);
                    }
                    c.freeItemRenderersByFactory[factory] = new Dictionary(true);
                }
            }
            var freeRenderers:Array = freeItemRenderersTable[c] as Array;
            if (freeRenderers)
            {
                while (freeRenderers.length)
                {
                    item = freeRenderers.pop();
                }
            }
        }

        item = c.cachedHeaderRenderer as DisplayObject;
        if (item && item.parent)
            item.parent.removeChild(item);
        c.cachedHeaderRenderer = null;

        rendererChanged = true;
        invalidateDisplayList();
    }

    //--------------------------------------------------------------------------
    //
    //  Overridden event handlers
    //
    //--------------------------------------------------------------------------

    /**
     *  @private
     *  Catches any events from the model. Optimized for editing one item.
     *  Creates columns when there are none. Inherited from list.
     *  @param eventObj
     */
    override protected function collectionChangeHandler(event:Event):void
    {
        if (event is CollectionEvent)
        {
            var curEditedItemPosition:Object;
            var ceEvent:CollectionEvent = CollectionEvent(event)
            if (ceEvent.kind == CollectionEventKind.RESET)
            {
                if (itemEditorInstance)
                    endEdit(DataGridEventReason.CANCELLED);
                setEditedItemPosition(null); // nothing left to edit
                if (generatedColumns)
                    generateCols();
                else
                    columnsInvalid = true;
                updateSortIndexAndDirection();
                if (lockedRowContent)
                    lockedRowContent.iterator = collection.createCursor();
                if (lockedColumnAndRowContent)
                    lockedColumnAndRowContent.iterator = collection.createCursor();
            }
            else if (ceEvent.kind == CollectionEventKind.REFRESH && !manualSort)
                updateSortIndexAndDirection();
            else
            {
                // if we get a add while editing adjust the editPosition
                if (ceEvent.kind == CollectionEventKind.ADD)
                {
                    if (editedItemPosition)
                    {
                        if (ceEvent.location <= editedItemPosition.rowIndex)
                        {
                            curEditedItemPosition = editedItemPosition;

                            if (inEndEdit)
                                _editedItemPosition = { columnIndex : editedItemPosition.columnIndex, 
                                                    rowIndex : Math.max(0, editedItemPosition.rowIndex + ceEvent.items.length)};
                            else if (itemEditorInstance)
                            {
                                _editedItemPosition = { columnIndex : editedItemPosition.columnIndex, 
                                                        rowIndex : Math.max(0, editedItemPosition.rowIndex + ceEvent.items.length)};
                                itemEditorPositionChanged = true;
                                lastEditedItemPosition = _editedItemPosition;
                            }
                            else
                                setEditedItemPosition({ columnIndex : curEditedItemPosition.columnIndex, 
                                                    rowIndex : Math.max(0, curEditedItemPosition.rowIndex + ceEvent.items.length)});
                        }
                    }
                }
                // if we get a remove while editing adjust the editPosition
                else if (ceEvent.kind == CollectionEventKind.REMOVE)
                {
                    if (editedItemPosition)
                    {
                        if (collection.length == 0)
                        {
                            if (itemEditorInstance)
                                endEdit(DataGridEventReason.CANCELLED);
                            setEditedItemPosition(null); // nothing left to edit
                        }
                        else if (ceEvent.location <= editedItemPosition.rowIndex)
                        {
                            curEditedItemPosition = editedItemPosition;

                            // if the editor is up on the item going away, cancel the session
                            if (ceEvent.location == editedItemPosition.rowIndex && itemEditorInstance)
                                endEdit(DataGridEventReason.CANCELLED);

                            if (inEndEdit)
                                _editedItemPosition = { columnIndex : editedItemPosition.columnIndex, 
                                                    rowIndex : Math.max(0, editedItemPosition.rowIndex - ceEvent.items.length)};
                            else if (itemEditorInstance)
                            {
                                _editedItemPosition = { columnIndex : editedItemPosition.columnIndex, 
                                                        rowIndex : Math.max(0, editedItemPosition.rowIndex - ceEvent.items.length)};
                                itemEditorPositionChanged = true;
                                lastEditedItemPosition = _editedItemPosition;
                            }
                            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(DataGridEventReason.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();
            }
            else if (ce.kind == CollectionEventKind.REFRESH)
            {
                // refresh locked row count iterator
                if (lockedRowCount && lockedRowContent)
                    lockedRowContent.iterator.seek(CursorBookmark.FIRST, 0);
            }
        }

            // We must make sure we correctly recompute our rowHeight if 
            // we've yet to accurately measure our data, queue up a visit
            // to commitProperties.
            if (itemsNeedMeasurement)
                invalidateProperties();

//      if (event.eventName != "sort" && bRowsChanged)
//          invInitHeaders = true;
    }

    /**
     *  @private
     */
    override protected function mouseDownHandler(event:MouseEvent):void
    {
        // trace(">>mouseDownHandler");
        var r:IListItemRenderer;
        var s:Sprite;

        r = mouseEventToItemRenderer(event);

        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)
            {
                lastItemDown = r;

                pos = itemRendererToIndices(r);

                var bEndedEdit:Boolean = true;

                if (itemEditorInstance)
                {
                    if (displayableColumns[pos.x].editable == false)
                    {
                        bEndedEdit = endEdit(DataGridEventReason.OTHER);
                    }
                    else
                    {
                        //Possible to get here and not have an editedItemPosition
                        if (editedItemPosition)
                        {
                            bEndedEdit = endEdit(editedItemPosition.rowIndex == pos.y ?
                                         DataGridEventReason.NEW_COLUMN :
                                         DataGridEventReason.NEW_ROW);
                        }
                        else
                        {
                            bEndedEdit = false;
                        }
                    }
                }

                // if we didn't end edit session, don't do default behavior (call super)
                if (!bEndedEdit)
                    return;
            }
            else
            {
                // trace("end edit?");
                if (itemEditorInstance)
                    endEdit(DataGridEventReason.OTHER);
            }

            super.mouseDownHandler(event);

            if (r)
            {
                if (displayableColumns[pos.x].rendererIsEditor)
                    resetDragScrolling();
            }
        }
        else
            resetDragScrolling();
        // trace("<<mouseDownHandler");
    }

    /**
     *  @private
     */
    override protected function mouseUpHandler(event:MouseEvent):void
    {
        var dataGridEvent:DataGridEvent;
        var r:IListItemRenderer;
        var s:Sprite;
        var n:int;
        var i:int;
        var pos:Point;

        r = mouseEventToItemRenderer(event);

        super.mouseUpHandler(event);

        if (r && r != itemEditorInstance && 
                    (lastItemDown == r || itemRendererContains(lastItemDown, lastItemFocused)))
        {
            // if lastItemDown != r, we clicked in one cell and dragged to another.
            // if lastItemFocused is in that cell, then we give lastItemDown the
            // edit session
            if (lastItemDown != r)
                r = lastItemDown;

            lastItemFocused = null;
            pos = itemRendererToIndices(r);

            if (pos && pos.y >= 0 && editable && !dontEdit)
            {
                if (displayableColumns[pos.x].editable)
                {
                    beginningEdit(displayableColumns[pos.x].colNum, pos.y, r);
                }
                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)
                {
                    beginningEdit(displayableColumns[pos.x].colNum, pos.y, lastItemDown);
                }
                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 };
            }
        }

        lastItemDown = null;
    }

    /**
     *  @private
     *  when the grid gets focus, focus an item renderer
     */
    override protected function focusInHandler(event:FocusEvent):void
    {
        //trace(">>DGFocusIn ", selectedIndex);
        var dataGridEvent:DataGridEvent;

        if (losingFocus)
        {
            losingFocus = false;
             //trace("losing focus via tab");
             //trace("<<DGFocusIn ");
            return;
        }

        if (editable)
        {
            addEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler);
            addEventListener(MouseEvent.MOUSE_DOWN, mouseFocusChangeHandler);
        }

        if (event.target != this)
        {
            if (itemEditorInstance && itemRendererContains(itemEditorInstance, DisplayObject(event.target)))
            {
                 //trace("item editor got focus ignoring");
                 //trace("<<DGFocusIn ");
                return;
            }
            lastItemFocused = DisplayObject(event.target);
             //trace("subcomponent got focus ignoring");
             //trace("<<DGFocusIn ");
            return;
        }
        lastItemFocused = null;

        super.focusInHandler(event);

        // isPressed is not correct if we switch in from a different window
        // via mouseDown.  The activation calls focusIn (if we had focus
        // when we lost activation) before mouseDown
        if (editable && !isPressed) // don't do this if we're mouse focused
        {
            _editedItemPosition = lastEditedItemPosition;

            var foundOne:Boolean = false;

            // start somewhere
            if (!_editedItemPosition)
                _editedItemPosition = { rowIndex: 0, columnIndex: 0 };
    

            for (;
                 _editedItemPosition.columnIndex != _columns.length;
                 _editedItemPosition.columnIndex++)
            {
                // If the editedItemPosition is valid, focus it,
                // otherwise find one.
                if (_columns[_editedItemPosition.columnIndex].editable &&
                    _columns[_editedItemPosition.columnIndex].visible)
                {
                    foundOne = true;
                    break;
                }
            }

            if (foundOne)
            {
                //trace("setting focus", _editedItemPosition.columnIndex, _editedItemPosition.rowIndex);
                beginningEdit(_editedItemPosition.columnIndex, _editedItemPosition.rowIndex);
            }

        }

         //trace("<<DGFocusIn ");
    }

    /**
     *  @private
     *  when the grid loses focus, close the editor
     */
    override protected function focusOutHandler(event:FocusEvent):void
    {
        // 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)))
        {
            // find renderer for target
            var target:DisplayObject = DisplayObject(event.relatedObject);
            while (target && target != this)
            {
                if (target is IListItemRenderer && target.parent.parent == this && target.parent is ListBaseContentHolder)
                {
                    if (target.visible)
                    {
                        // losing focus to another renderer
                        // let other logic end the session
                        return;
                    }
                }

                if (target is IUIComponent)
                    target = IUIComponent(target).owner;
                else 
                    target = target.parent;
            }

            // trace("call endEdit from focus out");
            endEdit(DataGridEventReason.OTHER);
            removeEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler);
            removeEventListener(MouseEvent.MOUSE_DOWN, mouseFocusChangeHandler);
        }
        // trace("<<DGFocusOut " + itemEditorInstance + " " + event.relatedObject);
    }

    /**
     *  @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(DataGridEventReason.OTHER);
            deferFocus();
        }
    }

    /**
     *  @private
     */
    override protected function keyDownHandler(event:KeyboardEvent):void
    {
        if (itemEditorInstance || event.target != event.currentTarget)
            return;

        if (event.keyCode != Keyboard.SPACE)
            super.keyDownHandler(event);
        else if (caretIndex != -1)
        {
            moveSelectionVertically(event.keyCode, event.shiftKey, event.ctrlKey);
        }
    }

    /**
     *  @private
     *  used by ListBase.findString.  Shouldn't be used elsewhere
     *  because column's itemToLabel is preferred
     */
    override public function itemToLabel(data:Object):String
    {
        return _columns[sortIndex == -1 ? 0 : sortIndex].itemToLabel(data);
    }

    //--------------------------------------------------------------------------
    //
    //  Event handlers
    //
    //--------------------------------------------------------------------------


    /**
     *  @private
     */
    private function editorMouseDownHandler(event:Event):void
    {
        if (event is MouseEvent && owns(DisplayObject(event.target)))
            return;

        endEdit(DataGridEventReason.OTHER);
        // set focus back to the grid so grid logic will deal if focus doesn't
        // end up somewhere else
        deferFocus();
    }

    /**
     *  @private
     */
    private function editorKeyDownHandler(event:KeyboardEvent):void
    {
        // ESC just kills the editor, no new data
        if (event.keyCode == Keyboard.ESCAPE)
        {
            endEdit(DataGridEventReason.CANCELLED);
        }
        else if (event.ctrlKey && event.charCode == 46)
        {   // Check for Ctrl-.
            endEdit(DataGridEventReason.CANCELLED);
        }
        else if (event.charCode == Keyboard.ENTER && event.keyCode != 229)
        {
            // multiline editors can take the enter key.
            if (!_editedItemPosition)
                return;

            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(DataGridEventReason.NEW_ROW) && !dontEdit)
            {
                findNextEnterItemRenderer(event);
                if (focusManager)
                    focusManager.defaultButtonEnabled = false;
            }
        }
    }

    /**
     *  @private
     */
    private function editorAncestorResizeHandler(event:Event):void
    {
        endEdit(DataGridEventReason.OTHER);
    }

    /**
     *  @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;

        // though endEdit checks this, an ITEM_EDIT_END event handler can, in
        // the meantime, wipe out the item editor (e.g. by setting a new data
        // provider). so we add another check here.
        if (!_editedItemPosition)
            return;

        var rowIndex:int = _editedItemPosition.rowIndex;
        var columnIndex:int = _editedItemPosition.columnIndex;
        // modify direction with SHIFT (up or down)
        var newIndex:int = _editedItemPosition.rowIndex +
                           (event.shiftKey ? -1 : 1);
        // only move if we're within range
        if (newIndex < collection.length && newIndex >= 0)
            rowIndex = newIndex;

        // send event to create the new one
        beginningEdit(columnIndex, rowIndex);
    }

    /**
     *  @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(DataGridEventReason.OTHER);
    }

    /**
     *  @private
     */
    private function itemEditorItemEditBeginningHandler(event:DataGridEvent):void
    {
        // trace("itemEditorItemEditBeginningHandler");
        if (!event.isDefaultPrevented())
            setEditedItemPosition({columnIndex: event.columnIndex, rowIndex: event.rowIndex});
        else if (!itemEditorInstance)
        {
            _editedItemPosition = null;
            // return focus to the grid w/o selecting an item
            editable = false;
            setFocus();
            editable = true;
        }
    }

    /**
     *  @private
     *  focus an item renderer in the grid - harder than it looks
     */
    private function itemEditorItemEditBeginHandler(event:DataGridEvent):void
    {
        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() && actualContentHolder.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));
                
            var event:DataGridEvent =
                new DataGridEvent(DataGridEvent.ITEM_FOCUS_IN);
            event.columnIndex = _editedItemPosition.columnIndex;
            event.rowIndex = _editedItemPosition.rowIndex;
                event.itemRenderer = itemEditorInstance;
            dispatchEvent(event);
        }
    }

    /**
     *  @private
     */
    private function itemEditorItemEditEndHandler(event:DataGridEvent):void
    {
        if (!event.isDefaultPrevented())
        {
            var bChanged:Boolean = false;

            if (event.reason == DataGridEventReason.NEW_COLUMN)
            {
                if (!collectionUpdatesDisabled)
                {
                    collection.disableAutoUpdate();
                    collectionUpdatesDisabled = true;
                }
            }
            else
            {
                if (collectionUpdatesDisabled)
                {
                    collection.enableAutoUpdate();
                    collectionUpdatesDisabled = false;
                }
            }

            if (itemEditorInstance && event.reason != DataGridEventReason.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);
                }
                /** Old code assumed that the property would be a simply name that could be dereferenced
                  * through array notation. Using a method call here provides, minimally, an override
                  * point where developers could extend this functionality in their own datagrid subclass **/
                if (property != null && getCurrentDataValue( data, property ) !== newData)
                {
                    bChanged = setNewValue( data, property, newData, event.columnIndex );
                }
                if (bChanged && !(data is IPropertyChangeNotifier || data is XML))
                {
                    collection.itemUpdated(data, property);
                }
                if (event.itemRenderer is IDropInListItemRenderer)
                {
                    var listData:DataGridListData = DataGridListData(IDropInListItemRenderer(event.itemRenderer).listData);
                    listData.label = _columns[event.columnIndex].itemToLabel(data);
                    IDropInListItemRenderer(event.itemRenderer).listData = listData;
                }
                event.itemRenderer.data = data;
            }
        }
        else
        {
            if (event.reason != DataGridEventReason.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 == DataGridEventReason.OTHER || !event.isDefaultPrevented())
        {
            destroyItemEditor();
        }
    }

    /**
     *  @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
     */ 
    protected function isComplexColumn( property:String ):Boolean
    {
        return ( property.indexOf( "." ) != -1 );
    }

    //Gets the reference to the parent object where a property will be updated
    /**
     *  @private
     */ 
    protected function deriveComplexFieldReference( data:Object, complexFieldNameComponents:Array ):Object
    {
        var currentRef:Object = data;
        if ( complexFieldNameComponents ) 
        {
            for ( var i:int=0; i<complexFieldNameComponents.length; i++ ) 
                currentRef = currentRef[ complexFieldNameComponents[ i ] ];
        }
        
        return currentRef;      
    }

    //default implementations of these two methods, intended for subclassing
    //not checking if it really is a complex value here as the performance hit of doing this here is negligible
    //compared with every display
    /**
     *  @private
     */ 
    protected function getCurrentDataValue( data:Object, property:String ):String
    {
        if ( !isComplexColumn( property ) )
            return data[ property ];
        
        var complexFieldNameComponents:Array = property.split( "." );
        var obj:Object = deriveComplexFieldReference( data, complexFieldNameComponents );

        return String( obj );
    }

    //Passing all of these parameters as it basically allows everything you would need to subclass for all sorts of fun implementations
    /**
     *  @private
     */ 
    protected function setNewValue( data:Object, property:String, value:Object, columnIndex:int ):Boolean 
    {
        if ( !isComplexColumn( property ) )
        {
            data[ property ] = value;
        } 
        else 
        {
            var complexFieldNameComponents:Array = property.split( "." );
            var lastProp:String = complexFieldNameComponents.pop();
            var parent:Object = deriveComplexFieldReference( data, complexFieldNameComponents );
            parent[ lastProp ] = value;
        }
        
        //The value they typed in is always converted to a string, but is the value actually a string in the dataprovider?
        //unknown as it is cast by datagridcolumn before datagrid ever gets to know...
        //control if this really causes an update in subclass
        return true;
    }

    /**
     *  @private
     */
    private function headerReleaseHandler(event:DataGridEvent):void
    {
        if (!event.isDefaultPrevented())
        {
            manualSort = true;
            sortByColumn(event.columnIndex);
            manualSort = false;
        }
    }

    /**
     *  @private
     */
    override protected function mouseWheelHandler(event:MouseEvent):void
    {
        if (itemEditorInstance)
            endEdit(DataGridEventReason.OTHER);

        super.mouseWheelHandler(event);
    }

    /**
     *  @private
     *  if some drags from the same row as an editor we can be left
     *  with updates disabled
     */
    override protected function dragStartHandler(event:DragEvent):void
    {
        if (collectionUpdatesDisabled)
        {
            collection.enableAutoUpdate();
            collectionUpdatesDisabled = false;
        }
        super.dragStartHandler(event);
    }

    mx_internal function get vScrollBar():ScrollBar
    {
        return verticalScrollBar;
    }

    /** 
     *  diagnostics
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    override mx_internal function get rendererArray():Array 
    {
        var arr:Array = listItems.slice();
        var arr2:Array = DataGridHeader(header).rendererArray;
        arr.unshift(arr2);
        return arr;
    }

    /** 
     *  diagnostics
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    mx_internal function get sortArrow():IFlexDisplayObject 
    {
        return DataGridHeader(header).sortArrow;
    }

    /**
     *  Called from the <code>updateDisplayList()</code> method to adjust the size and position of
     *  listContent.
     *  
     *  @param unscaledWidth The width of the listContent. This value ignores changes to the width from external components or settings.
     *  @param unscaledHeight The height of the listContent. This value ignores changes to the height from external components or settings.
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    override protected function adjustListContent(unscaledWidth:Number = -1,
                                       unscaledHeight:Number = -1):void
    {
        var ww:Number;
        var hh:Number = 0;
        var lcx:Number;
        var lcy:Number;
        var hcx:Number;

        if (headerVisible)
        {
            if (lockedColumnCount > 0)
            {
                lockedColumnHeader.visible = true;
                hcx = viewMetrics.left + Math.min(DataGridHeader(lockedColumnHeader).leftOffset, 0);
                lockedColumnHeader.move(hcx, viewMetrics.top);
                hh = lockedColumnHeader.getExplicitOrMeasuredHeight();
                lockedColumnHeader.setActualSize(lockedColumnWidth + 1, hh);
                DataGridHeader(lockedColumnHeader).needRightSeparator = true;
                DataGridHeader(lockedColumnHeader).needRightSeparatorEvents = true;
            }
            header.visible = true;
            hcx = viewMetrics.left + lockedColumnWidth + Math.min(DataGridHeader(header).leftOffset, 0);
            header.move(hcx, viewMetrics.top);
            // If we have a vScroll only, we want the scrollbar to be below
            // the header.
            if (verticalScrollBar != null && verticalScrollBar.visible &&
               (horizontalScrollBar == null || !horizontalScrollBar.visible) && headerVisible &&
               roomForScrollBar(verticalScrollBar, unscaledWidth, unscaledHeight-header.height))
                ww = Math.max(0, DataGridHeader(header).rightOffset) - hcx - borderMetrics.right;
            else
                ww = Math.max(0, DataGridHeader(header).rightOffset) - hcx - viewMetrics.right;
            hh = header.getExplicitOrMeasuredHeight();
            header.setActualSize(unscaledWidth + ww, hh);
            if (!skipHeaderUpdate)
            {
                header.headerItemsChanged = true;
                header.invalidateDisplayList(); // make sure it redraws, even if size didn't change
                // internal renderers could have changed
            }
        }
        else
        {
            header.visible = false;
            if (lockedColumnCount > 0)
                lockedColumnHeader.visible = false;
        }

        if (lockedRowCount > 0 && lockedRowContent && lockedRowContent.iterator)
        {
            try
            {
                lockedRowContent.iterator.seek(CursorBookmark.FIRST);
                var pt:Point = makeRows(lockedRowContent, 0, 0, unscaledWidth, unscaledHeight, 0, 0, true, lockedRowCount, true);

                if (lockedColumnCount > 0)
                {
                    lcx = viewMetrics.left + Math.min(lockedColumnAndRowContent.leftOffset, 0);
                    lcy = viewMetrics.top + Math.min(lockedColumnAndRowContent.topOffset, 0) + Math.ceil(hh);
                    lockedColumnAndRowContent.move(lcx, lcy);
                    lockedColumnAndRowContent.setActualSize(lockedColumnWidth, lockedColumnAndRowContent.getExplicitOrMeasuredHeight());
                }
                lcx = viewMetrics.left + lockedColumnWidth + Math.min(lockedRowContent.leftOffset, 0);
                lcy = viewMetrics.top + Math.min(lockedRowContent.topOffset, 0) + Math.ceil(hh);
                lockedRowContent.move(lcx, lcy);
                ww = Math.max(0, lockedRowContent.rightOffset) - lcx - viewMetrics.right;
                lockedRowContent.setActualSize(unscaledWidth + ww, lockedRowContent.getExplicitOrMeasuredHeight());
                hh += lockedRowContent.getExplicitOrMeasuredHeight();
            }
            catch (e:ItemPendingError)
            {
                e.addResponder(new ItemResponder(lockedRowSeekPendingResultHandler, seekPendingFailureHandler,
                                                    null));

            }
        }

        if (lockedColumnCount > 0)
        {
            lcx = viewMetrics.left + Math.min(lockedColumnContent.leftOffset, 0);
            lcy = viewMetrics.top + Math.min(lockedColumnContent.topOffset, 0) + Math.ceil(hh);
            lockedColumnContent.move(lcx, lcy);
            ww = lockedColumnWidth + lockedColumnContent.rightOffset - lockedColumnContent.leftOffset;
            lockedColumnContent.setActualSize(ww, 
                    unscaledHeight + Math.max(0, lockedColumnContent.bottomOffset) - lcy - viewMetrics.bottom);
        }
        lcx = viewMetrics.left + lockedColumnWidth + Math.min(listContent.leftOffset, 0);
        lcy = viewMetrics.top + Math.min(listContent.topOffset, 0) + Math.ceil(hh);
        listContent.move(lcx, lcy);
        ww = Math.max(0, listContent.rightOffset) - lcx - viewMetrics.right;
        hh = Math.max(0, listContent.bottomOffset) - lcy - viewMetrics.bottom;
        listContent.setActualSize(Math.max(0, unscaledWidth + ww), Math.max(0, unscaledHeight + hh));

    }

    private function lockedRowSeekPendingResultHandler(data:Object,
                                                info:ListBaseSeekPending):void
    {
        try 
        {
            lockedRowContent.iterator.seek(CursorBookmark.FIRST);
        }
        catch(e:ItemPendingError)
        {
            e.addResponder(new ItemResponder(lockedRowSeekPendingResultHandler, seekPendingFailureHandler,
                                                null));
        }
        itemsSizeChanged = true;
        invalidateDisplayList();
    }


    /**
     *  @inheritDoc
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    override protected function scrollPositionToIndex(horizontalScrollPosition:int,
                                             verticalScrollPosition:int):int
    {
        return iterator ? verticalScrollPosition + lockedRowCount : -1;
    }

    /**
     *  @inheritDoc
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    override protected function scrollVertically(pos:int, deltaPos:int,
                                        scrollUp:Boolean):void
    {
        super.scrollVertically(pos, deltaPos, scrollUp);
        if (getStyle("horizontalGridLines"))
        {
            drawLinesAndColumnGraphics(listContent, visibleColumns, {});
            if (lockedColumnCount)
            {
                drawLinesAndColumnGraphics(lockedColumnContent, visibleLockedColumns, { right: 1})
            }
        }
        else
        {
            var lines:Sprite = Sprite(listContent.getChildByName("lines"));
            if (lines)
                listContent.setChildIndex(lines, listContent.numChildren - 1);
            if (lockedColumnCount)
            {
                lines = Sprite(lockedColumnContent.getChildByName("lines"));
                if (lines)
                    lockedColumnContent.setChildIndex(lines, lockedColumnContent.numChildren - 1);
            }
        }
    }

    private var _focusPane:Sprite;

    /**
     *  @private
     */
    override public function set focusPane(value:Sprite):void
    {
        super.focusPane = value;
        if (!value && _focusPane)
            _focusPane.mask = null;
        _focusPane = value;
    }

    mx_internal var lockedColumnDropIndicator:IFlexDisplayObject;

    /**
     *  @private
     */
    override public function showDropFeedback(event:DragEvent):void
    {
        super.showDropFeedback(event);

        if (lockedColumnCount > 0)
        {
            if (!lockedColumnDropIndicator)
            {
                var dropIndicatorClass:Class = getStyle("dropIndicatorSkin");
                if (!dropIndicatorClass)
                    dropIndicatorClass = ListDropIndicator;
                lockedColumnDropIndicator = IFlexDisplayObject(new dropIndicatorClass());

                lockedColumnDropIndicator.x = 2;
                lockedColumnDropIndicator.setActualSize(lockedColumnContent.width - 2, 4);
                lockedColumnDropIndicator.visible = true;
            }
            if (dropIndicator.parent == listContent)
                lockedColumnContent.addChild(DisplayObject(lockedColumnDropIndicator));
            else
                lockedColumnAndRowContent.addChild(DisplayObject(lockedColumnDropIndicator));


            lockedColumnDropIndicator.y = dropIndicator.y;
        }
    }

    /**
     *  @private
     */
    override public function hideDropFeedback(event:DragEvent):void
    {
        super.hideDropFeedback(event);

        if (lockedColumnDropIndicator)
        {
            DisplayObject(lockedColumnDropIndicator).parent.removeChild(DisplayObject(lockedColumnDropIndicator));
            lockedColumnDropIndicator = null;
        }
    }

    /**
     *  @private
     */
    override public function regenerateStyleCache(recursive:Boolean):void
    {
        super.regenerateStyleCache(recursive);
        fontContextChanged = true;
        invalidateProperties();
    }
}

}
