1////////////////////////////////////////////////////////////////////////////////
2//
3//  ADOBE SYSTEMS INCORPORATED
4//  Copyright 2003-2007 Adobe Systems Incorporated
5//  All Rights Reserved.
6//
7//  NOTICE: Adobe permits you to use, modify, and distribute this file
8//  in accordance with the terms of the license agreement accompanying it.
9//
10////////////////////////////////////////////////////////////////////////////////
11
12package mx.controls
13{
14
15import flash.display.DisplayObject;
16import flash.display.DisplayObjectContainer;
17import flash.display.Graphics;
18import flash.display.Shape;
19import flash.display.Sprite;
20import flash.events.Event;
21import flash.events.FocusEvent;
22import flash.events.KeyboardEvent;
23import flash.events.MouseEvent;
24import flash.geom.Point;
25import flash.ui.Keyboard;
26import flash.utils.Dictionary;
27import flash.utils.describeType;
28
29import mx.collections.CursorBookmark;
30import mx.collections.ICollectionView;
31import mx.collections.ItemResponder;
32import mx.collections.Sort;
33import mx.collections.SortField;
34import mx.collections.errors.ItemPendingError;
35import mx.controls.dataGridClasses.DataGridBase;
36import mx.controls.dataGridClasses.DataGridColumn;
37import mx.controls.dataGridClasses.DataGridDragProxy;
38import mx.controls.dataGridClasses.DataGridHeader;
39import mx.controls.dataGridClasses.DataGridItemRenderer;
40import mx.controls.dataGridClasses.DataGridListData;
41import mx.controls.listClasses.IDropInListItemRenderer;
42import mx.controls.listClasses.IListItemRenderer;
43import mx.controls.listClasses.ListBaseContentHolder;
44import mx.controls.listClasses.ListBaseSeekPending;
45import mx.controls.listClasses.ListRowInfo;
46import mx.controls.scrollClasses.ScrollBar;
47import mx.core.ContextualClassFactory;
48import mx.core.EdgeMetrics;
49import mx.core.EventPriority;
50import mx.core.FlexShape;
51import mx.core.FlexSprite;
52import mx.core.FlexVersion;
53import mx.core.IFactory;
54import mx.core.IFlexDisplayObject;
55import mx.core.IFlexModuleFactory;
56import mx.core.IIMESupport;
57import mx.core.IInvalidating;
58import mx.core.IPropertyChangeNotifier;
59import mx.core.IRectangularBorder;
60import mx.core.IUIComponent;
61import mx.core.ScrollPolicy;
62import mx.core.UIComponent;
63import mx.core.UIComponentGlobals;
64import mx.core.mx_internal;
65import mx.events.CollectionEvent;
66import mx.events.CollectionEventKind;
67import mx.events.DataGridEvent;
68import mx.events.DataGridEventReason;
69import mx.events.DragEvent;
70import mx.events.IndexChangedEvent;
71import mx.events.ListEvent;
72import mx.events.SandboxMouseEvent;
73import mx.events.ScrollEvent;
74import mx.events.ScrollEventDetail;
75import mx.managers.IFocusManager;
76import mx.managers.IFocusManagerComponent;
77import mx.skins.halo.ListDropIndicator;
78import mx.styles.ISimpleStyleClient;
79import mx.styles.StyleManager;
80import mx.utils.ObjectUtil;
81import mx.utils.StringUtil;
82
83use namespace mx_internal;
84
85//--------------------------------------
86//  Events
87//--------------------------------------
88
89/**
90 *  Dispatched when the user releases the mouse button while over an item
91 *  renderer, tabs to the DataGrid control or within the DataGrid control,
92 *  or in any other way attempts to edit an item.
93 *
94 *  @eventType mx.events.DataGridEvent.ITEM_EDIT_BEGINNING
95 */
96[Event(name="itemEditBeginning", type="mx.events.DataGridEvent")]
97
98/**
99 *  Dispatched when the <code>editedItemPosition</code> property has been set
100 *  and the item can be edited.
101 *
102 *  @eventType mx.events.DataGridEvent.ITEM_EDIT_BEGIN
103 */
104[Event(name="itemEditBegin", type="mx.events.DataGridEvent")]
105
106/**
107 *  Dispatched when the item editor has just been instantiated.
108 *
109 *  @eventType mx.events.DataGridEvent.ITEM_EDITOR_CREATE
110 */
111[Event(name="itemEditorCreate", type="mx.events.DataGridEvent")]
112
113/**
114 *  Dispatched when an item editing session ends for any reason.
115 *
116 *  @eventType mx.events.DataGridEvent.ITEM_EDIT_END
117 */
118[Event(name="itemEditEnd", type="mx.events.DataGridEvent")]
119
120/**
121 *  Dispatched when an item renderer gets focus, which can occur if the user
122 *  clicks on an item in the DataGrid control or navigates to the item using
123 *  a keyboard.  Only dispatched if the item is editable.
124 *
125 *  @eventType mx.events.DataGridEvent.ITEM_FOCUS_IN
126 */
127[Event(name="itemFocusIn", type="mx.events.DataGridEvent")]
128
129/**
130 *  Dispatched when an item renderer loses focus, which can occur if the user
131 *  clicks another item in the DataGrid control or clicks outside the control,
132 *  or uses the keyboard to navigate to another item in the DataGrid control
133 *  or outside the control.
134 *  Only dispatched if the item is editable.
135 *
136 *  @eventType mx.events.DataGridEvent.ITEM_FOCUS_OUT
137 */
138[Event(name="itemFocusOut", type="mx.events.DataGridEvent")]
139
140/**
141 *  Dispatched when a user changes the width of a column, indicating that the
142 *  amount of data displayed in that column may have changed.
143 *  If <code>horizontalScrollPolicy</code> is <code>"off"</code>, other
144 *  columns shrink or expand to compensate for the columns' resizing,
145 *  and they also dispatch this event.
146 *
147 *  @eventType mx.events.DataGridEvent.COLUMN_STRETCH
148 */
149[Event(name="columnStretch", type="mx.events.DataGridEvent")]
150
151/**
152 *  Dispatched when the user releases the mouse button on a column header
153 *  to request the control to sort
154 *  the grid contents based on the contents of the column.
155 *  Only dispatched if the column is sortable and the data provider supports
156 *  sorting. The DataGrid control has a default handler for this event that implements
157 *  a single-column sort.  Multiple-column sort can be implemented by calling the
158 *  <code>preventDefault()</code> method to prevent the single column sort and setting
159 *  the <code>sort</code> property of the data provider.
160 * <p>
161 * <b>Note</b>: The sort arrows are defined by the default event handler for
162 * the headerRelease event. If you call the <code>preventDefault()</code> method
163 * in your event handler, the arrows are not drawn.
164 * </p>
165 *
166 *  @eventType mx.events.DataGridEvent.HEADER_RELEASE
167 */
168[Event(name="headerRelease", type="mx.events.DataGridEvent")]
169
170/**
171 *  Dispatched when the user releases the mouse button on a column header after
172 *  having dragged the column to a new location resulting in shifting the column
173 *  to a new index.
174 *
175 *  @eventType mx.events.IndexChangedEvent.HEADER_SHIFT
176 */
177[Event(name="headerShift", type="mx.events.IndexChangedEvent")]
178
179//--------------------------------------
180//  Styles
181//--------------------------------------
182
183include "../styles/metadata/IconColorStyles.as"
184
185/**
186 *  A flag that indicates whether to show vertical grid lines between
187 *  the columns.
188 *  If <code>true</code>, shows vertical grid lines.
189 *  If <code>false</code>, hides vertical grid lines.
190 *  @default true
191 */
192[Style(name="verticalGridLines", type="Boolean", inherit="no")]
193
194/**
195 *  A flag that indicates whether to show horizontal grid lines between
196 *  the rows.
197 *  If <code>true</code>, shows horizontal grid lines.
198 *  If <code>false</code>, hides horizontal grid lines.
199 *  @default false
200 */
201[Style(name="horizontalGridLines", type="Boolean", inherit="no")]
202
203/**
204 *  The color of the vertical grid lines.
205 *  @default 0x666666
206 */
207[Style(name="verticalGridLineColor", type="uint", format="Color", inherit="yes")]
208
209/**
210 *  The color of the horizontal grid lines.
211  */
212[Style(name="horizontalGridLineColor", type="uint", format="Color", inherit="yes")]
213
214/**
215 *  An array of two colors used to draw the header background gradient.
216 *  The first color is the top color.
217 *  The second color is the bottom color.
218 *  @default [0xFFFFFF, 0xE6E6E6]
219 */
220[Style(name="headerColors", type="Array", arrayType="uint", format="Color", inherit="yes")]
221
222/**
223 *  The color of the row background when the user rolls over the row.
224 *  @default 0xE3FFD6
225 */
226[Style(name="rollOverColor", type="uint", format="Color", inherit="yes")]
227
228/**
229 *  The color of the background for the row when the user selects
230 *  an item renderer in the row.
231 *  @default 0xCDFFC1
232 */
233[Style(name="selectionColor", type="uint", format="Color", inherit="yes")]
234
235/**
236 *  The name of a CSS style declaration for controlling other aspects of
237 *  the appearance of the column headers.
238 *  @default "dataGridStyles"
239 */
240[Style(name="headerStyleName", type="String", inherit="no")]
241
242/**
243 *  The class to use as the skin for a column that is being resized.
244 *  @default mx.skins.halo.DataGridColumnResizeSkin
245 */
246[Style(name="columnResizeSkin", type="Class", inherit="no")]
247
248
249/**
250 *  The class to use as the skin that defines the appearance of the
251 *  background of the column headers in a DataGrid control.
252 *  @default mx.skins.halo.DataGridHeaderSeparator
253 */
254[Style(name="headerBackgroundSkin", type="Class", inherit="no")]
255
256/**
257 *  The class to use as the skin that defines the appearance of the
258 *  separator between column headers in a DataGrid control.
259 *  @default mx.skins.halo.DataGridHeaderSeparator
260 */
261[Style(name="headerSeparatorSkin", type="Class", inherit="no")]
262
263/**
264 *  The class to use as the skin that defines the appearance of the
265 *  separator between rows in a DataGrid control.
266 *  By default, the DataGrid control uses the
267 *  <code>drawHorizontalLine()</code> and <code>drawVerticalLine()</code> methods
268 *  to draw the separators.
269 *
270 *  @default undefined
271 */
272[Style(name="horizontalSeparatorSkin", type="Class", inherit="no")]
273
274/**
275 *  The class to use as the skin that defines the appearance of the
276 *  separator between the locked and unlocked rows in a DataGrid control.
277 *  By default, the DataGrid control uses the
278 *  <code>drawHorizontalLine()</code> and <code>drawVerticalLine()</code> methods
279 *  to draw the separators.
280 *
281 *  @default undefined
282 */
283[Style(name="horizontalLockedSeparatorSkin", type="Class", inherit="no")]
284
285/**
286 *  The class to use as the skin that defines the appearance of the
287 *  separators between columns in a DataGrid control.
288 *  By default, the DataGrid control uses the
289 *  <code>drawHorizontalLine()</code> and <code>drawVerticalLine()</code> methods
290 *  to draw the separators.
291 *
292 *  @default undefined
293 */
294[Style(name="verticalSeparatorSkin", type="Class", inherit="no")]
295
296/**
297 *  The class to use as the skin that defines the appearance of the
298 *  separator between the locked and unlocked columns in a DataGrid control.
299 *  By default, the DataGrid control uses the
300 *  <code>drawHorizontalLine()</code> and <code>drawVerticalLine()</code> methods
301 *  to draw the separators.
302 *
303 *  @default undefined
304 */
305[Style(name="verticalLockedSeparatorSkin", type="Class", inherit="no")]
306
307/**
308 *  The class to use as the skin for the arrow that indicates the column sort
309 *  direction.
310 *  @default mx.skins.halo.DataGridSortArrow
311 */
312[Style(name="sortArrowSkin", type="Class", inherit="no")]
313
314/**
315 *  The class to use as the skin for the cursor that indicates that a column
316 *  can be resized.
317 *  The default value is the "cursorStretch" symbol from the Assets.swf file.
318 */
319[Style(name="stretchCursor", type="Class", inherit="no")]
320
321/**
322 *  The class to use as the skin that indicates that
323 *  a column can be dropped in the current location.
324 *
325 *  @default mx.skins.halo.DataGridColumnDropIndicator
326 */
327[Style(name="columnDropIndicatorSkin", type="Class", inherit="no")]
328
329/**
330 *  The name of a CSS style declaration for controlling aspects of the
331 *  appearance of column when the user is dragging it to another location.
332 *
333 *  @default "headerDragProxyStyle"
334 */
335[Style(name="headerDragProxyStyleName", type="String", inherit="no")]
336
337//--------------------------------------
338//  Excluded APIs
339//--------------------------------------
340
341[Exclude(name="columnCount", kind="property")]
342[Exclude(name="iconField", kind="property")]
343[Exclude(name="iconFunction", kind="property")]
344[Exclude(name="labelField", kind="property")]
345[Exclude(name="offscreenExtraRowsOrColumns", kind="property")]
346[Exclude(name="offscreenExtraRows", kind="property")]
347[Exclude(name="offscreenExtraRowsTop", kind="property")]
348[Exclude(name="offscreenExtraRowsBottom", kind="property")]
349[Exclude(name="offscreenExtraColumns", kind="property")]
350[Exclude(name="offscreenExtraColumnsLeft", kind="property")]
351[Exclude(name="offscreenExtraColumnsRight", kind="property")]
352[Exclude(name="offscreenExtraRowsOrColumnsChanged", kind="property")]
353[Exclude(name="showDataTips", kind="property")]
354[Exclude(name="cornerRadius", kind="style")]
355
356//--------------------------------------
357//  Other metadata
358//--------------------------------------
359
360[AccessibilityClass(implementation="mx.accessibility.DataGridAccImpl")]
361
362[DataBindingInfo("acceptedTypes", "{ dataProvider: &quot;String&quot; }")]
363
364[DefaultBindingProperty(source="selectedItem", destination="dataProvider")]
365
366[DefaultProperty("dataProvider")]
367
368[DefaultTriggerEvent("change")]
369
370[IconFile("DataGrid.png")]
371
372[RequiresDataBinding(true)]
373
374/**
375 *  The <code>DataGrid</code> control is like a List except that it can
376 *  show more than one column of data making it suited for showing
377 *  objects with multiple properties.
378 *  <p>
379 *  The DataGrid control provides the following features:
380 *  <ul>
381 *  <li>Columns of different widths or identical fixed widths</li>
382 *  <li>Columns that the user can resize at runtime </li>
383 *  <li>Columns that the user can reorder at runtime </li>
384 *  <li>Optional customizable column headers</li>
385 *  <li>Ability to use a custom item renderer for any column to display
386 *      data
387 *  other than text</li>
388 *  <li>Support for sorting the data by clicking on a column</li>
389 *  </ul>
390 *  </p>
391 *  The DataGrid control is intended for viewing data, and not as a
392 *  layout tool like an HTML table.
393 *  The mx.containers package provides those layout tools.
394 *
395 *  <p>The DataGrid control has the following default sizing
396 *     characteristics:</p>
397 *     <table class="innertable">
398 *        <tr>
399 *           <th>Characteristic</th>
400 *           <th>Description</th>
401 *        </tr>
402 *        <tr>
403 *           <td>Default size</td>
404 *           <td>If the columns are empty, the default width is 300
405 *               pixels. If the columns contain information but define
406 *               no explicit widths, the default width is 100 pixels
407 *               per column. The DataGrid width is sized to fit the
408 *               width of all columns, if possible.
409 *               The default number of displayed rows, including the
410 *               header is 7, and each row, by default, is 20 pixels
411 *               high.
412 *           </td>
413 *        </tr>
414 *        <tr>
415 *           <td>Minimum size</td>
416 *           <td>0 pixels.</td>
417 *        </tr>
418 *        <tr>
419 *           <td>Maximum size</td>
420 *           <td>5000 by 5000.</td>
421 *        </tr>
422 *     </table>
423 *
424 *  @mxml
425 *  <p>
426 *  The <code>&lt;mx:DataGrid&gt;</code> tag inherits all of the tag
427 *  attributes of its superclass, except for <code>labelField</code>,
428 *  <code>iconField</code>, and <code>iconFunction</code>, and adds the
429 *  following tag attributes:
430 *  </p>
431 *  <pre>
432 *  &lt;mx:DataGrid
433 *    <b>Properties</b>
434 *    columns="<i>From dataProvider</i>"
435 *    draggableColumns="true|false"
436 *    editable="false|true"
437 *    editedItemPosition="<code>null</code>"
438 *    horizontalScrollPosition="null"
439 *    imeMode="null"
440 *    itemEditorInstance="null"
441 *    minColumnWidth="<code>NaN</code>"
442 *    resizableColumns="true|false"
443 *    sortableColumns="true|false"
444 *
445 *    <b>Styles</b>
446 *    backgroundDisabledColor="0xEFEEEF"
447 *    columnDropIndicatorSkin="DataGridColumnDropIndicator"
448 *    columnResizeSkin="DataGridColumnResizeSkin"
449 *    disabledIconColor="0x999999"
450 *    headerColors="[#FFFFFF, #E6E6E6]"
451 *    headerDragProxyStyleName="headerDragProxyStyle"
452 *    headerSeparatorSkin="DataGridHeaderSeparator"
453 *    headerStyleName="dataGridStyles"
454 *    horizontalGridLineColor="<i>No default</i>"
455 *    horizontalGridLines="false|true"
456 *    horizontalLockedSeparatorSkin="undefined"
457 *    horizontalSeparatorSkin="undefined"
458 *    iconColor="0x111111"
459 *    rollOverColor="#E3FFD6"
460 *    selectionColor="#CDFFC1"
461 *    sortArrowSkin="DataGridSortArrow"
462 *    stretchCursor="<i>"cursorStretch" symbol from the Assets.swf file</i>"
463 *    verticalGridLineColor="#666666"
464 *    verticalGridLines="false|true"
465 *    verticalLockedSeparatorSkin="undefined"
466 *    verticalSeparatorSkin="undefined"
467 *
468 *    <b>Events</b>
469 *    columnStretch="<i>No default</i>"
470 *    headerRelease="<i>No default</i>"
471 *    headerShift="<i>No default</i>"
472 *    itemEditBegin="<i>No default</i>"
473 *    itemEditBeginning="<i>No default</i>"
474 *    itemEditEnd="<i>No default</i>"
475 *    itemFocusIn="<i>No default</i>"
476 *    itemFocusOut="<i>No default</i>"
477 *  /&gt;
478 *
479 *  <b>The following DataGrid code sample specifies the column order:</b>
480 *  &lt;mx:DataGrid&gt;
481 *    &lt;mx:dataProvider&gt;
482 *        &lt;mx:Object Artist="Pavement" Price="11.99"
483 *          Album="Slanted and Enchanted"/&gt;
484 *        &lt;mx:Object Artist="Pavement"
485 *          Album="Brighten the Corners" Price="11.99"/&gt;
486 *    &lt;/mx:dataProvider&gt;
487 *    &lt;mx:columns&gt;
488 *        &lt;mx:DataGridColumn dataField="Album"/&gt;
489 *        &lt;mx:DataGridColumn dataField="Price"/&gt;
490 *    &lt;/mx:columns&gt;
491 *  &lt;/mx:DataGrid&gt;
492 *  </pre>
493 *  </p>
494 *
495 *  @see mx.controls.dataGridClasses.DataGridItemRenderer
496 *  @see mx.controls.dataGridClasses.DataGridColumn
497 *  @see mx.controls.dataGridClasses.DataGridDragProxy
498 *  @see mx.events.DataGridEvent
499 *
500 *  @includeExample examples/SimpleDataGrid.mxml
501 */
502public class DataGrid extends DataGridBase implements IIMESupport
503{
504    include "../core/Version.as";
505
506    //--------------------------------------------------------------------------
507    //
508    //  Class mixins
509    //
510    //--------------------------------------------------------------------------
511
512    /**
513     *  @private
514     *  Placeholder for mixin by DataGridAccImpl.
515     */
516    mx_internal static var createAccessibilityImplementation:Function;
517
518    //--------------------------------------------------------------------------
519    //
520    //  Constructor
521    //
522    //--------------------------------------------------------------------------
523
524    /**
525     *  Constructor.
526     */
527    public function DataGrid()
528    {
529        super();
530
531        _columns = [];
532
533        // pick a default row height
534        setRowHeight(20);
535
536        // Register default handlers for item editing and sorting events.
537
538        addEventListener(DataGridEvent.ITEM_EDIT_BEGINNING,
539                        itemEditorItemEditBeginningHandler,
540                        false, EventPriority.DEFAULT_HANDLER);
541
542        addEventListener(DataGridEvent.ITEM_EDIT_BEGIN,
543                         itemEditorItemEditBeginHandler,
544                         false, EventPriority.DEFAULT_HANDLER);
545
546        addEventListener(DataGridEvent.ITEM_EDIT_END,
547                         itemEditorItemEditEndHandler,
548                         false, EventPriority.DEFAULT_HANDLER);
549
550        addEventListener(DataGridEvent.HEADER_RELEASE,
551                         headerReleaseHandler,
552                         false, EventPriority.DEFAULT_HANDLER);
553
554        addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
555    }
556
557    //--------------------------------------------------------------------------
558    //
559    //  Variables
560    //
561    //--------------------------------------------------------------------------
562
563    [Inspectable(environment="none")]
564
565    /**
566     *  A reference to the currently active instance of the item editor,
567     *  if it exists.
568     *
569     *  <p>To access the item editor instance and the new item value when an
570     *  item is being edited, you use the <code>itemEditorInstance</code>
571     *  property. The <code>itemEditorInstance</code> property
572     *  is not valid until after the event listener for
573     *  the <code>itemEditBegin</code> event executes. Therefore, you typically
574     *  only access the <code>itemEditorInstance</code> property from within
575     *  the event listener for the <code>itemEditEnd</code> event.</p>
576     *
577     *  <p>The <code>DataGridColumn.itemEditor</code> property defines the
578     *  class of the item editor
579     *  and, therefore, the data type of the item editor instance.</p>
580     *
581     *  <p>You do not set this property in MXML.</p>
582     */
583    public var itemEditorInstance:IListItemRenderer;
584
585    /**
586     *  A reference to the item renderer
587     *  in the DataGrid control whose item is currently being edited.
588     *
589     *  <p>From within an event listener for the <code>itemEditBegin</code>
590     *  and <code>itemEditEnd</code> events,
591     *  you can access the current value of the item being edited
592     *  using the <code>editedItemRenderer.data</code> property.</p>
593     */
594    public function get editedItemRenderer():IListItemRenderer
595    {
596        if (!itemEditorInstance) return null;
597
598        return actualContentHolder.listItems[actualRowIndex][actualColIndex];
599    }
600
601    /**
602     *  @private
603     *  true if we want to skip updating the headers during adjustListContent
604     */
605    private var skipHeaderUpdate:Boolean = false;
606
607    /**
608     *  @private
609     *  true if we want to block editing on mouseUp
610     */
611    private var dontEdit:Boolean = false;
612
613    /**
614     *  @private
615     *  true if we want to block editing on mouseUp
616     */
617    private var losingFocus:Boolean = false;
618
619    /**
620     *  @private
621     *  true if we're in the endEdit call.  Used to handle
622     *  some timing issues with collection updates
623     */
624    private var inEndEdit:Boolean = false;
625
626    /**
627     *  @private
628     *  true if we've disabled updates in the collection
629     */
630    private var collectionUpdatesDisabled:Boolean = false;
631
632    /**
633     *  @private
634     *  The index of the column being sorted.
635     */
636    mx_internal var sortIndex:int = -1;
637
638    /**
639     *  @private
640     *  The column being sorted.
641     */
642    private var sortColumn:DataGridColumn;
643
644    /**
645     *  @private
646     *  The direction of the sort
647     */
648    mx_internal var sortDirection:String;
649
650    /**
651     *  @private
652     *  The index of the last column being sorted on.
653     */
654    mx_internal var lastSortIndex:int = -1;
655
656    /**
657     *  @private
658     */
659    private var lastItemDown:IListItemRenderer;
660
661    /**
662     *  @private
663     */
664    private var lastItemFocused:DisplayObject;
665
666    /**
667     *  @private
668     */
669    private var displayWidth:Number;
670
671    /**
672     *  @private
673     */
674    private var lockedColumnWidth:Number = 0;
675
676    /**
677     *  @private
678     *  The column that is being moved.
679     */
680    mx_internal var movingColumn:DataGridColumn;
681
682    /**
683     *  @private
684     *  The column that is being resized.
685     */
686    mx_internal var resizingColumn:DataGridColumn;
687
688    /**
689     *  @private
690     *  Columns with visible="true"
691     */
692    private var displayableColumns:Array;
693
694    /**
695     *  @private
696     *  Whether we have auto-generated the set of columns
697     *  Defaults to true so we'll run the auto-generation at init time if needed
698     */
699    private var generatedColumns:Boolean = true;
700
701    // last known position of item editor instance
702    private var actualRowIndex:int;
703    private var actualColIndex:int;
704    private var actualContentHolder:ListBaseContentHolder;
705
706    /**
707     *  @private
708     *  Flag to indicate whether sorting is manual or programmatic.  If it's
709     *  not manual, we try to draw the sort arrow on the right column header.
710     */
711    private var manualSort:Boolean;
712
713
714    //--------------------------------------------------------------------------
715    //
716    //  Overridden properties
717    //
718    //--------------------------------------------------------------------------
719
720    //----------------------------------
721    //  baselinePosition
722    //----------------------------------
723
724    /**
725     *  @private
726     *  The baselinePosition of a DataGrid is calculated
727     *  for its first column header.
728     *  If the headers aren't shown, it is calculated as for List.
729     */
730    override public function get baselinePosition():Number
731    {
732        if (FlexVersion.compatibilityVersion < FlexVersion.VERSION_3_0)
733        {
734            var top:Number = 0;
735
736            if (border && border is IRectangularBorder)
737                top = IRectangularBorder(border).borderMetrics.top;
738
739            return top + measureText(" ").ascent;
740        }
741
742        if (!validateBaselinePosition())
743            return NaN;
744
745        if (!showHeaders)
746            return super.baselinePosition;
747
748        var header0:IUIComponent = DataGridHeader(header).rendererArray[0];
749        if (!header0)
750            return super.baselinePosition;
751
752        return header.y + header0.y + header0.baselinePosition;
753    }
754
755    /**
756     *  @private
757     *  Number of columns that can be displayed.
758     *  Some may be offscreen depending on horizontalScrollPolicy
759     *  and the width of the DataGrid.
760     */
761    override public function get columnCount():int
762    {
763        if (_columns)
764            return _columns.length;
765        else
766            return 0;
767    }
768
769    //----------------------------------
770    //  enabled
771    //----------------------------------
772
773    [Inspectable(category="General", enumeration="true,false", defaultValue="true")]
774
775    /**
776     *  @private
777     */
778    override public function set enabled(value:Boolean):void
779    {
780        super.enabled = value;
781
782        if (header)
783            header.enabled = value;
784
785        if (itemEditorInstance)
786            endEdit(DataGridEventReason.OTHER);
787
788        invalidateDisplayList();
789    }
790
791    //----------------------------------
792    //  headerHeight
793    //----------------------------------
794
795    /**
796     *  @private
797     */
798    override public function set headerHeight(value:Number):void
799    {
800        super.headerHeight = value;
801        _originalHeaderHeight = isNaN(value) ? 22 : value;
802        _originalExplicitHeaderHeight = !isNaN(value);
803    }
804
805    //----------------------------------
806    //  horizontalScrollPosition
807    //----------------------------------
808
809    /**
810     *  The offset into the content from the left edge.
811     *  This can be a pixel offset in some subclasses or some other metric
812     *  like the number of columns in a DataGrid control.
813     *
814     *  The DataGrid scrolls by columns so the value of the
815     *  <code>horizontalScrollPosition</code> property is always
816     *  in the range of 0 to the index of the columns
817     *  that will make the last column visible.  This is different from the
818     *  List control that scrolls by pixels.  The DataGrid control always aligns the left edge
819     *  of a column with the left edge of the DataGrid control.
820     */
821    override public function set horizontalScrollPosition(value:Number):void
822    {
823        // if not init or no data;
824        if (!initialized || listItems.length == 0)
825        {
826            super.horizontalScrollPosition = value;
827            return;
828        }
829
830        var oldValue:int = super.horizontalScrollPosition;
831        super.horizontalScrollPosition = value;
832
833        // columns have variable width so we need to recalc scroll parms
834        scrollAreaChanged = true;
835
836        columnsInvalid = true;
837        calculateColumnSizes();
838
839        // we are going to get a full repaint so don't repaint now
840        if (itemsSizeChanged)
841            return;
842
843        if (oldValue != value)
844        {
845            removeClipMask();
846
847            var bookmark:CursorBookmark;
848
849            if (iterator)
850                bookmark = iterator.bookmark;
851
852            clearIndicators();
853            clearVisibleData();
854            //if we scrolled more than the number of scrollable columns
855            makeRowsAndColumns(0, 0, listContent.width, listContent.height, 0, 0);
856            if (lockedRowCount)
857            {
858                var cursorPos:CursorBookmark;
859                cursorPos = lockedRowContent.iterator.bookmark;
860                makeRows(lockedRowContent, 0, 0, unscaledWidth, unscaledHeight, 0, 0, true, lockedRowCount);
861                if (iteratorValid)
862                    lockedRowContent.iterator.seek(cursorPos, 0);
863            }
864
865            if (headerVisible && header)
866            {
867                header.visibleColumns = visibleColumns;
868                header.headerItemsChanged = true;
869                header.invalidateSize();
870                header.validateNow();
871            }
872
873            if (iterator && bookmark)
874                iterator.seek(bookmark, 0);
875
876            invalidateDisplayList();
877
878            addClipMask(false);
879        }
880    }
881
882    //----------------------------------
883    //  horizontalScrollPolicy
884    //----------------------------------
885
886    /**
887     *  @private
888     *  Accomodates ScrollPolicy.AUTO.
889     *  Makes sure column widths stay in synch.
890     *
891     *  @param policy on, off, or auto
892     */
893    override public function set horizontalScrollPolicy(value:String):void
894    {
895        super.horizontalScrollPolicy = value;
896        columnsInvalid = true;
897        itemsSizeChanged = true;
898        invalidateDisplayList();
899    }
900
901    /**
902     *  @private
903     */
904    override public function set verticalScrollPosition(value:Number):void
905    {
906        skipHeaderUpdate = true;
907
908        var oldValue:Number = super.verticalScrollPosition;
909        super.verticalScrollPosition = value;
910        if (oldValue != value)
911        {
912            if (lockedColumnContent)
913                drawRowGraphics(lockedColumnContent)
914        }
915        skipHeaderUpdate = false;
916    }
917
918    /**
919     *  @private
920     *
921     */
922    override protected function createChildren():void
923    {
924        super.createChildren();
925
926        if (!header)
927        {
928            header = new headerClass();
929            header.styleName = this;
930            addChild(header);
931        }
932    }
933
934    //----------------------------------
935    //  imeMode
936    //----------------------------------
937
938    /**
939     *  @private
940     */
941    private var _imeMode:String = null;
942
943    [Inspectable(environment="none")]
944
945    /**
946     *  Specifies the IME (input method editor) mode.
947     *  The IME enables users to enter text in Chinese, Japanese, and Korean.
948     *  Flex sets the specified IME mode when the control gets the focus,
949     *  and sets it back to the previous value when the control loses the focus.
950     *
951     * <p>The flash.system.IMEConversionMode class defines constants for the
952     *  valid values for this property.
953     *  You can also specify <code>null</code> to specify no IME.</p>
954     *
955     *  @see flash.system.IMEConversionMode
956     *
957     *  @default null
958     */
959    public function get imeMode():String
960    {
961        return _imeMode;
962    }
963
964    /**
965     *  @private
966     */
967    public function set imeMode(value:String):void
968    {
969        _imeMode = value;
970    }
971
972    //----------------------------------
973    //  itemRenderer
974    //----------------------------------
975
976    /**
977     *  @private
978     *
979     *  Defer creations of the class factory
980     *  to give a chance for the moduleFactory to be set.
981     */
982    override public function get itemRenderer():IFactory
983    {
984        if (super.itemRenderer == null)
985        {
986            var fontName:String =
987                StringUtil.trimArrayElements(getStyle("fontFamily"), ",");
988            var fontWeight:String = getStyle("fontWeight");
989            var fontStyle:String = getStyle("fontStyle");
990            var bold:Boolean = (fontWeight == "bold");
991            var italic:Boolean = (fontStyle == "italic");
992
993            var flexModuleFactory:IFlexModuleFactory =
994                getFontContext(fontName, bold, italic);
995
996            super.itemRenderer = new ContextualClassFactory(
997                DataGridItemRenderer, flexModuleFactory);
998        }
999
1000        return super.itemRenderer;
1001    }
1002
1003    //----------------------------------
1004    //  minColumnWidth
1005    //----------------------------------
1006
1007    /**
1008     *  @private
1009     */
1010    private var _minColumnWidth:Number;
1011
1012    /**
1013     *  @private
1014     */
1015    private var minColumnWidthInvalid:Boolean = false;
1016
1017    [Inspectable(defaultValue="NaN")]
1018
1019    /**
1020     *  The minimum width of the columns, in pixels.  If not NaN,
1021     *  the DataGrid control applies this value as the minimum width for
1022     *  all columns.  Otherwise, individual columns can have
1023     *  their own minimum widths.
1024     *
1025     *  @default NaN
1026     */
1027    public function get minColumnWidth():Number
1028    {
1029        return _minColumnWidth;
1030    }
1031
1032    /**
1033     *  @private
1034     */
1035    public function set minColumnWidth(value:Number):void
1036    {
1037        _minColumnWidth = value;
1038        minColumnWidthInvalid = true;
1039        itemsSizeChanged = true;
1040        columnsInvalid = true;
1041        invalidateDisplayList();
1042    }
1043
1044    //--------------------------------------------------------------------------
1045    //
1046    //  Properties
1047    //
1048    //--------------------------------------------------------------------------
1049
1050    //----------------------------------
1051    //  columns
1052    //----------------------------------
1053
1054    /**
1055     *  @private
1056     */
1057    private var _columns:Array; // the array of our DataGridColumns
1058
1059    [Bindable("columnsChanged")]
1060    [Inspectable(category="General", arrayType="mx.controls.dataGridClasses.DataGridColumn")]
1061
1062    /**
1063     *  An array of DataGridColumn objects, one for each column that
1064     *  can be displayed.  If not explicitly set, the DataGrid control
1065     *  attempts to examine the first data provider item to determine the
1066     *  set of properties and display those properties in alphabetic
1067     *  order.
1068     *
1069     *  <p>If you want to change the set of columns, you must get this array,
1070     *  make modifications to the columns and order of columns in the array,
1071     *  and then assign the new array to the columns property.  This is because
1072     *  the DataGrid control returned a new copy of the array of columns and therefore
1073     *  did not notice the changes.</p>
1074     */
1075    override public function get columns():Array
1076    {
1077        return _columns.slice(0);
1078    }
1079
1080    /**
1081     *  @private
1082     */
1083    override public function set columns(value:Array):void
1084    {
1085        var n:int;
1086        var i:int;
1087
1088        n = _columns.length;
1089        for (i = 0; i < n; i++)
1090        {
1091            columnRendererChanged(_columns[i]);
1092        }
1093
1094        freeItemRenderersTable = new Dictionary(false);
1095        columnMap = {};
1096
1097        _columns = value.slice(0);
1098        columnsInvalid = true;
1099        generatedColumns = false;
1100
1101        n = value.length;
1102        for (i = 0; i < n; i++)
1103        {
1104            var column:DataGridColumn = _columns[i];
1105            column.owner = this;
1106            column.colNum = i;
1107            if (column.cachedHeaderRenderer)
1108            {
1109                var item:DisplayObject = column.cachedHeaderRenderer as DisplayObject
1110                if (item.parent)
1111                    item.parent.removeChild(item);
1112                column.cachedHeaderRenderer = null;
1113            }
1114        }
1115
1116        updateSortIndexAndDirection();
1117
1118        itemsSizeChanged = true;
1119        invalidateDisplayList();
1120        dispatchEvent(new Event("columnsChanged"));
1121    }
1122
1123    //----------------------------------
1124    //  draggableColumns
1125    //----------------------------------
1126
1127    /**
1128     *  @private
1129     *  Storage for the draggableColumns property.
1130     */
1131    private var _draggableColumns:Boolean = true;
1132
1133    [Inspectable(defaultValue="true")]
1134
1135    /**
1136     *  A flag that indicates whether the user is allowed to reorder columns.
1137     *  If <code>true</code>, the user can reorder the columns
1138     *  of the DataGrid control by dragging the header cells.
1139     *
1140     *  @default true
1141     */
1142    public function get draggableColumns():Boolean
1143    {
1144        return _draggableColumns;
1145    }
1146
1147    /**
1148     *  @private
1149     */
1150    public function set draggableColumns(value:Boolean):void
1151    {
1152        _draggableColumns = value;
1153    }
1154
1155    //----------------------------------
1156    //  editable
1157    //----------------------------------
1158
1159    [Inspectable(category="General")]
1160
1161    /**
1162     *  A flag that indicates whether or not the user can edit
1163     *  items in the data provider.
1164     *  If <code>true</code>, the item renderers in the control are editable.
1165     *  The user can click on an item renderer to open an editor.
1166     *
1167     *  <p>You can turn off editing for individual columns of the
1168     *  DataGrid control using the <code>DataGridColumn.editable</code> property,
1169     *  or by handling the <code>itemEditBeginning</code> and
1170     *  <code>itemEditBegin</code> events</p>
1171     *
1172     *  @default false
1173     */
1174    public var editable:Boolean = false;
1175
1176    //----------------------------------
1177    //  editedItemPosition
1178    //----------------------------------
1179
1180    /**
1181     *  @private
1182     */
1183    private var bEditedItemPositionChanged:Boolean = false;
1184
1185    /**
1186     *  @private
1187     *  undefined means we've processed it
1188     *  null means don't put up an editor
1189     *  {} is the coordinates for the editor
1190     */
1191    private var _proposedEditedItemPosition:*;
1192
1193    /**
1194     *  @private
1195     *  the last editedItemPosition and the last
1196     *  position where editing was attempted if editing
1197     *  was cancelled.  We restore editing
1198     *  to this point if we get focus from the TAB key
1199     */
1200    private var lastEditedItemPosition:*;
1201
1202    /**
1203     *  @private
1204     */
1205    private var _editedItemPosition:Object;
1206
1207    /**
1208     *  @private
1209     */
1210    private var itemEditorPositionChanged:Boolean = false;
1211
1212
1213    [Bindable("itemFocusIn")]
1214
1215    /**
1216     *  The column and row index of the item renderer for the
1217     *  data provider item being edited, if any.
1218     *
1219     *  <p>This Object has two fields, <code>columnIndex</code> and
1220     *  <code>rowIndex</code>,
1221     *  the zero-based column and row indexes of the item.
1222     *  For example: {columnIndex:2, rowIndex:3}</p>
1223     *
1224     *  <p>Setting this property scrolls the item into view and
1225     *  dispatches the <code>itemEditBegin</code> event to
1226     *  open an item editor on the specified item renderer.</p>
1227     *
1228     *  @default null
1229     */
1230    public function get editedItemPosition():Object
1231    {
1232        if (_editedItemPosition)
1233            return {rowIndex: _editedItemPosition.rowIndex,
1234                columnIndex: _editedItemPosition.columnIndex};
1235        else
1236            return _editedItemPosition;
1237    }
1238
1239    /**
1240     *  @private
1241     */
1242    public function set editedItemPosition(value:Object):void
1243    {
1244        if (!value)
1245        {
1246            setEditedItemPosition(null);
1247            return;
1248        }
1249
1250        var newValue:Object = {rowIndex: value.rowIndex,
1251            columnIndex: value.columnIndex};
1252
1253        setEditedItemPosition(newValue);
1254    }
1255
1256
1257    //----------------------------------
1258    //  resizableColumns
1259    //----------------------------------
1260
1261    [Inspectable(category="General")]
1262
1263    /**
1264     *  A flag that indicates whether the user can change the size of the
1265     *  columns.
1266     *  If <code>true</code>, the user can stretch or shrink the columns of
1267     *  the DataGrid control by dragging the grid lines between the header cells.
1268     *  If <code>true</code>, individual columns must also have their
1269     *  <code>resizable</code> properties set to <code>false</code> to
1270     *  prevent the user from resizing a particular column.
1271     *
1272     *  @default true
1273     */
1274    public var resizableColumns:Boolean = true;
1275
1276    //----------------------------------
1277    //  sortableColumns
1278    //----------------------------------
1279
1280    [Inspectable(category="General")]
1281
1282    /**
1283     *  A flag that indicates whether the user can sort the data provider items
1284     *  by clicking on a column header cell.
1285     *  If <code>true</code>, the user can sort the data provider items by
1286     *  clicking on a column header cell.
1287     *  The <code>DataGridColumn.dataField</code> property of the column
1288     *  or the <code>DataGridColumn.sortCompareFunction</code> property
1289     *  of the column is used as the sort field.
1290     *  If a column is clicked more than once
1291     *  the sort alternates between ascending and descending order.
1292     *  If <code>true</code>, individual columns can be made to not respond
1293     *  to a click on a header by setting the column's <code>sortable</code>
1294     *  property to <code>false</code>.
1295     *
1296     *  <p>When a user releases the mouse button over a header cell, the DataGrid
1297     *  control dispatches a <code>headerRelease</code> event if both
1298     *  this property and the column's sortable property are <code>true</code>.
1299     *  If no handler calls the <code>preventDefault()</code> method on the event, the
1300     *  DataGrid sorts using that column's <code>DataGridColumn.dataField</code> or
1301     *  <code>DataGridColumn.sortCompareFunction</code> properties.</p>
1302     *
1303     *  @default true
1304     *
1305     *  @see mx.controls.dataGridClasses.DataGridColumn#dataField
1306     *  @see mx.controls.dataGridClasses.DataGridColumn#sortCompareFunction
1307     */
1308    public var sortableColumns:Boolean = true;
1309
1310    //--------------------------------------------------------------------------
1311    //
1312    //  Overridden methods
1313    //
1314    //--------------------------------------------------------------------------
1315
1316    /**
1317     *  @private
1318     */
1319    override public function invalidateDisplayList():void
1320    {
1321        super.invalidateDisplayList();
1322        if (header)
1323        {
1324            header.headerItemsChanged = true;
1325            header.invalidateSize();
1326            header.invalidateDisplayList();
1327        }
1328        if (lockedColumnHeader)
1329        {
1330            lockedColumnHeader.headerItemsChanged = true;
1331            lockedColumnHeader.invalidateSize();
1332            lockedColumnHeader.invalidateDisplayList();
1333        }
1334    }
1335
1336    [Inspectable(category="Data", defaultValue="undefined")]
1337
1338    /**
1339     *  @private
1340     */
1341    override public function set dataProvider(value:Object):void
1342    {
1343        if (itemEditorInstance)
1344            endEdit(DataGridEventReason.OTHER);
1345
1346        lastEditedItemPosition = null;
1347
1348        super.dataProvider = value;
1349    }
1350
1351    /**
1352     *  @private
1353     */
1354    override protected function initializeAccessibility():void
1355    {
1356        if (DataGrid.createAccessibilityImplementation != null)
1357            DataGrid.createAccessibilityImplementation(this);
1358    }
1359
1360    /**
1361     *  @private
1362     *  Measures the DataGrid based on its contents,
1363     *  summing the total of the visible column widths.
1364     */
1365    override protected function measure():void
1366    {
1367        super.measure();
1368
1369        if (explicitRowCount != -1)
1370        {
1371            measuredHeight += headerHeight;
1372            measuredMinHeight += headerHeight;
1373        }
1374
1375        var o:EdgeMetrics = viewMetrics;
1376
1377        var n:int = columns.length;
1378        if (n == 0)
1379        {
1380            measuredWidth = DEFAULT_MEASURED_WIDTH;
1381            measuredMinWidth = DEFAULT_MEASURED_MIN_WIDTH;
1382            return;
1383        }
1384
1385        var columnWidths:Number = 0;
1386        var columnMinWidths:Number = 0;
1387        for (var i:int = 0; i < n; i++)
1388        {
1389            if (columns[i].visible)
1390            {
1391                columnWidths += columns[i].preferredWidth;
1392                if (isNaN(_minColumnWidth))
1393                    columnMinWidths += columns[i].minWidth;
1394            }
1395        }
1396
1397        if (!isNaN(_minColumnWidth))
1398            columnMinWidths = n * _minColumnWidth;
1399
1400        measuredWidth = columnWidths + o.left + o.right;
1401        measuredMinWidth = columnMinWidths + o.left + o.right;
1402
1403        // factor out scrollbars if policy == AUTO.  See Container.viewMetrics
1404        if (verticalScrollPolicy == ScrollPolicy.AUTO &&
1405            verticalScrollBar && verticalScrollBar.visible)
1406        {
1407            measuredWidth -= verticalScrollBar.minWidth;
1408            measuredMinWidth -= verticalScrollBar.minWidth;
1409        }
1410        if (horizontalScrollPolicy == ScrollPolicy.AUTO &&
1411            horizontalScrollBar && horizontalScrollBar.visible)
1412        {
1413            measuredHeight -= horizontalScrollBar.minHeight;
1414            measuredMinHeight -= horizontalScrollBar.minHeight;
1415        }
1416
1417    }
1418
1419    /**
1420     *  @private
1421     *  Sizes and positions the column headers, columns, and items based on the
1422     *  size of the DataGrid.
1423     */
1424    override protected function updateDisplayList(unscaledWidth:Number,
1425                                                  unscaledHeight:Number):void
1426    {
1427        // Note: We can't immediately call super.updateDisplayList()
1428        // because the visibleColumns array must be populated first.
1429
1430        // trace(">>updateDisplayList");
1431        if (displayWidth != unscaledWidth - viewMetrics.right - viewMetrics.left)
1432        {
1433            displayWidth = unscaledWidth - viewMetrics.right - viewMetrics.left;
1434            columnsInvalid = true;
1435        }
1436
1437        calculateColumnSizes();
1438
1439        if (itemEditorPositionChanged)
1440        {
1441            itemEditorPositionChanged = false;
1442            // don't do this if mouse is down on an item
1443            // on mouse up, we'll let the edit session logic
1444            // request a new position
1445            if (!lastItemDown)
1446                scrollToEditedItem(editedItemPosition.rowIndex, editedItemPosition.colIndex);
1447        }
1448
1449        super.updateDisplayList(unscaledWidth, unscaledHeight);
1450
1451        if (collection && collection.length)
1452        {
1453            setRowCount(listItems.length);
1454
1455            if (listItems.length)
1456                setColumnCount(listItems[0].length);
1457            else
1458                setColumnCount(0);
1459        }
1460
1461        // If we have a vScroll only, we want the scrollbar to be below
1462        // the header.
1463        if (verticalScrollBar != null && verticalScrollBar.visible &&
1464           (horizontalScrollBar == null || !horizontalScrollBar.visible) &&
1465           headerVisible)
1466        {
1467            var hh:Number = header.height;
1468            var bm:EdgeMetrics = borderMetrics;
1469
1470            if (roomForScrollBar(verticalScrollBar,
1471                        unscaledWidth-bm.left-bm.right,
1472                        unscaledHeight-hh-bm.top-bm.bottom))
1473            {
1474                verticalScrollBar.move(verticalScrollBar.x, viewMetrics.top + hh);
1475                verticalScrollBar.setActualSize(
1476                    verticalScrollBar.width,
1477                    unscaledHeight - viewMetrics.top - viewMetrics.bottom - hh);
1478                verticalScrollBar.visible = true;
1479                headerMask.width += verticalScrollBar.getExplicitOrMeasuredWidth();
1480
1481                if (!DataGridHeader(header).needRightSeparator)
1482                {
1483                    header.invalidateDisplayList();
1484                    DataGridHeader(header).needRightSeparator = true;
1485                }
1486            }
1487            else
1488            {
1489                if (DataGridHeader(header).needRightSeparator)
1490                {
1491                    header.invalidateDisplayList();
1492                    DataGridHeader(header).needRightSeparator = false;
1493                }
1494            }
1495        }
1496        else
1497        {
1498            if (DataGridHeader(header).needRightSeparator)
1499            {
1500                header.invalidateDisplayList();
1501                DataGridHeader(header).needRightSeparator = false;
1502            }
1503        }
1504
1505        if (bEditedItemPositionChanged)
1506        {
1507            bEditedItemPositionChanged = false;
1508            // don't do this if mouse is down on an item
1509            // on mouse up, we'll let the edit session logic
1510            // request a new position
1511            if (!lastItemDown)
1512                commitEditedItemPosition(_proposedEditedItemPosition);
1513            _proposedEditedItemPosition = undefined;
1514            itemsSizeChanged = false;
1515        }
1516
1517        drawRowBackgrounds();
1518        drawLinesAndColumnBackgrounds();
1519
1520        if (lockedRowCount && lockedRowContent)
1521        {
1522            drawRowGraphics(lockedRowContent);
1523            drawLinesAndColumnGraphics(lockedRowContent, visibleColumns, { bottom: 1});
1524            if (lockedColumnCount)
1525            {
1526                drawRowGraphics(lockedColumnAndRowContent);
1527                drawLinesAndColumnGraphics(lockedColumnAndRowContent, visibleLockedColumns, { right: 1, bottom: 1});
1528            }
1529        }
1530        if (lockedColumnCount)
1531        {
1532            drawRowGraphics(lockedColumnContent)
1533            drawLinesAndColumnGraphics(lockedColumnContent, visibleLockedColumns, { right: 1})
1534        }
1535
1536        // trace("<<updateDisplayList");
1537    }
1538
1539    /**
1540     *  @private
1541     */
1542    override protected function makeRowsAndColumns(left:Number, top:Number,
1543                                                right:Number, bottom:Number,
1544                                                firstCol:int, firstRow:int,
1545                                                byCount:Boolean = false, rowsNeeded:uint = 0):Point
1546    {
1547        allowItemSizeChangeNotification = false;
1548
1549        var pt:Point = super.makeRowsAndColumns(left, top, right, bottom,
1550                                                firstCol, firstRow, byCount, rowsNeeded);
1551        if (itemEditorInstance)
1552        {
1553            actualContentHolder.setChildIndex(DisplayObject(itemEditorInstance),
1554                                      actualContentHolder.numChildren - 1);
1555            var col:DataGridColumn;
1556            if (lockedColumnCount && editedItemPosition.columnIndex && visibleLockedColumns[lockedColumnCount - 1].colNum)
1557                col = visibleLockedColumns[actualColIndex];
1558            else
1559                col = visibleColumns[actualColIndex];
1560
1561            var item:IListItemRenderer = actualContentHolder.listItems[actualRowIndex][actualColIndex];
1562            var rowData:ListRowInfo = actualContentHolder.rowInfo[actualRowIndex];
1563            if (item && !col.rendererIsEditor)
1564            {
1565                var dx:Number = col.editorXOffset;
1566                var dy:Number = col.editorYOffset;
1567                var dw:Number = col.editorWidthOffset;
1568                var dh:Number = col.editorHeightOffset;
1569                itemEditorInstance.move(item.x + dx, rowData.y + dy);
1570                itemEditorInstance.setActualSize(Math.min(col.width + dw, actualContentHolder.width - itemEditorInstance.x),
1571                                         Math.min(rowData.height + dh, actualContentHolder.height - itemEditorInstance.y));
1572                item.visible = false;
1573
1574            }
1575
1576            var lines:Sprite = Sprite(actualContentHolder.getChildByName("lines"));
1577            if (lines)
1578                actualContentHolder.setChildIndex(lines, actualContentHolder.numChildren - 1);
1579        }
1580
1581        allowItemSizeChangeNotification = variableRowHeight;
1582        return pt;
1583    }
1584
1585   /**
1586     *  @private
1587     */
1588    override protected function commitProperties():void
1589    {
1590        super.commitProperties();
1591
1592        if (itemsNeedMeasurement)
1593        {
1594            itemsNeedMeasurement = false;
1595            if (isNaN(explicitRowHeight))
1596            {
1597                if (iterator && columns.length > 0)
1598                {
1599                    //set DataGridBase.visibleColumns to the set of
1600                    //all columns
1601                    visibleColumns = columns;
1602                    columnsInvalid = true;
1603
1604                    var paddingTop:Number = getStyle("paddingTop");
1605                    var paddingBottom:Number = getStyle("paddingBottom");
1606
1607                    var data:Object = iterator.current;
1608                    var item:IListItemRenderer;
1609                    var c:DataGridColumn;
1610                    var ch:Number = 0;
1611                    var n:int = columns.length;
1612                    for (var j:int = 0; j < n; j++)
1613                    {
1614                        c = columns[j];
1615
1616                        if (!c.visible)
1617                            continue;
1618
1619                        item = c.getMeasuringRenderer(false, data);
1620                        if (DisplayObject(item).parent == null)
1621                            listContent.addChild(DisplayObject(item));
1622                        setupRendererFromData(c, item, data);
1623                        ch = Math.max(ch, item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop);
1624                    }
1625
1626                    // unless specified otherwise, rowheight defaults to 20
1627                    setRowHeight(Math.max(ch, 20));
1628                }
1629                else
1630                    setRowHeight(20);
1631            }
1632        }
1633    }
1634
1635    /**
1636     *  @private
1637     *  Instead of measuring the items, we measure the visible columns instead.
1638     */
1639    override public function measureWidthOfItems(index:int = -1, count:int = 0):Number
1640    {
1641        var w:Number = 0;
1642
1643        var n:int = columns ? columns.length : 0;
1644        for (var i:int = 0; i < n; i++)
1645        {
1646            if (columns[i].visible)
1647                w += columns[i].width;
1648        }
1649
1650        return w;
1651    }
1652
1653    /**
1654     *  @private
1655     */
1656
1657    mx_internal function setupRendererFromData(c:DataGridColumn, item:IListItemRenderer, data:Object):void
1658    {
1659        var rowData:DataGridListData = DataGridListData(makeListData(data, itemToUID(data), 0, c.colNum, c));
1660        if (item is IDropInListItemRenderer)
1661            IDropInListItemRenderer(item).listData = data ? rowData : null;
1662        item.data = data;
1663        item.explicitWidth = getWidthOfItem(item, c);
1664        UIComponentGlobals.layoutManager.validateClient(item, true);
1665    }
1666
1667    /**
1668     *  @private
1669     */
1670    override public function measureHeightOfItems(index:int = -1, count:int = 0):Number
1671    {
1672        return measureHeightOfItemsUptoMaxHeight(index, count);
1673    }
1674
1675    /**
1676     *  @private
1677     */
1678    mx_internal function measureHeightOfItemsUptoMaxHeight(index:int = -1, count:int = 0, maxHeight:Number = -1):Number
1679    {
1680        if (!columns.length)
1681            return rowHeight * count;
1682
1683        var h:Number = 0;
1684
1685        var item:IListItemRenderer;
1686        var c:DataGridColumn;
1687        var ch:Number = 0;
1688        var n:int;
1689        var j:int;
1690
1691        var paddingTop:Number = getStyle("paddingTop");
1692        var paddingBottom:Number = getStyle("paddingBottom");
1693
1694        var lockedCount:int = lockedRowCount;
1695
1696        if (headerVisible && count > 0 && index == -1)
1697        {
1698            h = calculateHeaderHeight();
1699
1700            if (maxHeight != -1 && h > maxHeight)
1701            {
1702                setRowCount(0);
1703                return 0;
1704            }
1705
1706            // trace(this + " header preferredHeight = " + h);
1707            count --;
1708            index = 0;
1709        }
1710
1711        var bookmark:CursorBookmark = (iterator) ? iterator.bookmark : null;
1712
1713        var bMore:Boolean = iterator != null;
1714        if (index != -1 && iterator)
1715        {
1716            try
1717            {
1718                iterator.seek(CursorBookmark.FIRST, index);
1719            }
1720            catch(e:ItemPendingError)
1721            {
1722                bMore = false;
1723            }
1724        }
1725
1726        if (lockedCount > 0)
1727        {
1728            try
1729            {
1730                collectionIterator.seek(CursorBookmark.FIRST,0);
1731            }
1732            catch(e:ItemPendingError)
1733            {
1734                bMore = false;
1735            }
1736        }
1737
1738        for (var i:int = 0; i < count; i++)
1739        {
1740            var data:Object;
1741            if (bMore)
1742            {
1743                data = (lockedCount > 0) ? collectionIterator.current : iterator.current;
1744                ch = 0;
1745                n = columns.length;
1746                for (j = 0; j < n; j++)
1747                {
1748                    c = columns[j];
1749
1750                    if (!c.visible)
1751                        continue;
1752
1753                    item = c.getMeasuringRenderer(false, data);
1754                    if (DisplayObject(item).parent == null)
1755                        listContent.addChild(DisplayObject(item));
1756                    setupRendererFromData(c, item, data);
1757                    ch = Math.max(ch, variableRowHeight ? item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop : rowHeight);
1758                }
1759            }
1760
1761            if (maxHeight != -1 && (h + ch > maxHeight || !bMore))
1762            {
1763                try
1764                {
1765                    if (iterator)
1766                        iterator.seek(bookmark, 0);
1767                }
1768                catch(e:ItemPendingError)
1769                {
1770                    // we don't recover here since we'd only get here if the first seek failed.
1771                }
1772                count = (headerVisible) ? i + 1 : i;
1773                setRowCount(count);
1774                return h;
1775            }
1776
1777            h += ch;
1778            if (iterator)
1779            {
1780                try
1781                {
1782                    bMore = iterator.moveNext();
1783                    if (lockedCount > 0)
1784                    {
1785                        collectionIterator.moveNext();
1786                        lockedCount--;
1787                    }
1788                }
1789                catch(e:ItemPendingError)
1790                {
1791                    // if we run out of data, assume all remaining rows are the size of the previous row
1792                    bMore = false;
1793                }
1794            }
1795        }
1796
1797        if (iterator)
1798        {
1799            try
1800            {
1801                iterator.seek(bookmark, 0);
1802            }
1803            catch(e:ItemPendingError)
1804            {
1805                // we don't recover here since we'd only get here if the first seek failed.
1806            }
1807        }
1808
1809        // trace("calcheight = " + h);
1810        return h;
1811    }
1812
1813    /**
1814     *  @private
1815     */
1816    mx_internal function calculateHeaderHeight():Number
1817    {
1818        if (!columns.length)
1819            return rowHeight;
1820
1821        // block bad behavior from PDG
1822        if (!listContent)
1823            return rowHeight;
1824
1825        var item:IListItemRenderer;
1826        var c:DataGridColumn;
1827        var rowData:DataGridListData;
1828        var ch:Number = 0;
1829        var n:int;
1830        var j:int;
1831
1832        var paddingTop:Number = getStyle("paddingTop");
1833        var paddingBottom:Number = getStyle("paddingBottom");
1834
1835        if (showHeaders)
1836        {
1837            ch = 0;
1838            n = columns.length;
1839
1840            if (_headerWordWrapPresent)
1841            {
1842                _headerHeight = _originalHeaderHeight;
1843                _explicitHeaderHeight = _originalExplicitHeaderHeight;
1844            }
1845
1846            for (j = 0; j < n; j++)
1847            {
1848                c = columns[j];
1849
1850                if (!c.visible)
1851                    continue;
1852
1853                item = c.cachedHeaderRenderer;
1854                if (!item)
1855                {
1856                    item = createColumnItemRenderer(c, true, c);
1857                    item.styleName = c;
1858                    c.cachedHeaderRenderer = item;
1859                }
1860                if (DisplayObject(item).parent == null)
1861                {
1862                    listContent.addChild(DisplayObject(item));
1863                    item.visible = false;
1864                }
1865                rowData = DataGridListData(makeListData(c, uid, 0, c.colNum, c));
1866                rowMap[item.name] = rowData;
1867                if (item is IDropInListItemRenderer)
1868                    IDropInListItemRenderer(item).listData = rowData;
1869                item.data = c;
1870                item.explicitWidth = c.width;
1871                UIComponentGlobals.layoutManager.validateClient(item, true);
1872                ch = Math.max(ch, _explicitHeaderHeight ? headerHeight : item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop);
1873
1874                if (columnHeaderWordWrap(c))
1875                    _headerWordWrapPresent = true;
1876            }
1877
1878            if (_headerWordWrapPresent)
1879            {
1880                // take backups
1881                _originalHeaderHeight = _headerHeight;
1882                _originalExplicitHeaderHeight = _explicitHeaderHeight;
1883
1884                _headerHeight = ch;
1885                _explicitHeaderHeight = true;
1886            }
1887        }
1888        return ch;
1889    }
1890
1891    private var _headerWordWrapPresent:Boolean = false;
1892    private var _originalExplicitHeaderHeight:Boolean = false;
1893    private var _originalHeaderHeight:Number = 0;
1894
1895    /**
1896     *  @private
1897     */
1898    override protected function calculateRowHeight(data:Object, hh:Number, skipVisible:Boolean = false):Number
1899    {
1900        var item:IListItemRenderer;
1901        var c:DataGridColumn;
1902
1903        var n:int = columns.length;
1904        var j:int;
1905        var k:int = 0;
1906        var l:int = visibleLockedColumns.length;
1907
1908        if (skipVisible && visibleColumns.length == _columns.length)
1909            return hh;
1910
1911        var paddingTop:Number = getStyle("paddingTop");
1912        var paddingBottom:Number = getStyle("paddingBottom");
1913
1914        for (j = 0; j < n; j++)
1915        {
1916            // skip any columns that are visible
1917            if (skipVisible && k < l && visibleLockedColumns[k].colNum == columns[j].colNum)
1918            {
1919                k++;
1920                continue;
1921            }
1922            if (skipVisible && k - l < visibleColumns.length && visibleColumns[k - l].colNum == columns[j].colNum)
1923            {
1924                k++;
1925                continue;
1926            }
1927            c = columns[j];
1928
1929            if (!c.visible)
1930                continue;
1931
1932            item = c.getMeasuringRenderer(false, data);
1933            if (DisplayObject(item).parent == null)
1934                listContent.addChild(DisplayObject(item));
1935            setupRendererFromData(c, item, data);
1936            hh = Math.max(hh, item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop);
1937        }
1938        return hh;
1939    }
1940
1941    /**
1942     *  @private
1943     */
1944    override protected function scrollHandler(event:Event):void
1945    {
1946        if (event.target == verticalScrollBar ||
1947            event.target == horizontalScrollBar)
1948        {
1949            // TextField.scroll bubbles so you might see it here
1950            if (event is ScrollEvent)
1951            {
1952                if (!liveScrolling &&
1953                    ScrollEvent(event).detail == ScrollEventDetail.THUMB_TRACK)
1954                {
1955                    return;
1956                }
1957
1958                if (itemEditorInstance)
1959                    endEdit(DataGridEventReason.OTHER);
1960
1961                var scrollBar:ScrollBar = ScrollBar(event.target);
1962                var pos:Number = scrollBar.scrollPosition;
1963
1964                if (scrollBar == verticalScrollBar)
1965                    verticalScrollPosition = pos;
1966                else if (scrollBar == horizontalScrollBar)
1967                    horizontalScrollPosition = pos;
1968
1969                super.scrollHandler(event);
1970            }
1971        }
1972    }
1973
1974    private function displayingPartialRow():Boolean
1975    {
1976        var index:int = listItems.length - 1 - offscreenExtraRowsBottom;
1977        if (rowInfo[index].y + rowInfo[index].height > listContent.heightExcludingOffsets - listContent.topOffset)
1978            return true;
1979        return false;
1980    }
1981
1982    /**
1983     *  @private
1984     */
1985    override protected function configureScrollBars():void
1986    {
1987        if (columnsInvalid)
1988            return;
1989
1990        if (!displayableColumns)
1991            return;
1992
1993        // for purposes of computing rows, we need to accomodate
1994        // the case where all the visible columns are locked columns
1995        var countableContentListItems:Array = this.listItems;
1996        if (visibleColumns && !visibleColumns.length && visibleLockedColumns && visibleLockedColumns.length)
1997            countableContentListItems = lockedColumnContent.listItems;
1998
1999        var oldHorizontalScrollBar:Object = horizontalScrollBar;
2000        var oldVerticalScrollBar:Object = verticalScrollBar;
2001
2002        var rowCount:int = countableContentListItems.length;
2003        if (rowCount == 0)
2004        {
2005            // Get rid of any existing scrollbars.
2006            if (oldHorizontalScrollBar || oldVerticalScrollBar)
2007                // protect against situation where the scrollbars
2008                // cause re-layout and the listContent is sized
2009                // to zero because of number of lockedRowCount
2010                if (listContent.height)
2011                    setScrollBarProperties(0, 0, 0, 0);
2012
2013            return;
2014        }
2015
2016        // partial last rows don't count
2017        if (rowCount > 1 && displayingPartialRow())
2018            rowCount--;
2019
2020        // offset, when added to rowCount, is the index of the dataProvider
2021        // item for that row.  IOW, row 10 in listItems is showing dataProvider
2022        // item 10 + verticalScrollPosition - lockedRowCount;
2023        var offset:int = verticalScrollPosition;
2024        // don't count filler rows at the bottom either.
2025        var fillerRows:int = 0;
2026        while (rowCount && countableContentListItems[rowCount - 1].length == 0)
2027        {
2028            // as long as we're past the end of the collection, add up
2029            // fillerRows
2030            if (collection && rowCount + offset >= collection.length - lockedRowCount)
2031            {
2032                rowCount--;
2033                ++fillerRows;
2034            }
2035            else
2036                break;
2037        }
2038
2039        // we have to scroll up.  We can't have filler rows unless the scrollPosition is 0
2040        if (verticalScrollPosition > 0 && fillerRows > 0)
2041        {
2042            if (adjustVerticalScrollPositionDownward(Math.max(rowCount, 1)))
2043                return;
2044        }
2045
2046        rowCount -= (offscreenExtraRowsTop + offscreenExtraRowsBottom);
2047
2048        var collectionHasRows:Boolean = collection && collection.length > 0;
2049
2050        var colCount:int = (collectionHasRows && rowCount > 0) ? listItems[0].length : visibleColumns.length;
2051
2052        // if the last column is visible and partially offscreen (but it isn't the only
2053        // column) then adjust the column count so we can scroll to see it
2054        if (collectionHasRows && rowCount > 0 && colCount > 1 &&
2055            listItems[0][colCount - 1].x +
2056            visibleColumns[colCount - 1].width > (displayWidth - listContent.x + viewMetrics.left))
2057            colCount--;
2058        else if (colCount > 1 && !collectionHasRows)
2059        {
2060            // the slower computation requires adding up the previous columns
2061            var colX:int = 0;
2062            for (var i:int = 0; i < visibleColumns.length; i++)
2063            {
2064                colX += visibleColumns[i].width;
2065            }
2066            if (colX > (displayWidth - listContent.x + viewMetrics.left))
2067                colCount--;
2068        }
2069
2070        // trace("configureSB", verticalScrollPosition);
2071
2072        setScrollBarProperties(displayableColumns.length - lockedColumnCount, Math.max(colCount, 1),
2073                            collection ? collection.length - lockedRowCount : 0,
2074                            Math.max(rowCount, 1));
2075
2076        if ((!verticalScrollBar || !verticalScrollBar.visible) && collection &&
2077            collection.length - lockedRowCount > rowCount)
2078            maxVerticalScrollPosition = collection.length - lockedRowCount - rowCount;
2079        if ((!horizontalScrollBar || !horizontalScrollBar.visible) &&
2080            displayableColumns.length - lockedColumnCount  > colCount - lockedColumnCount)
2081            maxHorizontalScrollPosition = displayableColumns.length - lockedColumnCount - colCount;
2082    }
2083
2084    /**
2085     *  @private
2086     *  Makes verticalScrollPosition smaller until it is 0 or there
2087     *  are no empty rows.  This is needed if we're scrolled to the
2088     *  bottom and something is deleted or the rows resize so more
2089     *  rows can be shown.
2090     */
2091    private function adjustVerticalScrollPositionDownward(rowCount:int):Boolean
2092    {
2093        var bookmark:CursorBookmark = iterator.bookmark;
2094
2095        // add up how much space we're currently taking with valid items
2096        var h:Number = 0;
2097
2098        var item:IListItemRenderer;
2099        var c:DataGridColumn;
2100        var ch:Number = 0;
2101        var n:int;
2102        var j:int;
2103
2104        var paddingTop:Number = getStyle("paddingTop");
2105        var paddingBottom:Number = getStyle("paddingBottom");
2106
2107        h = rowInfo[rowCount - 1].y + rowInfo[rowCount - 1].height;
2108        h = listContent.heightExcludingOffsets - listContent.topOffset - h;
2109
2110        // back up one
2111        var numRows:int = 0;
2112        try
2113        {
2114            if (iterator.afterLast)
2115                iterator.seek(CursorBookmark.LAST, 0)
2116            else
2117                var bMore:Boolean = iterator.movePrevious();
2118        }
2119        catch(e:ItemPendingError)
2120        {
2121            bMore = false;
2122        }
2123        if (!bMore)
2124        {
2125            // reset to 0;
2126            super.verticalScrollPosition = 0;
2127            try
2128            {
2129                iterator.seek(CursorBookmark.FIRST, 0);
2130                // sometimes, if the iterator is invalid we'll get lucky and succeed
2131                // here, then we have to make the iterator valid again
2132                if (!iteratorValid)
2133                {
2134                    iteratorValid = true;
2135                    lastSeekPending = null;
2136                }
2137            }
2138            catch(e:ItemPendingError)
2139            {
2140                lastSeekPending = new ListBaseSeekPending(CursorBookmark.FIRST, 0);
2141                e.addResponder(new ItemResponder(seekPendingResultHandler, seekPendingFailureHandler,
2142                                                lastSeekPending));
2143                iteratorValid = false;
2144                invalidateList();
2145                return true;
2146            }
2147            updateList();
2148            return true;
2149        }
2150
2151        // now work backwards to see how many more rows we need to create
2152        while (h > 0 && bMore)
2153        {
2154            var data:Object;
2155            if (bMore)
2156            {
2157                data = iterator.current;
2158                ch = 0;
2159                n = columns.length;
2160                for (j = 0; j < n; j++)
2161                {
2162                    c = columns[j];
2163
2164                    if (!c.visible)
2165                        continue;
2166
2167                    if (variableRowHeight)
2168                    {
2169                        item = c.getMeasuringRenderer(false, data);
2170                        if (DisplayObject(item).parent == null)
2171                            listContent.addChild(DisplayObject(item));
2172                        setupRendererFromData(c, item, data);
2173                    }
2174                    ch = Math.max(ch, variableRowHeight ? item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop : rowHeight);
2175                }
2176            }
2177            h -= ch;
2178            try
2179            {
2180                bMore = iterator.movePrevious();
2181                numRows++;
2182            }
2183            catch(e:ItemPendingError)
2184            {
2185                // if we run out of data, assume all remaining rows are the size of the previous row
2186                bMore = false;
2187            }
2188        }
2189        // if we overrun, go back one.
2190        if (h < 0)
2191        {
2192            numRows--;
2193        }
2194
2195        iterator.seek(bookmark, 0);
2196        verticalScrollPosition = Math.max(0, verticalScrollPosition - numRows);
2197
2198        // make sure we get through configureScrollBars w/o coming in here.
2199        if (numRows > 0 && !variableRowHeight)
2200            configureScrollBars();
2201
2202        return (numRows > 0);
2203    }
2204
2205    /**
2206     *  @private
2207     */
2208    override public function calculateDropIndex(event:DragEvent = null):int
2209    {
2210        if (event)
2211        {
2212            var item:IListItemRenderer;
2213            var lastItem:IListItemRenderer;
2214            var pt:Point = new Point(event.localX, event.localY);
2215            pt = DisplayObject(event.target).localToGlobal(pt);
2216            pt = listContent.globalToLocal(pt);
2217
2218            var n:int = listItems.length;
2219            for (var i:int = 0; i < n; i++)
2220            {
2221                if (listItems[i][0])
2222                    lastItem = listItems[i][0];
2223
2224                if (rowInfo[i].y <= pt.y && pt.y < rowInfo[i].y + rowInfo[i].height)
2225                {
2226                    item = listItems[i][0];
2227                    break;
2228                }
2229            }
2230            if (!item && lockedRowContent)
2231            {
2232                pt = listContent.localToGlobal(pt);
2233                pt = lockedRowContent.globalToLocal(pt);
2234                n = lockedRowContent.listItems.length;
2235                for (i = 0; i < n; i++)
2236                {
2237                    if (lockedRowContent.rowInfo[i].y <= pt.y && pt.y < lockedRowContent.rowInfo[i].y + lockedRowContent.rowInfo[i].height)
2238                    {
2239                        item = lockedRowContent.listItems[i][0];
2240                        break;
2241                    }
2242                }
2243            }
2244
2245
2246            if (item)
2247                lastDropIndex = itemRendererToIndex(item);
2248            else
2249            {
2250                if (lastItem)
2251                    lastDropIndex = itemRendererToIndex(lastItem) + 1;
2252                else
2253                    lastDropIndex = collection ? collection.length : 0;
2254            }
2255        }
2256
2257        return lastDropIndex;
2258    }
2259
2260    /**
2261     *  @private
2262     */
2263    override protected function drawRowBackgrounds():void
2264    {
2265        drawRowGraphics(listContent);
2266    }
2267
2268    /**
2269     *  @private
2270     */
2271    protected function drawRowGraphics(contentHolder:ListBaseContentHolder):void
2272    {
2273        var rowBGs:Sprite = Sprite(contentHolder.getChildByName("rowBGs"));
2274        if (!rowBGs)
2275        {
2276            rowBGs = new FlexSprite();
2277            rowBGs.mouseEnabled = false;
2278            rowBGs.name = "rowBGs";
2279            contentHolder.addChildAt(rowBGs, 0);
2280        }
2281
2282        var colors:Array;
2283
2284        colors = getStyle("alternatingItemColors");
2285
2286        if (!colors || colors.length == 0)
2287        {
2288            while (rowBGs.numChildren > n)
2289            {
2290                rowBGs.removeChildAt(rowBGs.numChildren - 1);
2291            }
2292            return;
2293        }
2294
2295        StyleManager.getColorNames(colors);
2296
2297        var curRow:int = 0;
2298
2299        var i:int = 0;
2300        var actualRow:int = verticalScrollPosition;
2301        var n:int = contentHolder.listItems.length;
2302
2303        while (curRow < n)
2304        {
2305            drawRowBackground(rowBGs, i++, contentHolder.rowInfo[curRow].y, contentHolder.rowInfo[curRow].height,
2306                colors[actualRow % colors.length], actualRow);
2307            curRow++;
2308            actualRow++;
2309        }
2310
2311        while (rowBGs.numChildren > i)
2312        {
2313            rowBGs.removeChildAt(rowBGs.numChildren - 1);
2314        }
2315    }
2316
2317    /**
2318     *  @private
2319     */
2320    override protected function mouseEventToItemRenderer(event:MouseEvent):IListItemRenderer
2321    {
2322        var r:IListItemRenderer;
2323
2324        r = super.mouseEventToItemRenderer(event);
2325
2326        return r == itemEditorInstance ? null : r;
2327    }
2328
2329    /**
2330     *  @private
2331     */
2332    override protected function get dragImage():IUIComponent
2333    {
2334        var image:DataGridDragProxy = new DataGridDragProxy();
2335        image.owner = this;
2336        image.moduleFactory = moduleFactory;
2337        return image;
2338    }
2339
2340    //--------------------------------------------------------------------------
2341    //
2342    //  Methods
2343    //
2344    //--------------------------------------------------------------------------
2345
2346
2347    /**
2348     *  @private
2349     *  Move a column to a new position in the columns array, shifting all
2350     *  other columns left or right and updating the sortIndex and
2351     *  lastSortIndex variables accordingly.
2352     */
2353    mx_internal function shiftColumns(oldIndex:int, newIndex:int,
2354                                      trigger:Event = null):void
2355    {
2356        if (newIndex >= 0 && oldIndex != newIndex)
2357        {
2358            var incr:int = oldIndex < newIndex ? 1 : -1;
2359            for (var i:int = oldIndex; i != newIndex; i += incr)
2360            {
2361                var j:int = i + incr;
2362                var c:DataGridColumn = _columns[i];
2363                _columns[i] = _columns[j];
2364                _columns[j] = c;
2365                _columns[i].colNum = i;
2366                _columns[j].colNum = j;
2367            }
2368
2369            if (sortIndex == oldIndex)
2370                sortIndex += newIndex - oldIndex;
2371            else if ((oldIndex < sortIndex && sortIndex <= newIndex)
2372                    || (newIndex <= sortIndex && sortIndex < oldIndex))
2373                sortIndex -= incr;
2374
2375            if (lastSortIndex == oldIndex)
2376                lastSortIndex += newIndex - oldIndex;
2377            else if ((oldIndex < lastSortIndex
2378                        && lastSortIndex <= newIndex)
2379                    || (newIndex <= lastSortIndex
2380                        && lastSortIndex < oldIndex))
2381                lastSortIndex -= incr;
2382
2383            columnsInvalid = true;
2384            itemsSizeChanged = true;
2385            invalidateDisplayList();
2386            if (lockedColumnHeader)
2387                lockedColumnHeader.invalidateDisplayList();
2388
2389            var icEvent:IndexChangedEvent =
2390                new IndexChangedEvent(IndexChangedEvent.HEADER_SHIFT);
2391            icEvent.oldIndex = oldIndex;
2392            icEvent.newIndex = newIndex;
2393            icEvent.triggerEvent = trigger;
2394            dispatchEvent(icEvent);
2395        }
2396    }
2397
2398    /**
2399     *  @private
2400     *  Searches the iterator to determine columns.
2401     */
2402    private function generateCols():void
2403    {
2404        if (collection.length > 0)
2405        {
2406            var col:DataGridColumn;
2407            var newCols:Array = [];
2408            var cols:Array;
2409            if (dataProvider)
2410            {
2411                try
2412                {
2413                    iterator.seek(CursorBookmark.FIRST);
2414                    if (!iteratorValid)
2415                    {
2416                        iteratorValid = true;
2417                        lastSeekPending = null;
2418                    }
2419                }
2420                catch(e:ItemPendingError)
2421                {
2422                    lastSeekPending = new ListBaseSeekPending(CursorBookmark.FIRST, 0);
2423                    e.addResponder(new ItemResponder(generateColumnsPendingResultHandler, seekPendingFailureHandler,
2424                                                    lastSeekPending));
2425                    iteratorValid = false;
2426                    return;
2427                }
2428                var info:Object =
2429                    ObjectUtil.getClassInfo(iterator.current,
2430                                            ["uid", "mx_internal_uid"]);
2431
2432                if (info)
2433                    cols = info.properties;
2434            }
2435
2436            if (!cols)
2437            {
2438                // introspect the first item and use its fields
2439                var itmObj:Object = iterator.current;
2440                for (var p:String in itmObj)
2441                {
2442                    if (p != "uid")
2443                    {
2444                        col = new DataGridColumn();
2445                        col.dataField = p;
2446                        newCols.push(col);
2447                    }
2448                }
2449            }
2450            else
2451            {
2452                // this is an old recordset - use its columns
2453                var n:int = cols.length;
2454                var colName:Object;
2455                for (var i:int = 0; i < n; i++)
2456                {
2457                    colName = cols[i];
2458                    if (colName is QName)
2459                        colName = QName(colName).localName;
2460                    col = new DataGridColumn();
2461                    col.dataField = String(colName);
2462                    newCols.push(col);
2463                }
2464            }
2465            columns = newCols;
2466            generatedColumns = true;
2467        }
2468    }
2469
2470    /**
2471     *  @private
2472     */
2473    private function generateColumnsPendingResultHandler(data:Object, info:ListBaseSeekPending):void
2474    {
2475        // generate cols if we haven't successfully generated them
2476        if (columns.length == 0)
2477            generateCols();
2478        seekPendingResultHandler(data, info);
2479    }
2480
2481    /**
2482     *  @private
2483     */
2484    private function calculateColumnSizes():void
2485    {
2486        var delta:Number;
2487        var n:int;
2488        var i:int;
2489        var totalWidth:Number = 0;
2490        var col:DataGridColumn;
2491        var cw:Number;
2492
2493        if (columns.length == 0)
2494        {
2495            visibleColumns = [];
2496            visibleLockedColumns = [];
2497            lockedColumnWidth = 0;
2498            columnsInvalid = false;
2499            return;
2500        }
2501
2502        // no columns are visible so figure out which ones
2503        // to make visible
2504        if (columnsInvalid)
2505        {
2506            columnsInvalid = false;
2507            visibleColumns = [];
2508            visibleLockedColumns = [];
2509            lockedColumnWidth = 0;
2510
2511            if (minColumnWidthInvalid)
2512            {
2513                n = columns.length;
2514                for (i = 0; i < n; i++)
2515                {
2516                    columns[i].minWidth = minColumnWidth;
2517                }
2518                minColumnWidthInvalid = false;
2519            }
2520
2521            displayableColumns = null;
2522            n = _columns.length;
2523            for (i = 0; i < n; i++)
2524            {
2525                if (displayableColumns && _columns[i].visible)
2526                {
2527                    displayableColumns.push(_columns[i]);
2528                }
2529                else if (!displayableColumns && !_columns[i].visible)
2530                {
2531                    displayableColumns = new Array(i);
2532                    for (var k:int = 0; k < i; k++)
2533                        displayableColumns[k] = _columns[k];
2534                }
2535            }
2536
2537            // If there are no hidden columns, displayableColumns points to
2538            // _columns (we don't need a duplicate copy of _columns).
2539            if (!displayableColumns)
2540                displayableColumns = _columns;
2541
2542            // if no hscroll, then pack columns in available space
2543            if (horizontalScrollPolicy == ScrollPolicy.OFF)
2544            {
2545                n = displayableColumns.length;
2546                for (i = 0; i < n; i++)
2547                {
2548                    col = displayableColumns[i];
2549
2550                    if (i < lockedColumnCount)
2551                    {
2552                        visibleLockedColumns.push(col);
2553                    }
2554                    else
2555                        visibleColumns.push(col);
2556                }
2557            }
2558            else
2559            {
2560                n = displayableColumns.length;
2561                for (i = 0; i < n; i++)
2562                {
2563                    if (i >= lockedColumnCount &&
2564                        i < lockedColumnCount + horizontalScrollPosition)
2565                    {
2566                        continue;
2567                    }
2568
2569                    col = displayableColumns[i];
2570                    if (col.preferredWidth < col.minWidth)
2571                        col.preferredWidth = col.minWidth;
2572
2573                    if (totalWidth < displayWidth)
2574                    {
2575                        if (i < lockedColumnCount)
2576                        {
2577                            lockedColumnWidth += Math.max(isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth, col.minWidth);
2578                            visibleLockedColumns.push(col);
2579                        }
2580                        else
2581                            visibleColumns.push(col);
2582                        totalWidth += Math.max(isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth, col.minWidth);
2583                        if (col.width != col.preferredWidth)
2584                            col.setWidth(col.preferredWidth);
2585                    }
2586                    else
2587                    {
2588                        if (visibleColumns.length == 0)
2589                            visibleColumns.push(displayableColumns[0]);
2590                        break;
2591                    }
2592                }
2593            }
2594        }
2595
2596        var lastColumn:DataGridColumn;
2597        var newSize:Number;
2598
2599        // if no hscroll, then pack columns in available space
2600        if (horizontalScrollPolicy == ScrollPolicy.OFF)
2601        {
2602            var numResizable:int = 0;
2603            var fixedWidth:Number = 0;
2604
2605            // trace("resizing columns");
2606
2607            // count how many resizable columns and how wide they are
2608            n = visibleColumns.length;
2609            for (i = 0; i < n; i++)
2610            {
2611                // trace("column " + i + " width = " + visibleColumns[i].width);
2612                if (visibleColumns[i].resizable && !visibleColumns[i].newlyVisible)
2613                {
2614                    // trace("    resizable");
2615                    if (!isNaN(visibleColumns[i].explicitWidth))
2616                    {
2617                        // trace("    explicit width " + visibleColumns[i].width);
2618                        fixedWidth += visibleColumns[i].width;
2619                    }
2620                    else
2621                    {
2622                        // trace("    implicitly resizable");
2623                        numResizable++;
2624                        fixedWidth += visibleColumns[i].minWidth;
2625                        // trace("    minWidth " + visibleColumns[i].minWidth);
2626                    }
2627                }
2628                else
2629                {
2630                    // trace("    not resizable");
2631                    fixedWidth += visibleColumns[i].width;
2632                }
2633
2634                totalWidth += visibleColumns[i].width;
2635            }
2636            n = visibleLockedColumns.length;
2637            for (i = 0; i < n; i++)
2638            {
2639                // trace("column " + i + " width = " + visibleLockedColumns[i].width);
2640                if (visibleLockedColumns[i].resizable && !visibleLockedColumns[i].newlyVisible)
2641                {
2642                    // trace("    resizable");
2643                    if (!isNaN(visibleLockedColumns[i].explicitWidth))
2644                    {
2645                        // trace("    explicit width " + visibleLockedColumns[i].width);
2646                        fixedWidth += visibleLockedColumns[i].width;
2647                    }
2648                    else
2649                    {
2650                        // trace("    implicitly resizable");
2651                        numResizable++;
2652                        fixedWidth += visibleLockedColumns[i].minWidth;
2653                        // trace("    minWidth " + visibleLockedColumns[i].minWidth);
2654                    }
2655                }
2656                else
2657                {
2658                    // trace("    not resizable");
2659                    fixedWidth += visibleLockedColumns[i].width;
2660                }
2661
2662                totalWidth += visibleLockedColumns[i].width;
2663            }
2664            // trace("totalWidth = " + totalWidth);
2665            // trace("displayWidth = " + displayWidth);
2666
2667            var ratio:Number;
2668            var newTotal:Number = displayWidth;
2669            var minWidth:Number;
2670            if (displayWidth > fixedWidth && numResizable)
2671            {
2672                // we have flexible columns and room to honor minwidths and non-resizable
2673                // trace("have enough room");
2674
2675                // divide and distribute the excess among the resizable
2676                n = visibleLockedColumns.length;
2677                for (i = 0; i < n; i++)
2678                {
2679                    if (visibleLockedColumns[i].resizable && !visibleLockedColumns[i].newlyVisible && isNaN(visibleLockedColumns[i].explicitWidth))
2680                    {
2681                        lastColumn = visibleLockedColumns[i];
2682                        if (totalWidth > displayWidth)
2683                            ratio = (lastColumn.width - lastColumn.minWidth)/ (totalWidth - fixedWidth);
2684                        else
2685                            ratio = lastColumn.width / totalWidth;
2686                        newSize = lastColumn.width - (totalWidth - displayWidth) * ratio;
2687                        minWidth = visibleLockedColumns[i].minWidth;
2688                        visibleLockedColumns[i].setWidth(newSize > minWidth ? newSize : minWidth);
2689                        // trace("column " + i + " set to " + visibleLockedColumns[i].width);
2690                    }
2691                    newTotal -= visibleLockedColumns[i].width;
2692                    visibleLockedColumns[i].newlyVisible = false;
2693                }
2694                n = visibleColumns.length;
2695                for (i = 0; i < n; i++)
2696                {
2697                    if (visibleColumns[i].resizable && !visibleColumns[i].newlyVisible && isNaN(visibleColumns[i].explicitWidth))
2698                    {
2699                        lastColumn = visibleColumns[i];
2700                        if (totalWidth > displayWidth)
2701                            ratio = (lastColumn.width - lastColumn.minWidth)/ (totalWidth - fixedWidth);
2702                        else
2703                            ratio = lastColumn.width / totalWidth;
2704                        newSize = lastColumn.width - (totalWidth - displayWidth) * ratio;
2705                        minWidth = visibleColumns[i].minWidth;
2706                        visibleColumns[i].setWidth(newSize > minWidth ? newSize : minWidth);
2707                        // trace("column " + i + " set to " + visibleColumns[i].width);
2708                    }
2709                    newTotal -= visibleColumns[i].width;
2710                    visibleColumns[i].newlyVisible = false;
2711                }
2712                if (newTotal && lastColumn)
2713                {
2714                    // trace("excess = " + newTotal);
2715                    lastColumn.setWidth(lastColumn.width + newTotal);
2716                }
2717            }
2718            else // can't honor minwidth and non-resizables so just scale everybody
2719            {
2720                // trace("too small or too big");
2721                n = visibleLockedColumns.length;
2722                for (i = 0; i < n; i++)
2723                {
2724                    lastColumn = visibleLockedColumns[i];
2725                    ratio = lastColumn.width / totalWidth;
2726                    //totalWidth -= visibleLockedColumns[i].width;
2727                    newSize = displayWidth * ratio;
2728                    lastColumn.setWidth(newSize);
2729                    lastColumn.explicitWidth = NaN;
2730                    // trace("column " + i + " set to " + visibleLockedColumns[i].width);
2731                    newTotal -= newSize;
2732                }
2733                n = visibleColumns.length;
2734                for (i = 0; i < n; i++)
2735                {
2736                    lastColumn = visibleColumns[i];
2737                    ratio = lastColumn.width / totalWidth;
2738                    //totalWidth -= visibleColumns[i].width;
2739                    newSize = displayWidth * ratio;
2740                    lastColumn.setWidth(newSize);
2741                    lastColumn.explicitWidth = NaN;
2742                    // trace("column " + i + " set to " + visibleColumns[i].width);
2743                    newTotal -= newSize;
2744                }
2745                if (newTotal && lastColumn)
2746                {
2747                    // trace("excess = " + newTotal);
2748                    lastColumn.setWidth(lastColumn.width + newTotal);
2749                }
2750            }
2751        }
2752        else // we have or can have an horizontalScrollBar
2753        {
2754            totalWidth = 0;
2755            // drop any that completely overflow
2756            n = visibleColumns.length;
2757            for (i = 0; i < n; i++)
2758            {
2759                if (totalWidth > displayWidth - lockedColumnWidth)
2760                {
2761                    visibleColumns.splice(i);
2762                    break;
2763                }
2764                totalWidth += isNaN(visibleColumns[i].explicitWidth) ? visibleColumns[i].preferredWidth : visibleColumns[i].explicitWidth;
2765            }
2766
2767            if (visibleColumns.length == 0)
2768                return;
2769
2770            i = visibleColumns[visibleColumns.length - 1].colNum + 1;
2771            // add more if we have room
2772            if (totalWidth < displayWidth - lockedColumnWidth && i < displayableColumns.length)
2773            {
2774                n = displayableColumns.length;
2775                for (; i < n && totalWidth < displayWidth - lockedColumnWidth; i++)
2776                {
2777                    col = displayableColumns[i];
2778
2779                    visibleColumns.push(col);
2780                    totalWidth += isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth;
2781                }
2782            }
2783            else if (totalWidth < displayWidth - lockedColumnWidth && horizontalScrollPosition > 0)
2784            {
2785                while (totalWidth < displayWidth - lockedColumnWidth && horizontalScrollPosition > 0)
2786                {
2787                    col = displayableColumns[lockedColumnCount + horizontalScrollPosition - 1];
2788                    cw = isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth;
2789                    if (cw < displayWidth - lockedColumnWidth - totalWidth)
2790                    {
2791                        visibleColumns.splice(0, 0, col);
2792                        super.horizontalScrollPosition--;
2793                        totalWidth += cw;
2794                    }
2795                    else
2796                        break;
2797                }
2798            }
2799
2800            lastColumn = visibleColumns[visibleColumns.length - 1];
2801            cw = isNaN(lastColumn.explicitWidth) ? lastColumn.preferredWidth : lastColumn.explicitWidth;
2802            newSize = cw + displayWidth - lockedColumnWidth - totalWidth;
2803            if (lastColumn == displayableColumns[displayableColumns.length - 1]
2804                && lastColumn.resizable
2805                && newSize >= lastColumn.minWidth
2806                && newSize > cw)
2807            {
2808                lastColumn.setWidth(newSize);
2809                maxHorizontalScrollPosition =
2810                    displayableColumns.length - visibleColumns.length;
2811            }
2812            else if (visibleColumns.length == 1 &&
2813                    lastColumn == displayableColumns[displayableColumns.length - 1])
2814            {
2815                maxHorizontalScrollPosition =
2816                    displayableColumns.length - visibleColumns.length;
2817            }
2818            else
2819            {
2820                maxHorizontalScrollPosition =
2821                    displayableColumns.length - visibleColumns.length + 1;
2822            }
2823        }
2824        lockedColumnWidth = 0;
2825        if (visibleLockedColumns.length)
2826        {
2827            n = visibleLockedColumns.length;
2828            for (i = 0; i < n; i++)
2829            {
2830                col = visibleLockedColumns[i];
2831                lockedColumnWidth += col.width;
2832            }
2833        }
2834    }
2835
2836    /**
2837     *  @private
2838     *  If there is no horizontal scroll bar, changes the display width of other columns when
2839     *  one column's width is changed.
2840     *  @param col column whose width is changed
2841     *  @param w width of column
2842     */
2843    override mx_internal function resizeColumn(col:int, w:Number):void
2844    {
2845        // there's a window of time before we calccolumnsizes
2846        // that someone can set width in AS
2847        if ((!visibleColumns || visibleColumns.length == 0) && (!visibleLockedColumns || visibleLockedColumns.length == 0))
2848        {
2849            _columns[col].setWidth(w);
2850            _columns[col].preferredWidth = w;
2851            return;
2852        }
2853
2854        if (w < _columns[col].minWidth)
2855            w = _columns[col].minWidth;
2856
2857        // hScrollBar is present
2858        if (_horizontalScrollPolicy == ScrollPolicy.ON ||
2859            _horizontalScrollPolicy == ScrollPolicy.AUTO)
2860        {
2861            // adjust the column's width
2862            _columns[col].setWidth(w);
2863            _columns[col].explicitWidth = w;
2864            _columns[col].preferredWidth = w;
2865            columnsInvalid = true;
2866        }
2867        else
2868        {
2869            // find the columns in the set of visible columns;
2870            var n:int = _columns.length;
2871            var i:int;
2872            for (i = 0; i < n; i++)
2873            {
2874                if (col == _columns[i].colNum)
2875                    break;
2876            }
2877            if (i >= _columns.length - 1)   // no resize of right most column
2878                return;
2879            col = i;
2880
2881            // we want all cols's new widths to the right of this to be in proportion
2882            // to what they were before the stretch.
2883
2884            // get the original space to the right not taken up by the column
2885            var totalSpace:Number = 0;
2886            var lastColumn:DataGridColumn;
2887            var newWidth:Number;
2888            //non-resizable columns don't count though
2889            for (i = col + 1; i < n; i++)
2890            {
2891                if (_columns[i].visible)
2892                    if (_columns[i].resizable)
2893                        totalSpace += _columns[i].width;
2894            }
2895
2896            var newTotalSpace:Number = _columns[col].width - w + totalSpace;
2897            if (totalSpace)
2898            {
2899                _columns[col].setWidth(w);
2900                _columns[col].explicitWidth = w;
2901            }
2902
2903            var totX:Number = 0;
2904            // resize the columns to the right proportionally to what they were
2905            for (i = col + 1; i < n; i++)
2906            {
2907                if (_columns[i].visible)
2908                    if (_columns[i].resizable)
2909                    {
2910                        newWidth = Math.floor(_columns[i].width
2911                                                    * newTotalSpace / totalSpace);
2912                        if (newWidth < _columns[i].minWidth)
2913                            newWidth = _columns[i].minWidth;
2914                        _columns[i].setWidth(newWidth);
2915                        totX += _columns[i].width;
2916                        lastColumn = _columns[i];
2917                    }
2918            }
2919
2920            if (totX > newTotalSpace)
2921            {
2922                // if excess then should be taken out only from changing column
2923                // cause others would have already gone to their minimum
2924                newWidth = _columns[col].width - totX + newTotalSpace;
2925                if (newWidth < _columns[col].minWidth)
2926                    newWidth = _columns[col].minWidth;
2927                _columns[col].setWidth(newWidth);
2928            }
2929            else if (lastColumn)
2930            {
2931                // if less then should be added in last column
2932                // dont need to check for minWidth as we are adding
2933                lastColumn.setWidth(lastColumn.width - totX + newTotalSpace);
2934            }
2935        }
2936        itemsSizeChanged = true
2937
2938        invalidateDisplayList();
2939    }
2940
2941    /**
2942     *  Draws a row background
2943     *  at the position and height specified using the
2944     *  color specified.  This implementation creates a Shape as a
2945     *  child of the input Sprite and fills it with the appropriate color.
2946     *  This method also uses the <code>backgroundAlpha</code> style property
2947     *  setting to determine the transparency of the background color.
2948     *
2949     *  @param s A Sprite that will contain a display object
2950     *  that contains the graphics for that row.
2951     *
2952     *  @param rowIndex The row's index in the set of displayed rows.  The
2953     *  header does not count, the top most visible row has a row index of 0.
2954     *  This is used to keep track of the objects used for drawing
2955     *  backgrounds so a particular row can re-use the same display object
2956     *  even though the index of the item that row is rendering has changed.
2957     *
2958     *  @param y The suggested y position for the background
2959     *
2960     *  @param height The suggested height for the indicator
2961     *
2962     *  @param color The suggested color for the indicator
2963     *
2964     *  @param dataIndex The index of the item for that row in the
2965     *  data provider.  This can be used to color the 10th item differently
2966     *  for example.
2967     */
2968    protected function drawRowBackground(s:Sprite, rowIndex:int,
2969                                            y:Number, height:Number, color:uint, dataIndex:int):void
2970    {
2971        var contentHolder:ListBaseContentHolder = ListBaseContentHolder(s.parent);
2972
2973        var background:Shape;
2974        if (rowIndex < s.numChildren)
2975        {
2976            background = Shape(s.getChildAt(rowIndex));
2977        }
2978        else
2979        {
2980            background = new FlexShape();
2981            background.name = "background";
2982            s.addChild(background);
2983        }
2984
2985        background.y = y;
2986
2987        // Height is usually as tall is the items in the row, but not if
2988        // it would extend below the bottom of listContent
2989        var height:Number = Math.min(height,
2990                                     contentHolder.height -
2991                                     y);
2992
2993        var g:Graphics = background.graphics;
2994        g.clear();
2995        g.beginFill(color, getStyle("backgroundAlpha"));
2996        g.drawRect(0, 0, contentHolder.width, height);
2997        g.endFill();
2998    }
2999
3000    /**
3001     *  Draws a column background for a column with the suggested color.
3002     *  This implementation creates a Shape as a
3003     *  child of the input Sprite and fills it with the appropriate color.
3004     *
3005     *  @param s A Sprite that will contain a display object
3006     *  that contains the graphics for that column.
3007     *
3008     *  @param columnIndex The column's index in the set of displayed columns.
3009     *  The left most visible column has a column index of 0.
3010     *  This is used to keep track of the objects used for drawing
3011     *  backgrounds so a particular column can re-use the same display object
3012     *  even though the index of the DataGridColumn for that column has changed.
3013     *
3014     *  @param color The suggested color for the indicator
3015     *
3016     *  @param column The column of the DataGrid control that you are drawing the background for.
3017     */
3018    protected function drawColumnBackground(s:Sprite, columnIndex:int,
3019                                            color:uint, column:DataGridColumn):void
3020    {
3021        var background:Shape;
3022        background = Shape(s.getChildByName(columnIndex.toString()));
3023        if (!background)
3024        {
3025            background = new FlexShape();
3026            s.addChild(background);
3027            background.name = columnIndex.toString();
3028        }
3029
3030        var g:Graphics = background.graphics;
3031        g.clear();
3032        g.beginFill(color);
3033
3034        var lastRow:Object = rowInfo[listItems.length - 1];
3035        var columnHeader:DataGridHeader = (s.parent == lockedColumnContent) ?
3036                                        DataGridHeader(lockedColumnHeader) :
3037                                        DataGridHeader(header);
3038
3039        var xx:Number = columnHeader.rendererArray[columnIndex].x
3040        var yy:Number = rowInfo[0].y
3041
3042        // Height is usually as tall is the items in the row, but not if
3043        // it would extend below the bottom of listContent
3044        var height:Number = Math.min(lastRow.y + lastRow.height,
3045                                     listContent.height - yy);
3046
3047        g.drawRect(xx, yy, columnHeader.visibleColumns[columnIndex].width,
3048                   listContent.height - yy);
3049        g.endFill();
3050    }
3051
3052    /**
3053     *  Creates and sizes the horizontalSeparator skins. If none have been specified, then draws the lines using
3054     *  drawHorizontalLine().
3055     */
3056    private function drawHorizontalSeparator(s:Sprite, rowIndex:int, color:uint, y:Number, useLockedSeparator:Boolean = false):void
3057    {
3058        var hSepSkinName:String = "hSeparator" + rowIndex;
3059        var hLockedSepSkinName:String = "hLockedSeparator" + rowIndex;
3060        var createThisSkinName:String = useLockedSeparator ? hLockedSepSkinName : hSepSkinName;
3061        var createThisStyleName:String = useLockedSeparator ? "horizontalLockedSeparatorSkin" : "horizontalSeparatorSkin";
3062
3063        var sepSkin:IFlexDisplayObject;
3064        var lockedSepSkin:IFlexDisplayObject;
3065        var deleteThisSkin:IFlexDisplayObject;
3066        var createThisSkin:IFlexDisplayObject;
3067
3068        // Look for separator by name
3069        sepSkin = IFlexDisplayObject(s.getChildByName(hSepSkinName));
3070        lockedSepSkin = IFlexDisplayObject(s.getChildByName(hLockedSepSkinName));
3071
3072        createThisSkin = useLockedSeparator ? lockedSepSkin : sepSkin;
3073        deleteThisSkin = useLockedSeparator ? sepSkin : lockedSepSkin;
3074
3075        if (deleteThisSkin)
3076        {
3077            s.removeChild(DisplayObject(deleteThisSkin));
3078            //delete deleteThisSkin;
3079        }
3080
3081        if (!createThisSkin)
3082        {
3083            var sepSkinClass:Class = Class(getStyle(createThisStyleName));
3084
3085            if (sepSkinClass)
3086            {
3087                createThisSkin = IFlexDisplayObject(new sepSkinClass());
3088                createThisSkin.name = createThisSkinName;
3089
3090                var styleableSkin:ISimpleStyleClient = createThisSkin as ISimpleStyleClient;
3091                if (styleableSkin)
3092                    styleableSkin.styleName = this;
3093
3094                s.addChild(DisplayObject(createThisSkin));
3095            }
3096        }
3097
3098        if (createThisSkin)
3099        {
3100            var mHeight:Number = !isNaN(createThisSkin.measuredHeight) ? createThisSkin.measuredHeight : 1;
3101            createThisSkin.setActualSize(displayWidth - lockedColumnWidth, mHeight);
3102            createThisSkin.move(0, y);
3103        }
3104        else // If we still don't have a sepSkin, then we have no skin style defined. Use the default function instead
3105        {
3106            drawHorizontalLine(s, rowIndex, color, y);
3107        }
3108
3109    }
3110
3111    /**
3112     *  Draws a line between rows.  This implementation draws a line
3113     *  directly into the given Sprite.  The Sprite has been cleared
3114     *  before lines are drawn into it.
3115     *
3116     *  @param s A Sprite that will contain a display object
3117     *  that contains the graphics for that row.
3118     *
3119     *  @param rowIndex The row's index in the set of displayed rows.  The
3120     *  header does not count, the top most visible row has a row index of 0.
3121     *  This is used to keep track of the objects used for drawing
3122     *  backgrounds so a particular row can re-use the same display object
3123     *  even though the index of the item that row is rendering has changed.
3124     *
3125     *  @param color The suggested color for the indicator
3126     *
3127     *  @param y The suggested y position for the background
3128     */
3129    protected function drawHorizontalLine(s:Sprite, rowIndex:int, color:uint, y:Number):void
3130    {
3131        var contentHolder:ListBaseContentHolder = s.parent.parent as ListBaseContentHolder;
3132        var g:Graphics = s.graphics;
3133
3134        g.lineStyle(1, color);
3135        g.moveTo(0, y);
3136        g.lineTo(contentHolder.width, y);
3137    }
3138
3139    /**
3140     *  Creates and sizes the verticalSeparator skins. If none have been specified, then draws the lines using
3141     *  drawVerticalLine().
3142     */
3143    private function drawVerticalSeparator(s:Sprite, colIndex:int, color:uint, x:Number, useLockedSeparator:Boolean = false):void
3144    {
3145        var vSepSkinName:String = "vSeparator" + colIndex;
3146        var vLockedSepSkinName:String = "vLockedSeparator" + colIndex;
3147        var createThisSkinName:String = useLockedSeparator ? vLockedSepSkinName : vSepSkinName;
3148        var createThisStyleName:String = useLockedSeparator ? "verticalLockedSeparatorSkin" : "verticalSeparatorSkin";
3149
3150        var sepSkin:IFlexDisplayObject;
3151        var lockedSepSkin:IFlexDisplayObject;
3152        var deleteThisSkin:IFlexDisplayObject;
3153        var createThisSkin:IFlexDisplayObject;
3154
3155        // Look for separator by name
3156        sepSkin = IFlexDisplayObject(s.getChildByName(vSepSkinName));
3157        lockedSepSkin = IFlexDisplayObject(s.getChildByName(vLockedSepSkinName));
3158
3159        createThisSkin = useLockedSeparator ? lockedSepSkin : sepSkin;
3160        deleteThisSkin = useLockedSeparator ? sepSkin : lockedSepSkin;
3161
3162        if (deleteThisSkin)
3163        {
3164            s.removeChild(DisplayObject(deleteThisSkin));
3165            //delete deleteThisSkin;
3166        }
3167
3168        if (!createThisSkin)
3169        {
3170            var sepSkinClass:Class = Class(getStyle(createThisStyleName));
3171
3172            if (sepSkinClass)
3173            {
3174                createThisSkin = IFlexDisplayObject(new sepSkinClass());
3175                createThisSkin.name = createThisSkinName;
3176
3177                var styleableSkin:ISimpleStyleClient = createThisSkin as ISimpleStyleClient;
3178                if (styleableSkin)
3179                    styleableSkin.styleName = this;
3180
3181                s.addChild(DisplayObject(createThisSkin));
3182            }
3183        }
3184
3185        if (createThisSkin)
3186        {
3187            var mWidth:Number = !isNaN(createThisSkin.measuredWidth) ? createThisSkin.measuredWidth : 1;
3188            createThisSkin.setActualSize(mWidth, s.parent.parent.height);
3189            createThisSkin.move(x - Math.round(mWidth / 2), 0);
3190        }
3191        else // If we still don't have a sepSkin, then we have no skin style defined. Use the default function instead
3192        {
3193            drawVerticalLine(s, colIndex, color, x);
3194        }
3195
3196    }
3197
3198    /**
3199     *  Draw lines between columns.  This implementation draws a line
3200     *  directly into the given Sprite.  The Sprite has been cleared
3201     *  before lines are drawn into it.
3202     *
3203     *  @param s A Sprite that will contain a display object
3204     *  that contains the graphics for that row.
3205     *
3206     *  @param columnIndex The column's index in the set of displayed columns.
3207     *  The left most visible column has a column index of 0.
3208     *
3209     *  @param color The suggested color for the indicator
3210     *
3211     *  @param x The suggested x position for the background
3212     */
3213    protected function drawVerticalLine(s:Sprite, colIndex:int, color:uint, x:Number):void
3214    {
3215        var contentHolder:ListBaseContentHolder = s.parent.parent as ListBaseContentHolder;
3216        //draw our vertical lines
3217        var g:Graphics = s.graphics;
3218        g.lineStyle(1, color, 100);
3219        g.moveTo(x, headerVisible ? 0 : 1);
3220        g.lineTo(x, contentHolder.height);
3221    }
3222
3223    /**
3224     *  Draw lines between columns, and column backgrounds.
3225     *  This implementation calls the <code>drawHorizontalLine()</code>,
3226     *  <code>drawVerticalLine()</code>,
3227     *  and <code>drawColumnBackground()</code> methods as needed.
3228     *  It creates a
3229     *  Sprite that contains all of these graphics and adds it as a
3230     *  child of the listContent at the front of the z-order.
3231     */
3232    protected function drawLinesAndColumnBackgrounds():void
3233    {
3234        drawLinesAndColumnGraphics(listContent, visibleColumns, {});
3235    }
3236
3237    /**
3238     *  Draw lines between columns, and column backgrounds.
3239     *  This implementation calls the <code>drawHorizontalLine()</code>,
3240     *  <code>drawVerticalLine()</code>,
3241     *  and <code>drawColumnBackground()</code> methods as needed.
3242     *  It creates a
3243     *  Sprite that contains all of these graphics and adds it as a
3244     *  child of the listContent at the front of the z-order.
3245     *
3246     *  @param contentHolder A container of all of the DataGrid's item renderers and item editors.
3247     *  @param visibleColumns An array of the visible columns in the DataGrid.
3248     *  @param separators An object that defines the top, bottom, left, and right lines that separate the columns and rows.
3249     */
3250    protected function drawLinesAndColumnGraphics(contentHolder:ListBaseContentHolder, visibleColumns:Array, separators:Object):void
3251    {
3252        var lines:Sprite = Sprite(contentHolder.getChildByName("lines"));
3253        if (!lines)
3254        {
3255            lines = new UIComponent();
3256            lines.name = "lines";
3257            lines.cacheAsBitmap = true;
3258            lines.mouseEnabled = false;
3259            contentHolder.addChild(lines);
3260        }
3261        contentHolder.setChildIndex(lines, contentHolder.numChildren - 1);
3262        var rowInfo:Array = contentHolder.rowInfo;
3263
3264        lines.graphics.clear();
3265
3266        var linesBody:Sprite = Sprite(lines.getChildByName("body"));
3267
3268        if (!linesBody)
3269        {
3270            linesBody = new UIComponent();
3271            linesBody.name = "body";
3272            linesBody.mouseEnabled = false;
3273            lines.addChild(linesBody);
3274        }
3275
3276        linesBody.graphics.clear();
3277        while (linesBody.numChildren)
3278        {
3279            linesBody.removeChildAt(0);
3280        }
3281
3282
3283        var tmpHeight:Number = unscaledHeight - 1; // FIXME: can remove?
3284        var lineCol:uint;
3285
3286        var i:int;
3287
3288        var len:uint = visibleColumns ? visibleColumns.length : 0;
3289        var rowlen:uint = contentHolder.listItems.length
3290
3291        // draw horizontalGridlines if needed.
3292        lineCol = getStyle("horizontalGridLineColor");
3293        if (getStyle("horizontalGridLines"))
3294        {
3295            for (i = 0; i < rowlen; i++)
3296            {
3297                var yy:Number = rowInfo[i].y + rowInfo[i].height;
3298                if (yy < contentHolder.height)
3299                    drawHorizontalSeparator(linesBody, i, lineCol, yy);
3300            }
3301        }
3302        if (separators.top)
3303            drawHorizontalSeparator(linesBody, i++, 0, rowInfo[0].y, true);
3304        if (separators.bottom && rowlen > 0)
3305            drawHorizontalSeparator(linesBody, i++, 0, rowInfo[rowlen - 1].y + rowInfo[rowlen - 1].height, true);
3306
3307        var vLines:Boolean = getStyle("verticalGridLines");
3308        lineCol = getStyle("verticalGridLineColor");
3309
3310        if (len)
3311        {
3312            var colBGs:Sprite = Sprite(contentHolder.getChildByName("colBGs"));
3313            // traverse the columns, set the sizes, draw the column backgrounds
3314            var lastChild:int = -1;
3315            var xx:Number = 0;
3316            for (i = 0; i < len; i++)
3317            {
3318                // only draw the vertical separator for the ones in the middle (not beginning and not end)
3319                if (vLines && i < (len - 1))
3320                    drawVerticalSeparator(linesBody, i, lineCol, xx + visibleColumns[i].width);
3321
3322                var col:DataGridColumn = visibleColumns[i];
3323                var bgCol:Object;
3324                if (enabled)
3325                    bgCol = col.getStyle("backgroundColor");
3326                else
3327                    bgCol = col.getStyle("backgroundDisabledColor");
3328
3329                if (bgCol !== null && !isNaN(Number(bgCol)))
3330                {
3331                    if (!colBGs)
3332                    {
3333                        colBGs = new FlexSprite();
3334                        colBGs.mouseEnabled = false;
3335                        colBGs.name = "colBGs";
3336                        contentHolder.addChildAt(colBGs, contentHolder.getChildIndex(contentHolder.getChildByName("rowBGs")) + 1);
3337                    }
3338                    drawColumnBackground(colBGs, i, Number(bgCol), col);
3339                    lastChild = i;
3340                }
3341                else if (colBGs)
3342                {
3343                    var background:Shape = Shape(colBGs.getChildByName(i.toString()));
3344                    if (background)
3345                    {
3346                        var g:Graphics = background.graphics;
3347                        g.clear();
3348                        colBGs.removeChild(background);
3349                    }
3350                }
3351                xx += visibleColumns[i].width;
3352            }
3353            if (colBGs && colBGs.numChildren)
3354            {
3355                while (colBGs.numChildren)
3356                {
3357                    var bg:DisplayObject = colBGs.getChildAt(colBGs.numChildren - 1);
3358                    if (parseInt(bg.name) > lastChild)
3359                        colBGs.removeChild(bg);
3360                    else
3361                        break;
3362                }
3363            }
3364
3365        }
3366
3367        if (separators.right && visibleColumns && visibleColumns.length)
3368        {
3369            if (contentHolder.listItems.length && contentHolder.listItems[0].length)
3370                drawVerticalSeparator(linesBody, i++, 0, contentHolder.listItems[0][len - 1].x + visibleColumns[len - 1].width, true);
3371            else
3372            {
3373                xx = 0;
3374                for (i = 0; i < len; i++)
3375                {
3376                    xx += visibleColumns[i].width;
3377                }
3378                drawVerticalSeparator(linesBody, i++, 0, xx, true);
3379            }
3380        }
3381        if (separators.left)
3382            drawVerticalSeparator(linesBody, i++, 0, 0, true);
3383
3384    }
3385
3386    mx_internal function _drawHeaderBackground(headerBG:UIComponent):void
3387    {
3388        drawHeaderBackground(headerBG);
3389    }
3390
3391    /**
3392     *  Draws the background of the headers into the given
3393     *  UIComponent.  The graphics drawn may be scaled horizontally
3394     *  if the component's width changes or this method will be
3395     *  called again to redraw at a different width and/or height
3396     *
3397     *  @param headerBG A UIComponent that will contain the header
3398     *  background graphics.
3399     */
3400    protected function drawHeaderBackground(headerBG:UIComponent):void
3401    {
3402        DataGridHeader(headerBG.parent)._drawHeaderBackground(headerBG);
3403    }
3404
3405    mx_internal function _clearSeparators():void
3406    {
3407        clearSeparators();
3408    }
3409
3410    /**
3411     *  Removes column header separators that the user normally uses
3412     *  to resize columns.
3413     */
3414    protected function clearSeparators():void
3415    {
3416        DataGridHeader(header)._clearSeparators();
3417        if (lockedColumnHeader)
3418            DataGridHeader(lockedColumnHeader)._clearSeparators();
3419    }
3420
3421    mx_internal function _drawSeparators():void
3422    {
3423        drawSeparators();
3424    }
3425
3426    /**
3427     *  Creates and displays the column header separators that the user
3428     *  normally uses to resize columns.  This implementation uses
3429     *  the same Sprite as the lines and column backgrounds and adds
3430     *  instances of the <code>headerSeparatorSkin</code> and attaches mouse
3431     *  listeners to them in order to know when the user wants
3432     *  to resize a column.
3433     */
3434    protected function drawSeparators():void
3435    {
3436        DataGridHeader(header)._drawSeparators();
3437        if (lockedColumnHeader)
3438            DataGridHeader(lockedColumnHeader)._drawSeparators();
3439    }
3440
3441    /**
3442     *  @private
3443     *  Update sortIndex and sortDirection based on sort info availabled in
3444     *  underlying data provider.
3445     */
3446    private function updateSortIndexAndDirection():void
3447    {
3448        // Don't show sort indicator if sortableColumns is false or if the
3449        // column sorted on has sortable="false"
3450
3451        if (!sortableColumns)
3452        {
3453            lastSortIndex = sortIndex;
3454            sortIndex = -1;
3455
3456            if (lastSortIndex != sortIndex)
3457                invalidateDisplayList();
3458
3459            return;
3460        }
3461
3462        if (!dataProvider)
3463            return;
3464
3465        var view:ICollectionView = ICollectionView(dataProvider);
3466        var sort:Sort = view.sort;
3467        if (!sort)
3468        {
3469            sortIndex = lastSortIndex = -1;
3470            return;
3471        }
3472
3473        var fields:Array = sort.fields;
3474        if (!fields)
3475            return;
3476
3477        if (fields.length != 1)
3478        {
3479            lastSortIndex = sortIndex;
3480            sortIndex = -1;
3481
3482            if (lastSortIndex != sortIndex)
3483                invalidateDisplayList();
3484
3485            return;
3486        }
3487
3488        // fields.length == 1, so the collection is sorted on a single field.
3489        var sortField:SortField = fields[0];
3490        var n:int = _columns.length;
3491        sortIndex = -1;
3492        for (var i:int = 0; i < n; i++)
3493        {
3494            if (_columns[i].dataField == sortField.name)
3495            {
3496                sortIndex = _columns[i].sortable ? i : -1;
3497                sortDirection = sortField.descending ? "DESC" : "ASC";
3498                return;
3499            }
3500        }
3501    }
3502
3503    mx_internal function _placeSortArrow():void
3504    {
3505        placeSortArrow();
3506    }
3507
3508    /**
3509     *  Draws the sort arrow graphic on the column that is the current sort key.
3510     *  This implementation creates or reuses an instance of the skin specified
3511     *  by <code>sortArrowSkin</code> style property and places
3512     *  it in the appropriate column header.  It
3513     *  also shrinks the size of the column header if the text in the header
3514     *  would be obscured by the sort arrow.
3515     */
3516    protected function placeSortArrow():void
3517    {
3518        DataGridHeader(header)._placeSortArrow();
3519        if (lockedColumnHeader)
3520            DataGridHeader(lockedColumnHeader)._placeSortArrow();
3521    }
3522
3523    /**
3524     *  @private
3525     */
3526    private function sortByColumn(index:int):void
3527    {
3528        var c:DataGridColumn = columns[index];
3529        var desc:Boolean = c.sortDescending;
3530
3531        // do the sort if we're allowed to
3532        if (c.sortable)
3533        {
3534            var s:Sort = collection.sort;
3535            var f:SortField;
3536            if (s)
3537            {
3538                s.compareFunction = null;
3539                // analyze the current sort to see what we've been given
3540                var sf:Array = s.fields;
3541                if (sf)
3542                {
3543                    for (var i:int = 0; i < sf.length; i++)
3544                    {
3545
3546                        if (sf[i].name == c.dataField)
3547                        {
3548                            // we're part of the current sort
3549                            f = sf[i]
3550                            // flip the logic so desc is new desired order
3551                            desc = !f.descending;
3552                            break;
3553                        }
3554                    }
3555                }
3556            }
3557            else
3558                s = new Sort;
3559
3560            if (!f)
3561                f = new SortField(c.dataField);
3562
3563
3564            c.sortDescending = desc;
3565            var dir:String = (desc) ? "DESC" : "ASC";
3566            sortDirection = dir;
3567
3568            // set the grid's sortIndex
3569            lastSortIndex = sortIndex;
3570            sortIndex = index;
3571            sortColumn = c;
3572
3573            // if you have a labelFunction you must supply a sortCompareFunction
3574            f.name = c.dataField;
3575            if (c.sortCompareFunction != null)
3576            {
3577                f.compareFunction = c.sortCompareFunction;
3578            }
3579            else
3580            {
3581                f.compareFunction = null;
3582            }
3583            f.descending = desc;
3584            s.fields = [f];
3585        }
3586        collection.sort = s;
3587        collection.refresh();
3588
3589    }
3590
3591    /**
3592     *  @private
3593     */
3594    private function setEditedItemPosition(coord:Object):void
3595    {
3596        bEditedItemPositionChanged = true;
3597        _proposedEditedItemPosition = coord;
3598        invalidateDisplayList();
3599    }
3600
3601    /**
3602     *  @private
3603     *  focus an item renderer in the grid - harder than it looks
3604     */
3605    private function commitEditedItemPosition(coord:Object):void
3606    {
3607        if (!enabled || !editable)
3608            return;
3609
3610        if (!collection || collection.length == 0)
3611            return;
3612
3613        // just give focus back to the itemEditorInstance
3614        if (itemEditorInstance && coord &&
3615            itemEditorInstance is IFocusManagerComponent &&
3616            _editedItemPosition.rowIndex == coord.rowIndex &&
3617            _editedItemPosition.columnIndex == coord.columnIndex)
3618        {
3619            IFocusManagerComponent(itemEditorInstance).setFocus();
3620            return;
3621        }
3622
3623        // dispose of any existing editor, saving away its data first
3624        if (itemEditorInstance)
3625        {
3626            var reason:String;
3627            if (!coord)
3628            {
3629                reason = DataGridEventReason.OTHER;
3630            }
3631            else
3632            {
3633                reason = (!editedItemPosition || coord.rowIndex == editedItemPosition.rowIndex) ?
3634                         DataGridEventReason.NEW_COLUMN :
3635                         DataGridEventReason.NEW_ROW;
3636            }
3637            if (!endEdit(reason) && reason != DataGridEventReason.OTHER)
3638                return;
3639        }
3640
3641        // store the value
3642        _editedItemPosition = coord;
3643
3644        // allow setting of undefined to dispose item editor instance
3645        if (!coord)
3646            return;
3647
3648        if (dontEdit)
3649        {
3650            return;
3651        }
3652
3653        var rowIndex:int = coord.rowIndex;
3654        var colIndex:int = coord.columnIndex;
3655        if (displayableColumns.length != _columns.length)
3656        {
3657            for (var i:int = 0; i < displayableColumns.length; i++)
3658            {
3659                if (displayableColumns[i].colNum >= colIndex)
3660                {
3661                    colIndex = i;
3662                    break;
3663                }
3664            }
3665            if (i == displayableColumns.length)
3666                colIndex = 0;
3667        }
3668
3669        // trace("commitEditedItemPosition ", coord.rowIndex, selectedIndex);
3670
3671        var needChangeEvent:Boolean = false;
3672        if (selectedIndex != coord.rowIndex)
3673        {
3674            commitSelectedIndex(coord.rowIndex);
3675            needChangeEvent = true;
3676        }
3677
3678        scrollToEditedItem(rowIndex, colIndex);
3679
3680        // get the actual references for the column, row, and item
3681        var item:IListItemRenderer = actualContentHolder.listItems[actualRowIndex][actualColIndex];
3682        if (!item)
3683        {
3684            // assume that editing was cancelled
3685            commitEditedItemPosition(null);
3686            return;
3687        }
3688        if (!isItemEditable(item.data))
3689        {
3690            // assume that editing was cancelled
3691            commitEditedItemPosition(null);
3692            return;
3693        }
3694
3695        if (needChangeEvent)
3696        {
3697            var evt:ListEvent = new ListEvent(ListEvent.CHANGE);
3698            evt.columnIndex = coord.columnIndex;
3699            evt.rowIndex = coord.rowIndex;;
3700            evt.itemRenderer = item;
3701            dispatchEvent(evt);
3702        }
3703
3704        var event:DataGridEvent =
3705            new DataGridEvent(DataGridEvent.ITEM_EDIT_BEGIN, false, true);
3706            // ITEM_EDIT events are cancelable
3707        event.columnIndex = displayableColumns[colIndex].colNum;
3708        event.rowIndex = _editedItemPosition.rowIndex;
3709        event.itemRenderer = item;
3710        dispatchEvent(event);
3711
3712        lastEditedItemPosition = _editedItemPosition;
3713
3714        // user may be trying to change the focused item renderer
3715        if (bEditedItemPositionChanged)
3716        {
3717            bEditedItemPositionChanged = false;
3718            commitEditedItemPosition(_proposedEditedItemPosition);
3719            _proposedEditedItemPosition = undefined;
3720
3721        }
3722
3723        if (!itemEditorInstance)
3724        {
3725            // assume that editing was cancelled
3726            commitEditedItemPosition(null);
3727        }
3728    }
3729
3730    // computes actualRowIndex, actualColIndex and actualContentHolder by
3731    // taking inputs for rowIndex, colIndex and scrolling to the right
3732    // place
3733    private function scrollToEditedItem(rowIndex:int, colIndex:int):void
3734    {
3735        actualContentHolder = listContent;
3736        var listItems:Array  = actualContentHolder.listItems;
3737
3738        var lastRowIndex:int = verticalScrollPosition + listItems.length - 1 + lockedRowCount;
3739        var partialRow:int = (rowInfo[listItems.length - 1].y + rowInfo[listItems.length - 1].height > listContent.height) ? 1 : 0;
3740
3741        // actual row/column is the offset into one of the containers
3742        if (rowIndex > lockedRowCount)
3743        {
3744            // not a locked editable row make sure it is on screen
3745            if (rowIndex < verticalScrollPosition + lockedRowCount)
3746                verticalScrollPosition = rowIndex - lockedRowCount;
3747            else
3748            {
3749                // variable row heights means that we can't know how far to scroll sometimes so we loop
3750                // until we get it right
3751                while (rowIndex > lastRowIndex ||
3752                    // we're the last row, and we're partially visible, but we're not
3753                    // the top scrollable row already
3754                    (rowIndex == lastRowIndex && rowIndex > verticalScrollPosition + lockedRowCount &&
3755                        partialRow))
3756                {
3757                    if (verticalScrollPosition == maxVerticalScrollPosition)
3758                        break;
3759                    verticalScrollPosition = Math.min(verticalScrollPosition + (rowIndex > lastRowIndex ? rowIndex - lastRowIndex : partialRow), maxVerticalScrollPosition);
3760                    lastRowIndex = verticalScrollPosition + listItems.length - 1 + lockedRowCount;
3761                    partialRow = (rowInfo[listItems.length - 1].y + rowInfo[listItems.length - 1].height > listContent.height) ? 1 : 0;
3762                }
3763            }
3764
3765            actualRowIndex = rowIndex - verticalScrollPosition - lockedRowCount;
3766
3767        }
3768        else
3769        {
3770            if (rowIndex == lockedRowCount)
3771            {
3772                verticalScrollPosition = 0;
3773                actualRowIndex = rowIndex - lockedRowCount;
3774            }
3775            else
3776            {
3777                if (lockedRowCount)
3778                    actualContentHolder = lockedRowContent;
3779
3780                actualRowIndex = rowIndex;
3781            }
3782        }
3783
3784        // reset since actualContentHolder could have changed
3785        listItems = actualContentHolder.listItems;
3786
3787        var len:uint = (listItems && listItems[0]) ? listItems[0].length : visibleColumns.length;
3788        var lastColIndex:int = horizontalScrollPosition + len - 1 + lockedColumnCount;
3789
3790        if (colIndex > lockedColumnCount)
3791        {
3792            var partialCol:int = (listItems[0][len - 1].x + listItems[0][len - 1].width > listContent.width) ? 1 : 0;
3793            if (colIndex < horizontalScrollPosition + lockedColumnCount)
3794                horizontalScrollPosition = colIndex - lockedColumnCount;
3795            else
3796            {
3797                while (colIndex > lastColIndex ||
3798                       (colIndex == lastColIndex && colIndex > horizontalScrollPosition + lockedColumnCount &&
3799                       partialCol))
3800                {
3801                    if (horizontalScrollPosition == maxHorizontalScrollPosition)
3802                        break;
3803                    horizontalScrollPosition = Math.min(horizontalScrollPosition + (colIndex > lastColIndex ? colIndex - lastColIndex : partialCol), maxHorizontalScrollPosition);
3804                    len = (listItems && listItems[0]) ? listItems[0].length : visibleColumns.length;
3805                    lastColIndex = horizontalScrollPosition + len - 1 + lockedColumnCount;
3806                    partialCol = (listItems[0][len - 1].x + listItems[0][len - 1].width > listContent.width) ? 1 : 0;
3807                }
3808            }
3809            actualColIndex = colIndex - horizontalScrollPosition - lockedColumnCount;
3810        }
3811        else
3812        {
3813            if (colIndex == lockedColumnCount)
3814            {
3815                horizontalScrollPosition = 0;
3816                actualColIndex = colIndex - lockedColumnCount;
3817            }
3818            else
3819            {
3820                if (lockedColumnCount)
3821                {
3822                    if (actualContentHolder == lockedRowContent)
3823                        actualContentHolder = lockedColumnAndRowContent;
3824                    else
3825                        actualContentHolder = lockedColumnContent;
3826                }
3827
3828                actualColIndex = colIndex;
3829            }
3830        }
3831    }
3832
3833    /**
3834     *  Creates the item editor for the item renderer at the
3835     *  <code>editedItemPosition</code> using the editor
3836     *  specified by the <code>itemEditor</code> property.
3837     *
3838     *  <p>This method sets the editor instance as the
3839     *  <code>itemEditorInstance</code> property.</p>
3840     *
3841     *  <p>You may only call this method from within the event listener
3842     *  for the <code>itemEditBegin</code> event.
3843     *  To create an editor at other times, set the
3844     *  <code>editedItemPosition</code> property to generate
3845     *  the <code>itemEditBegin</code> event.</p>
3846     *
3847     *  @param colIndex The column index in the data provider of the item to be edited.
3848     *
3849     *  @param rowIndex The row index in the data provider of the item to be edited.
3850     */
3851    public function createItemEditor(colIndex:int, rowIndex:int):void
3852    {
3853        if (displayableColumns.length != _columns.length)
3854        {
3855            for (var i:int = 0; i < displayableColumns.length; i++)
3856            {
3857                if (displayableColumns[i].colNum >= colIndex)
3858                {
3859                    colIndex = i;
3860                    break;
3861                }
3862            }
3863            if (i == displayableColumns.length)
3864                colIndex = 0;
3865        }
3866
3867        var col:DataGridColumn = displayableColumns[colIndex];
3868        if (rowIndex >= lockedRowCount)
3869            rowIndex -= verticalScrollPosition + lockedRowCount;
3870
3871        if (colIndex >= lockedColumnCount)
3872            colIndex -= horizontalScrollPosition + lockedColumnCount;
3873
3874        var item:IListItemRenderer = actualContentHolder.listItems[rowIndex][colIndex];
3875        var rowData:ListRowInfo = actualContentHolder.rowInfo[rowIndex];
3876
3877        if (!col.rendererIsEditor)
3878        {
3879            var dx:Number = 0;
3880            var dy:Number = -2;
3881            var dw:Number = 0;
3882            var dh:Number = 4;
3883            // if this isn't implemented, use an input control as editor
3884            if (!itemEditorInstance)
3885            {
3886                var itemEditor:IFactory = col.itemEditor;
3887                dx = col.editorXOffset;
3888                dy = col.editorYOffset;
3889                dw = col.editorWidthOffset;
3890                dh = col.editorHeightOffset;
3891                itemEditorInstance = itemEditor.newInstance();
3892                itemEditorInstance.owner = this;
3893                itemEditorInstance.styleName = col;
3894                actualContentHolder.addChild(DisplayObject(itemEditorInstance));
3895            }
3896            actualContentHolder.setChildIndex(DisplayObject(itemEditorInstance), actualContentHolder.numChildren - 1);
3897            // give it the right size, look and placement
3898            itemEditorInstance.visible = true;
3899            itemEditorInstance.move(item.x + dx, rowData.y + dy);
3900            itemEditorInstance.setActualSize(
3901                            Math.min(col.width + dw,
3902                                     actualContentHolder.width - 1 - itemEditorInstance.x),
3903                            Math.min(rowData.height + dh,
3904                                     actualContentHolder.height - itemEditorInstance.y));
3905            DisplayObject(itemEditorInstance).addEventListener(FocusEvent.FOCUS_OUT, itemEditorFocusOutHandler);
3906            item.visible = false;
3907
3908        }
3909        else
3910        {
3911            // if the item renderer is also the editor, we'll use it
3912            itemEditorInstance = item;
3913        }
3914
3915        // listen for keyStrokes on the itemEditorInstance (which lets the grid supervise for ESC/ENTER)
3916        DisplayObject(itemEditorInstance).addEventListener(KeyboardEvent.KEY_DOWN, editorKeyDownHandler);
3917        // we disappear on any mouse down outside the editor
3918        systemManager.getSandboxRoot().
3919            addEventListener(MouseEvent.MOUSE_DOWN, editorMouseDownHandler, true, 0, true);
3920        systemManager.getSandboxRoot().
3921            addEventListener(SandboxMouseEvent.MOUSE_DOWN_SOMEWHERE, editorMouseDownHandler, false, 0, true);
3922        // we disappear if stage is resized
3923        systemManager.addEventListener(Event.RESIZE, editorStageResizeHandler, true, 0, true);
3924
3925	// Dispatch our item editor created event
3926        var event:DataGridEvent =
3927        		new DataGridEvent(DataGridEvent.ITEM_EDITOR_CREATE, false, true, colIndex, null, rowIndex);
3928        dispatchEvent(event);
3929    }
3930
3931    /**
3932     *  @private
3933     *  Determines the next item renderer to navigate to using the Tab key.
3934     *  If the item renderer to be focused falls out of range (the end or beginning
3935     *  of the grid) then move focus outside the grid.
3936     */
3937    private function findNextItemRenderer(shiftKey:Boolean):Boolean
3938    {
3939        if (!lastEditedItemPosition)
3940            return false;
3941
3942        // some other thing like a collection change has changed the
3943        // position, so bail and wait for commit to reset the editor.
3944        if (_proposedEditedItemPosition !== undefined)
3945            return true;
3946
3947        _editedItemPosition = lastEditedItemPosition;
3948
3949        var index:int = _editedItemPosition.rowIndex;
3950        var colIndex:int = _editedItemPosition.columnIndex;
3951
3952        var found:Boolean = false;
3953        var incr:int = shiftKey ? -1 : 1;
3954        var maxIndex:int = collection.length - 1;
3955
3956        // cycle till we find something worth focusing, or the end of the grid
3957        while (!found)
3958        {
3959            // go to next column
3960            colIndex += incr;
3961            if (colIndex >= _columns.length || colIndex < 0)
3962            {
3963                // if we fall off the end of the columns, wrap around
3964                colIndex = (colIndex < 0) ? _columns.length - 1 : 0;
3965                // and increment/decrement the row index
3966                index += incr;
3967                if (index > maxIndex || index < 0)
3968                {
3969                    if (endEdit(DataGridEventReason.NEW_ROW))
3970                    {
3971                        // if we've fallen off the rows, we need to leave the grid. get rid of the editor
3972                        setEditedItemPosition(null);
3973                        // set focus back to the grid so default handler will move it to the next component
3974                        losingFocus = true;
3975                        setFocus();
3976                        return false;
3977                    }
3978                    return true;
3979                }
3980            }
3981            // if we find a visible and editable column, move to it
3982            if (_columns[colIndex].editable && _columns[colIndex].visible)
3983            {
3984                found = true;
3985                // kill the old edit session
3986                var reason:String;
3987                reason = index == _editedItemPosition.rowIndex ?
3988                         DataGridEventReason.NEW_COLUMN :
3989                         DataGridEventReason.NEW_ROW;
3990                if (!itemEditorInstance || endEdit(reason))
3991                {
3992                    // send event to create the new one
3993                    beginningEdit(colIndex, index);
3994                }
3995            }
3996        }
3997        return found;
3998    }
3999
4000    /**
4001     *  This method closes an item editor currently open on an item renderer.
4002     *  You typically only call this method from within the event listener
4003     *  for the <code>itemEditEnd</code> event, after
4004     *  you have already called the <code>preventDefault()</code> method to
4005     *  prevent the default event listener from executing.
4006     */
4007    public function destroyItemEditor():void
4008    {
4009        // trace("destroyItemEditor");
4010        if (itemEditorInstance)
4011        {
4012            DisplayObject(itemEditorInstance).removeEventListener(KeyboardEvent.KEY_DOWN, editorKeyDownHandler);
4013            systemManager.getSandboxRoot().
4014                removeEventListener(MouseEvent.MOUSE_DOWN, editorMouseDownHandler, true);
4015            systemManager.getSandboxRoot().
4016                removeEventListener(SandboxMouseEvent.MOUSE_DOWN_SOMEWHERE, editorMouseDownHandler);
4017            systemManager.removeEventListener(Event.RESIZE, editorStageResizeHandler, true);
4018
4019            var event:DataGridEvent =
4020                new DataGridEvent(DataGridEvent.ITEM_FOCUS_OUT);
4021            event.columnIndex = _editedItemPosition.columnIndex;
4022            event.rowIndex = _editedItemPosition.rowIndex;
4023            event.itemRenderer = itemEditorInstance;
4024            dispatchEvent(event);
4025
4026            if (! _columns[_editedItemPosition.columnIndex].rendererIsEditor)
4027            {
4028                // FocusManager.removeHandler() does not find
4029                // itemEditors in focusableObjects[] array
4030                // and hence does not remove the focusRectangle
4031                if (itemEditorInstance && itemEditorInstance is UIComponent)
4032                    UIComponent(itemEditorInstance).drawFocus(false);
4033
4034                // must call removeChild() so FocusManager.lastFocus becomes null
4035                actualContentHolder.removeChild(DisplayObject(itemEditorInstance));
4036                editedItemRenderer.visible = true;
4037            }
4038            itemEditorInstance = null;
4039            _editedItemPosition = null;
4040        }
4041    }
4042
4043    /**
4044     *  @private
4045     *  dispatch an ITEM_EDIT_BEGINNING event;
4046     */
4047    private function beginningEdit(columnIndex:int, rowIndex:int, itemRenderer:IListItemRenderer = null):void
4048    {
4049        var dataGridEvent:DataGridEvent =
4050            new DataGridEvent(DataGridEvent.ITEM_EDIT_BEGINNING, false, true);
4051            // ITEM_EDIT events are cancelable
4052        dataGridEvent.columnIndex = columnIndex;
4053        dataGridEvent.dataField = _columns[columnIndex].dataField;
4054        dataGridEvent.rowIndex = rowIndex;
4055        dataGridEvent.itemRenderer = itemRenderer;
4056        if (!dispatchEvent(dataGridEvent))
4057            lastEditedItemPosition = { columnIndex: columnIndex, rowIndex: rowIndex };
4058
4059    }
4060
4061    /**
4062     *  @private
4063     *  When the user finished editing an item, this method is called.
4064     *  It dispatches the DataGridEvent.ITEM_EDIT_END event to start the process
4065     *  of copying the edited data from
4066     *  the itemEditorInstance to the data provider and hiding the itemEditorInstance.
4067     *  returns true if nobody called preventDefault.
4068     */
4069    private function endEdit(reason:String):Boolean
4070    {
4071        // this happens if the renderer is removed asynchronously ususally with FDS
4072        if (!editedItemRenderer)
4073            return true;
4074
4075        inEndEdit = true;
4076
4077        var dataGridEvent:DataGridEvent =
4078            new DataGridEvent(DataGridEvent.ITEM_EDIT_END, false, true);
4079            // ITEM_EDIT events are cancelable
4080        dataGridEvent.columnIndex = editedItemPosition.columnIndex;
4081        dataGridEvent.dataField = _columns[editedItemPosition.columnIndex].dataField;
4082        dataGridEvent.rowIndex = editedItemPosition.rowIndex;
4083        dataGridEvent.itemRenderer = editedItemRenderer;
4084        dataGridEvent.reason = reason;
4085        dispatchEvent(dataGridEvent);
4086        // set a flag to not open another edit session if the item editor is still up
4087        // this means somebody wants the old edit session to stay.
4088        dontEdit = itemEditorInstance != null;
4089        // trace("dontEdit", dontEdit);
4090
4091        if (!dontEdit && reason == DataGridEventReason.CANCELLED)
4092        {
4093            losingFocus = true;
4094            setFocus();
4095        }
4096
4097        inEndEdit = false;
4098
4099        return !(dataGridEvent.isDefaultPrevented())
4100    }
4101
4102    /**
4103     *  Determines whether to allow editing of a dataprovider item on a per-row basis.
4104     *  The default implementation of this method only checks the <code>editable</code> property
4105     *  of the DataGrid and returns <code>false</code> if <code>editable</code> is <code>false</code>
4106     *  or if the dataprovider item is <code>null</code>.
4107     *
4108     *  <p>This method can be overridden to allow fine-grained control of which dataprovider items
4109     *  are editable. For example, if you want to disallow editing of grouping rows or summary rows
4110     *  you would override this method with custom logic to this behavior.</p>
4111     *
4112     *  @param data The data provider item. The default implementation of this method returns
4113     *  <code>false</code> if the data object is <code>null</code>.
4114     *
4115     *  @return The default behavior is to return <code>true</code> if the DataGrid's <code>editable</code> property is
4116     *  <code>true</code> and the data object is not <code>null</code>.
4117     */
4118    public function isItemEditable(data:Object):Boolean
4119    {
4120        if (!editable)
4121            return false;
4122
4123        if (!data)
4124            return false;
4125
4126        return true;
4127    }
4128
4129    /**
4130     *  @private
4131     */
4132    override mx_internal function columnRendererChanged(c:DataGridColumn):void
4133    {
4134        var item:DisplayObject;
4135
4136        var factories:Dictionary = c.measuringObjects;
4137        if (factories)
4138        {
4139            for (var p:* in factories)
4140            {
4141                var factory:IFactory = IFactory(p);
4142                item = c.measuringObjects[factory];
4143                if (item)
4144                {
4145                    item.parent.removeChild(item);
4146                    c.measuringObjects[factory] = null;
4147                }
4148
4149                if (c.freeItemRenderersByFactory && c.freeItemRenderersByFactory[factory])
4150                {
4151                    var d:Dictionary = c.freeItemRenderersByFactory[factory];
4152                    for (var q:* in d)
4153                    {
4154                        item = DisplayObject(q);
4155                        item.parent.removeChild(item);
4156                    }
4157                    c.freeItemRenderersByFactory[factory] = new Dictionary(true);
4158                }
4159            }
4160            var freeRenderers:Array = freeItemRenderersTable[c] as Array;
4161            if (freeRenderers)
4162            {
4163                while (freeRenderers.length)
4164                {
4165                    item = freeRenderers.pop();
4166                }
4167            }
4168        }
4169
4170        item = c.cachedHeaderRenderer as DisplayObject;
4171        if (item && item.parent)
4172            item.parent.removeChild(item);
4173        c.cachedHeaderRenderer = null;
4174
4175        rendererChanged = true;
4176        invalidateDisplayList();
4177    }
4178
4179    //--------------------------------------------------------------------------
4180    //
4181    //  Overridden event handlers
4182    //
4183    //--------------------------------------------------------------------------
4184
4185    /**
4186     *  @private
4187     *  Catches any events from the model. Optimized for editing one item.
4188     *  Creates columns when there are none. Inherited from list.
4189     *  @param eventObj
4190     */
4191    override protected function collectionChangeHandler(event:Event):void
4192    {
4193        if (event is CollectionEvent)
4194        {
4195            var curEditedItemPosition:Object;
4196            var ceEvent:CollectionEvent = CollectionEvent(event)
4197            if (ceEvent.kind == CollectionEventKind.RESET)
4198            {
4199                if (itemEditorInstance)
4200                    endEdit(DataGridEventReason.CANCELLED);
4201                setEditedItemPosition(null); // nothing left to edit
4202                if (generatedColumns)
4203                    generateCols();
4204                else
4205                    columnsInvalid = true;
4206                updateSortIndexAndDirection();
4207                if (lockedRowContent)
4208                    lockedRowContent.iterator = collection.createCursor();
4209                if (lockedColumnAndRowContent)
4210                    lockedColumnAndRowContent.iterator = collection.createCursor();
4211            }
4212            else if (ceEvent.kind == CollectionEventKind.REFRESH && !manualSort)
4213                updateSortIndexAndDirection();
4214            else
4215            {
4216                // if we get a add while editing adjust the editPosition
4217                if (ceEvent.kind == CollectionEventKind.ADD)
4218                {
4219                    if (editedItemPosition)
4220                    {
4221                        if (ceEvent.location <= editedItemPosition.rowIndex)
4222                        {
4223                            curEditedItemPosition = editedItemPosition;
4224
4225                            if (inEndEdit)
4226                                _editedItemPosition = { columnIndex : editedItemPosition.columnIndex,
4227                                                    rowIndex : Math.max(0, editedItemPosition.rowIndex + ceEvent.items.length)};
4228                            else if (itemEditorInstance)
4229                            {
4230                                _editedItemPosition = { columnIndex : editedItemPosition.columnIndex,
4231                                                        rowIndex : Math.max(0, editedItemPosition.rowIndex + ceEvent.items.length)};
4232                                itemEditorPositionChanged = true;
4233                                lastEditedItemPosition = _editedItemPosition;
4234                            }
4235                            else
4236                                setEditedItemPosition({ columnIndex : curEditedItemPosition.columnIndex,
4237                                                    rowIndex : Math.max(0, curEditedItemPosition.rowIndex + ceEvent.items.length)});
4238                        }
4239                    }
4240                }
4241                // if we get a remove while editing adjust the editPosition
4242                else if (ceEvent.kind == CollectionEventKind.REMOVE)
4243                {
4244                    if (editedItemPosition)
4245                    {
4246                        if (collection.length == 0)
4247                        {
4248                            if (itemEditorInstance)
4249                                endEdit(DataGridEventReason.CANCELLED);
4250                            setEditedItemPosition(null); // nothing left to edit
4251                        }
4252                        else if (ceEvent.location <= editedItemPosition.rowIndex)
4253                        {
4254                            curEditedItemPosition = editedItemPosition;
4255
4256                            // if the editor is up on the item going away, cancel the session
4257                            if (ceEvent.location == editedItemPosition.rowIndex && itemEditorInstance)
4258                                endEdit(DataGridEventReason.CANCELLED);
4259
4260                            if (inEndEdit)
4261                                _editedItemPosition = { columnIndex : editedItemPosition.columnIndex,
4262                                                    rowIndex : Math.max(0, editedItemPosition.rowIndex - ceEvent.items.length)};
4263                            else if (itemEditorInstance)
4264                            {
4265                                _editedItemPosition = { columnIndex : editedItemPosition.columnIndex,
4266                                                        rowIndex : Math.max(0, editedItemPosition.rowIndex - ceEvent.items.length)};
4267                                itemEditorPositionChanged = true;
4268                                lastEditedItemPosition = _editedItemPosition;
4269                            }
4270                            else
4271                                setEditedItemPosition({ columnIndex : curEditedItemPosition.columnIndex,
4272                                                    rowIndex : Math.max(0, curEditedItemPosition.rowIndex - ceEvent.items.length)});
4273                        }
4274                    }
4275                }
4276                else if (ceEvent.kind == CollectionEventKind.REPLACE)
4277                {
4278                    if (editedItemPosition)
4279                    {
4280                        // if the editor is up on the item going away, cancel the session
4281                        if (ceEvent.location == editedItemPosition.rowIndex && itemEditorInstance)
4282                            endEdit(DataGridEventReason.CANCELLED);
4283                    }
4284                }
4285            }
4286        }
4287
4288        super.collectionChangeHandler(event);
4289
4290        if (event is CollectionEvent)
4291        {
4292            // trace("ListBase collectionEvent");
4293            var ce:CollectionEvent = CollectionEvent(event);
4294            if (ce.kind == CollectionEventKind.ADD)
4295            {
4296                // added first item, generate columns for it if needed
4297                if (collection.length == 1)
4298                    if (generatedColumns)
4299                        generateCols();
4300            }
4301            else if (ce.kind == CollectionEventKind.REFRESH)
4302            {
4303                // refresh locked row count iterator
4304                if (lockedRowCount && lockedRowContent)
4305                    lockedRowContent.iterator.seek(CursorBookmark.FIRST, 0);
4306            }
4307        }
4308
4309
4310//      if (event.eventName != "sort" && bRowsChanged)
4311//          invInitHeaders = true;
4312    }
4313
4314    /**
4315     *  @private
4316     */
4317    override protected function mouseDownHandler(event:MouseEvent):void
4318    {
4319        // trace(">>mouseDownHandler");
4320        var r:IListItemRenderer;
4321        var s:Sprite;
4322
4323        r = mouseEventToItemRenderer(event);
4324
4325        lastItemDown = null;
4326
4327        var isItemEditor:Boolean = itemRendererContains(itemEditorInstance, DisplayObject(event.target));
4328
4329        // If it isn't an item renderer, or an item editor do default behavior
4330        if (!isItemEditor)
4331        {
4332            var pos:Point;
4333            if (r)
4334            {
4335                lastItemDown = r;
4336
4337                pos = itemRendererToIndices(r);
4338
4339                var bEndedEdit:Boolean = true;
4340
4341                if (itemEditorInstance)
4342                {
4343                    if (displayableColumns[pos.x].editable == false)
4344                    {
4345                        bEndedEdit = endEdit(DataGridEventReason.OTHER);
4346                    }
4347                    else
4348                    {
4349                        //Possible to get here and not have an editedItemPosition
4350                        if (editedItemPosition)
4351                        {
4352                            bEndedEdit = endEdit(editedItemPosition.rowIndex == pos.y ?
4353                                         DataGridEventReason.NEW_COLUMN :
4354                                         DataGridEventReason.NEW_ROW);
4355                        }
4356                        else
4357                        {
4358                            bEndedEdit = false;
4359                        }
4360                    }
4361                }
4362
4363                // if we didn't end edit session, don't do default behavior (call super)
4364                if (!bEndedEdit)
4365                    return;
4366            }
4367            else
4368            {
4369                // trace("end edit?");
4370                if (itemEditorInstance)
4371                    endEdit(DataGridEventReason.OTHER);
4372            }
4373
4374            super.mouseDownHandler(event);
4375
4376            if (r)
4377            {
4378                if (displayableColumns[pos.x].rendererIsEditor)
4379                    resetDragScrolling();
4380            }
4381        }
4382        else
4383            resetDragScrolling();
4384        // trace("<<mouseDownHandler");
4385    }
4386
4387    /**
4388     *  @private
4389     */
4390    override protected function mouseUpHandler(event:MouseEvent):void
4391    {
4392        var dataGridEvent:DataGridEvent;
4393        var r:IListItemRenderer;
4394        var s:Sprite;
4395        var n:int;
4396        var i:int;
4397        var pos:Point;
4398
4399        r = mouseEventToItemRenderer(event);
4400
4401        super.mouseUpHandler(event);
4402
4403        if (r && r != itemEditorInstance &&
4404                    (lastItemDown == r || itemRendererContains(lastItemDown, lastItemFocused)))
4405        {
4406            // if lastItemDown != r, we clicked in one cell and dragged to another.
4407            // if lastItemFocused is in that cell, then we give lastItemDown the
4408            // edit session
4409            if (lastItemDown != r)
4410                r = lastItemDown;
4411
4412            lastItemFocused = null;
4413            pos = itemRendererToIndices(r);
4414
4415            if (pos && pos.y >= 0 && editable && !dontEdit)
4416            {
4417                if (displayableColumns[pos.x].editable)
4418                {
4419                    beginningEdit(displayableColumns[pos.x].colNum, pos.y, r);
4420                }
4421                else
4422                    // if the item is not editable, set lastPosition to it anyways
4423                    // so future tabbing starts from there
4424                    lastEditedItemPosition = { columnIndex: displayableColumns[pos.x].colNum, rowIndex: pos.y };
4425            }
4426        }
4427        else if (lastItemDown && lastItemDown != itemEditorInstance)
4428        {
4429            pos = itemRendererToIndices(lastItemDown);
4430
4431            if (pos && pos.y >= 0 && editable && !dontEdit)
4432            {
4433                if (displayableColumns[pos.x].editable)
4434                {
4435                    beginningEdit(displayableColumns[pos.x].colNum, pos.y, lastItemDown);
4436                }
4437                else
4438                    // if the item is not editable, set lastPosition to it anyways
4439                    // so future tabbing starts from there
4440                    lastEditedItemPosition = { columnIndex: displayableColumns[pos.x].colNum, rowIndex: pos.y };
4441            }
4442        }
4443
4444        lastItemDown = null;
4445    }
4446
4447    /**
4448     *  @private
4449     *  when the grid gets focus, focus an item renderer
4450     */
4451    override protected function focusInHandler(event:FocusEvent):void
4452    {
4453        // trace(">>DGFocusIn ", selectedIndex);
4454        var dataGridEvent:DataGridEvent;
4455
4456        if (losingFocus)
4457        {
4458            losingFocus = false;
4459            // trace("losing focus via tab");
4460            // trace("<<DGFocusIn ");
4461            return;
4462        }
4463
4464        if (editable)
4465        {
4466            addEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler);
4467            addEventListener(MouseEvent.MOUSE_DOWN, mouseFocusChangeHandler);
4468        }
4469
4470        if (event.target != this)
4471        {
4472            if (itemEditorInstance && itemRendererContains(itemEditorInstance, DisplayObject(event.target)))
4473            {
4474                // trace("item editor got focus ignoring");
4475                // trace("<<DGFocusIn ");
4476                return;
4477            }
4478            lastItemFocused = DisplayObject(event.target);
4479            // trace("subcomponent got focus ignoring");
4480            // trace("<<DGFocusIn ");
4481            return;
4482        }
4483        lastItemFocused = null;
4484
4485        super.focusInHandler(event);
4486
4487        // isPressed is not correct if we switch in from a different window
4488        // via mouseDown.  The activation calls focusIn (if we had focus
4489        // when we lost activation) before mouseDown
4490        if (editable && !isPressed) // don't do this if we're mouse focused
4491        {
4492            _editedItemPosition = lastEditedItemPosition;
4493
4494            var foundOne:Boolean = false;
4495
4496            // start somewhere
4497            if (!_editedItemPosition)
4498                _editedItemPosition = { rowIndex: 0, columnIndex: 0 };
4499
4500
4501            for (;
4502                 _editedItemPosition.columnIndex != _columns.length;
4503                 _editedItemPosition.columnIndex++)
4504            {
4505                // If the editedItemPosition is valid, focus it,
4506                // otherwise find one.
4507                if (_columns[_editedItemPosition.columnIndex].editable &&
4508                    _columns[_editedItemPosition.columnIndex].visible)
4509                {
4510                    foundOne = true;
4511                    break;
4512                }
4513            }
4514
4515            if (foundOne)
4516            {
4517                // trace("setting focus", _editedItemPosition.columnIndex, _editedItemPosition.rowIndex);
4518                beginningEdit(_editedItemPosition.columnIndex, _editedItemPosition.rowIndex);
4519            }
4520
4521        }
4522
4523        // trace("<<DGFocusIn ");
4524    }
4525
4526    /**
4527     *  @private
4528     *  when the grid loses focus, close the editor
4529     */
4530    override protected function focusOutHandler(event:FocusEvent):void
4531    {
4532        // trace(">>DGFocusOut " + itemEditorInstance + " " + event.relatedObject, event.target);
4533        if (event.target == this)
4534            super.focusOutHandler(event);
4535
4536        // just leave if item editor is losing focus back to grid.  Usually happens
4537        // when someone clicks out of the editor onto a new item renderer.
4538        if (event.relatedObject == this && itemRendererContains(itemEditorInstance, DisplayObject(event.target)))
4539            return;
4540
4541        // just leave if the cell renderer is losing focus to nothing while its editor exists.
4542        // this happens when we make the cell renderer invisible as we put up the editor
4543        // if the renderer can have focus.
4544        if (event.relatedObject == null && itemRendererContains(editedItemRenderer, DisplayObject(event.target)))
4545            return;
4546
4547        // just leave if item editor is losing focus to nothing.  Usually happens
4548        // when someone clicks out of the textfield
4549        if (event.relatedObject == null && itemRendererContains(itemEditorInstance, DisplayObject(event.target)))
4550            return;
4551
4552        // however, if we're losing focus to anything other than the editor or the grid
4553        // hide the editor;
4554        if (itemEditorInstance && (!event.relatedObject || !itemRendererContains(itemEditorInstance, event.relatedObject)))
4555        {
4556            // find renderer for target
4557            var target:DisplayObject = DisplayObject(event.relatedObject);
4558            while (target && target != this)
4559            {
4560                if (target is IListItemRenderer && target.parent.parent == this && target.parent is ListBaseContentHolder)
4561                {
4562                    if (target.visible)
4563                    {
4564                        // losing focus to another renderer
4565                        // let other logic end the session
4566                        return;
4567                    }
4568                }
4569
4570                if (target is IUIComponent)
4571                    target = IUIComponent(target).owner;
4572                else
4573                    target = target.parent;
4574            }
4575
4576            // trace("call endEdit from focus out");
4577            endEdit(DataGridEventReason.OTHER);
4578            removeEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler);
4579            removeEventListener(MouseEvent.MOUSE_DOWN, mouseFocusChangeHandler);
4580        }
4581        // trace("<<DGFocusOut " + itemEditorInstance + " " + event.relatedObject);
4582    }
4583
4584    /**
4585     *  @private
4586     */
4587    private function deactivateHandler(event:Event):void
4588    {
4589        // if stage losing activation, set focus to DG so when we get it back
4590        // we popup an editor again
4591        if (itemEditorInstance)
4592        {
4593            endEdit(DataGridEventReason.OTHER);
4594            losingFocus = true;
4595            setFocus();
4596        }
4597    }
4598
4599    /**
4600     *  @private
4601     */
4602    override protected function keyDownHandler(event:KeyboardEvent):void
4603    {
4604        if (itemEditorInstance || event.target != event.currentTarget)
4605            return;
4606
4607        if (event.keyCode != Keyboard.SPACE)
4608            super.keyDownHandler(event);
4609        else if (caretIndex != -1)
4610        {
4611            moveSelectionVertically(event.keyCode, event.shiftKey, event.ctrlKey);
4612        }
4613    }
4614
4615    /**
4616     *  @private
4617     *  used by ListBase.findString.  Shouldn't be used elsewhere
4618     *  because column's itemToLabel is preferred
4619     */
4620    override public function itemToLabel(data:Object):String
4621    {
4622        return displayableColumns[sortIndex == -1 ? 0 : sortIndex].itemToLabel(data);
4623    }
4624
4625    //--------------------------------------------------------------------------
4626    //
4627    //  Event handlers
4628    //
4629    //--------------------------------------------------------------------------
4630
4631
4632    /**
4633     *  @private
4634     */
4635    private function editorMouseDownHandler(event:Event):void
4636    {
4637        if (event is MouseEvent && owns(DisplayObject(event.target)))
4638			return;
4639
4640        endEdit(DataGridEventReason.OTHER);
4641        // set focus back to the grid so grid logic will deal if focus doesn't
4642        // end up somewhere else
4643        losingFocus = true;
4644        setFocus();
4645    }
4646
4647    /**
4648     *  @private
4649     */
4650    private function editorKeyDownHandler(event:KeyboardEvent):void
4651    {
4652        // ESC just kills the editor, no new data
4653        if (event.keyCode == Keyboard.ESCAPE)
4654        {
4655            endEdit(DataGridEventReason.CANCELLED);
4656        }
4657        else if (event.ctrlKey && event.charCode == 46)
4658        {   // Check for Ctrl-.
4659            endEdit(DataGridEventReason.CANCELLED);
4660        }
4661        else if (event.charCode == Keyboard.ENTER && event.keyCode != 229)
4662        {
4663            // multiline editors can take the enter key.
4664            if (!_editedItemPosition)
4665                return;
4666
4667            if (columns[_editedItemPosition.columnIndex].editorUsesEnterKey)
4668                return;
4669
4670            // Enter edits the item, moves down a row
4671            // The 229 keyCode is for IME compatability. When entering an IME expression,
4672            // the enter key is down, but the keyCode is 229 instead of the enter key code.
4673            // Thanks to Yukari for this little trick...
4674            if (endEdit(DataGridEventReason.NEW_ROW) && !dontEdit)
4675                findNextEnterItemRenderer(event);
4676        }
4677    }
4678
4679    /**
4680     *  @private
4681     */
4682    private function editorStageResizeHandler(event:Event):void
4683    {
4684        if (event.target is DisplayObjectContainer &&
4685            DisplayObjectContainer(event.target).contains(this))
4686            endEdit(DataGridEventReason.OTHER);
4687    }
4688
4689    /**
4690     *  @private
4691     *  find the next item renderer down from the currently edited item renderer, and focus it.
4692     */
4693    private function findNextEnterItemRenderer(event:KeyboardEvent):void
4694    {
4695        // some other thing like a collection change has changed the
4696        // position, so bail and wait for commit to reset the editor.
4697        if (_proposedEditedItemPosition !== undefined)
4698            return;
4699
4700        _editedItemPosition = lastEditedItemPosition;
4701
4702        // though endEdit checks this, an ITEM_EDIT_END event handler can, in
4703        // the meantime, wipe out the item editor (e.g. by setting a new data
4704        // provider). so we add another check here.
4705        if (!_editedItemPosition)
4706            return;
4707
4708        var rowIndex:int = _editedItemPosition.rowIndex;
4709        var columnIndex:int = _editedItemPosition.columnIndex;
4710        // modify direction with SHIFT (up or down)
4711        var newIndex:int = _editedItemPosition.rowIndex +
4712                           (event.shiftKey ? -1 : 1);
4713        // only move if we're within range
4714        if (newIndex < collection.length && newIndex >= 0)
4715            rowIndex = newIndex;
4716
4717        // send event to create the new one
4718        beginningEdit(columnIndex, rowIndex);
4719    }
4720
4721    /**
4722     *  @private
4723     *  This gets called when the tab key is hit.
4724     */
4725    private function mouseFocusChangeHandler(event:MouseEvent):void
4726    {
4727        // trace("mouseFocus handled by " + this);
4728
4729        if (itemEditorInstance &&
4730            !event.isDefaultPrevented() &&
4731            itemRendererContains(itemEditorInstance, DisplayObject(event.target)))
4732        {
4733            event.preventDefault();
4734        }
4735    }
4736
4737    /**
4738     *  @private
4739     *  This gets called when the tab key is hit.
4740     */
4741    private function keyFocusChangeHandler(event:FocusEvent):void
4742    {
4743        // trace("tabHandled by " + this);
4744
4745        if (event.keyCode == Keyboard.TAB &&
4746            ! event.isDefaultPrevented() &&
4747            findNextItemRenderer(event.shiftKey))
4748        {
4749            event.preventDefault();
4750        }
4751    }
4752
4753    /**
4754     *  @private
4755     *  Hides the itemEditorInstance.
4756     */
4757    private function itemEditorFocusOutHandler(event:FocusEvent):void
4758    {
4759        // trace("itemEditorFocusOut " + event.relatedObject);
4760        if (event.relatedObject && contains(event.relatedObject))
4761            return;
4762
4763        // ignore textfields losing focus on mousedowns
4764        if (!event.relatedObject)
4765            return;
4766
4767        // trace("endEdit from itemEditorFocusOut");
4768        if (itemEditorInstance)
4769            endEdit(DataGridEventReason.OTHER);
4770    }
4771
4772    /**
4773     *  @private
4774     */
4775    private function itemEditorItemEditBeginningHandler(event:DataGridEvent):void
4776    {
4777        // trace("itemEditorItemEditBeginningHandler");
4778        if (!event.isDefaultPrevented())
4779            setEditedItemPosition({columnIndex: event.columnIndex, rowIndex: event.rowIndex});
4780        else if (!itemEditorInstance)
4781        {
4782            _editedItemPosition = null;
4783            // return focus to the grid w/o selecting an item
4784            editable = false;
4785            setFocus();
4786            editable = true;
4787        }
4788    }
4789
4790    /**
4791     *  @private
4792     *  focus an item renderer in the grid - harder than it looks
4793     */
4794    private function itemEditorItemEditBeginHandler(event:DataGridEvent):void
4795    {
4796		if (root)
4797	        systemManager.addEventListener(Event.DEACTIVATE, deactivateHandler, false, 0, true);
4798
4799        // if not prevented and if data is not null (might be from dataservices)
4800        if (!event.isDefaultPrevented() && actualContentHolder.listItems[actualRowIndex][actualColIndex].data != null)
4801        {
4802            createItemEditor(event.columnIndex, event.rowIndex);
4803
4804            if (editedItemRenderer is IDropInListItemRenderer && itemEditorInstance is IDropInListItemRenderer)
4805                IDropInListItemRenderer(itemEditorInstance).listData = IDropInListItemRenderer(editedItemRenderer).listData;
4806            // if rendererIsEditor, don't apply the data as the data may have already changed in some way.
4807            // This can happen if clicking on a checkbox rendererIsEditor as the checkbox will try to change
4808            // its value as we try to stuff in an old value here.
4809            if (!columns[event.columnIndex].rendererIsEditor)
4810                itemEditorInstance.data = editedItemRenderer.data;
4811
4812            if (itemEditorInstance is IInvalidating)
4813                IInvalidating(itemEditorInstance).validateNow();
4814
4815            if (itemEditorInstance is IIMESupport)
4816                IIMESupport(itemEditorInstance).imeMode =
4817                    (columns[event.columnIndex].imeMode == null) ? _imeMode : columns[event.columnIndex].imeMode;
4818
4819            var fm:IFocusManager = focusManager;
4820            // trace("setting focus to item editor");
4821            if (itemEditorInstance is IFocusManagerComponent)
4822                fm.setFocus(IFocusManagerComponent(itemEditorInstance));
4823            fm.defaultButtonEnabled = false;
4824
4825            var event:DataGridEvent =
4826                new DataGridEvent(DataGridEvent.ITEM_FOCUS_IN);
4827            event.columnIndex = _editedItemPosition.columnIndex;
4828            event.rowIndex = _editedItemPosition.rowIndex;
4829                event.itemRenderer = itemEditorInstance;
4830            dispatchEvent(event);
4831        }
4832    }
4833
4834    /**
4835     *  @private
4836     */
4837    private function itemEditorItemEditEndHandler(event:DataGridEvent):void
4838    {
4839        if (!event.isDefaultPrevented())
4840        {
4841            var bChanged:Boolean = false;
4842
4843            if (event.reason == DataGridEventReason.NEW_COLUMN)
4844            {
4845                if (!collectionUpdatesDisabled)
4846                {
4847                    collection.disableAutoUpdate();
4848                    collectionUpdatesDisabled = true;
4849                }
4850            }
4851            else
4852            {
4853                if (collectionUpdatesDisabled)
4854                {
4855                    collection.enableAutoUpdate();
4856                    collectionUpdatesDisabled = false;
4857                }
4858            }
4859
4860            if (itemEditorInstance && event.reason != DataGridEventReason.CANCELLED)
4861            {
4862                var newData:Object = itemEditorInstance[_columns[event.columnIndex].editorDataField];
4863                var property:String = _columns[event.columnIndex].dataField;
4864                var data:Object = event.itemRenderer.data;
4865                var typeInfo:String = "";
4866                for each(var variable:XML in describeType(data).variable)
4867                {
4868                    if (property == variable.@name.toString())
4869                    {
4870                        typeInfo = variable.@type.toString();
4871                        break;
4872                    }
4873                }
4874
4875                if (typeInfo == "String")
4876                {
4877                    if (!(newData is String))
4878                        newData = newData.toString();
4879                }
4880                else if (typeInfo == "uint")
4881                {
4882                    if (!(newData is uint))
4883                        newData = uint(newData);
4884                }
4885                else if (typeInfo == "int")
4886                {
4887                    if (!(newData is int))
4888                        newData = int(newData);
4889                }
4890                else if (typeInfo == "Number")
4891                {
4892                    if (!(newData is int))
4893                        newData = Number(newData);
4894                }
4895                /** Old code assumed that the property would be a simply name that could be dereferenced
4896                  * through array notation. Using a method call here provides, minimally, an override
4897                  * point where developers could extend this functionality in their own datagrid subclass **/
4898                if (property != null && getCurrentDataValue( data, property ) !== newData)
4899                {
4900                    bChanged = setNewValue( data, property, newData, event.columnIndex );
4901                }
4902                if (bChanged && !(data is IPropertyChangeNotifier || data is XML))
4903                {
4904                    collection.itemUpdated(data, property);
4905                }
4906                if (event.itemRenderer is IDropInListItemRenderer)
4907                {
4908                    var listData:DataGridListData = DataGridListData(IDropInListItemRenderer(event.itemRenderer).listData);
4909                    listData.label = _columns[event.columnIndex].itemToLabel(data);
4910                    IDropInListItemRenderer(event.itemRenderer).listData = listData;
4911                }
4912                event.itemRenderer.data = data;
4913            }
4914        }
4915        else
4916        {
4917            if (event.reason != DataGridEventReason.OTHER)
4918            {
4919                if (itemEditorInstance && _editedItemPosition)
4920                {
4921                    // edit session is continued so restore focus and selection
4922                    if (selectedIndex != _editedItemPosition.rowIndex)
4923                        selectedIndex = _editedItemPosition.rowIndex;
4924                    var fm:IFocusManager = focusManager;
4925                    // trace("setting focus to itemEditorInstance", selectedIndex);
4926                    if (itemEditorInstance is IFocusManagerComponent)
4927                        fm.setFocus(IFocusManagerComponent(itemEditorInstance));
4928                }
4929            }
4930        }
4931
4932        if (event.reason == DataGridEventReason.OTHER || !event.isDefaultPrevented())
4933        {
4934            destroyItemEditor();
4935        }
4936    }
4937
4938	protected function isComplexColumn( property:String ):Boolean
4939    {
4940        return ( property.indexOf( "." ) != -1 );
4941    }
4942
4943	//Gets the reference to the parent object where a property will be updated
4944    protected function deriveComplexFieldReference( data:Object, complexFieldNameComponents:Array ):Object
4945    {
4946        var currentRef:Object = data;
4947        if ( complexFieldNameComponents )
4948        {
4949            for ( var i:int=0; i<complexFieldNameComponents.length; i++ )
4950                currentRef = currentRef[ complexFieldNameComponents[ i ] ];
4951        }
4952
4953        return currentRef;
4954    }
4955
4956    //default implementations of these two methods, intended for subclassing
4957    //not checking if it really is a complex value here as the performance hit of doing this here is negligible
4958    //compared with every display
4959    protected function getCurrentDataValue( data:Object, property:String ):String
4960    {
4961        if ( !isComplexColumn( property ) )
4962            return data[ property ];
4963
4964        var complexFieldNameComponents:Array = property.split( "." );
4965        var obj:Object = deriveComplexFieldReference( data, complexFieldNameComponents );
4966
4967        return String( obj );
4968    }
4969
4970	//Passing all of these parameters as it basically allows everything you would need to subclass for all sorts of fun implementations
4971    protected function setNewValue( data:Object, property:String, value:Object, columnIndex:int ):Boolean
4972    {
4973        if ( !isComplexColumn( property ) )
4974        {
4975        	data[ property ] = value;
4976        }
4977        else
4978        {
4979            var complexFieldNameComponents:Array = property.split( "." );
4980            var lastProp:String = complexFieldNameComponents.pop();
4981            var parent:Object = deriveComplexFieldReference( data, complexFieldNameComponents );
4982            parent[ lastProp ] = value;
4983        }
4984
4985        //The value they typed in is always converted to a string, but is the value actually a string in the dataprovider?
4986        //unknown as it is cast by datagridcolumn before datagrid ever gets to know...
4987        //control if this really causes an update in subclass
4988        return true;
4989    }
4990
4991    /**
4992     *  @private
4993     */
4994    private function headerReleaseHandler(event:DataGridEvent):void
4995    {
4996        if (!event.isDefaultPrevented())
4997        {
4998            manualSort = true;
4999            sortByColumn(event.columnIndex);
5000            manualSort = false;
5001        }
5002    }
5003
5004    /**
5005     *  @private
5006     */
5007    override protected function mouseWheelHandler(event:MouseEvent):void
5008    {
5009        if (itemEditorInstance)
5010            endEdit(DataGridEventReason.OTHER);
5011
5012        super.mouseWheelHandler(event);
5013    }
5014
5015    /**
5016     *  @private
5017     *  if some drags from the same row as an editor we can be left
5018     *  with updates disabled
5019     */
5020    override protected function dragStartHandler(event:DragEvent):void
5021    {
5022        if (collectionUpdatesDisabled)
5023        {
5024            collection.enableAutoUpdate();
5025            collectionUpdatesDisabled = false;
5026        }
5027        super.dragStartHandler(event);
5028    }
5029
5030    mx_internal function get vScrollBar():ScrollBar
5031    {
5032        return verticalScrollBar;
5033    }
5034
5035    /**
5036     *  diagnostics
5037     */
5038    override mx_internal function get rendererArray():Array
5039    {
5040        var arr:Array = listItems.slice();
5041        var arr2:Array = DataGridHeader(header).rendererArray;
5042        arr.unshift(arr2);
5043        return arr;
5044    }
5045
5046    /**
5047     *  diagnostics
5048     */
5049    mx_internal function get sortArrow():IFlexDisplayObject
5050    {
5051        return DataGridHeader(header).sortArrow;
5052    }
5053
5054    /**
5055     *  Called from the <code>updateDisplayList()</code> method to adjust the size and position of
5056     *  listContent.
5057     *
5058     *  @param unscaledWidth The width of the listContent. This value ignores changes to the width from external components or settings.
5059     *  @param unscaledHeight The height of the listContent. This value ignores changes to the height from external components or settings.
5060     */
5061    override protected function adjustListContent(unscaledWidth:Number = -1,
5062                                       unscaledHeight:Number = -1):void
5063    {
5064        var ww:Number;
5065        var hh:Number = 0;
5066        var lcx:Number;
5067        var lcy:Number;
5068        var hcx:Number;
5069
5070        if (headerVisible)
5071        {
5072            if (lockedColumnCount > 0)
5073            {
5074                lockedColumnHeader.visible = true;
5075                hcx = viewMetrics.left + Math.min(DataGridHeader(lockedColumnHeader).leftOffset, 0);
5076                lockedColumnHeader.move(hcx, viewMetrics.top);
5077                hh = lockedColumnHeader.getExplicitOrMeasuredHeight();
5078                lockedColumnHeader.setActualSize(lockedColumnWidth + 1, hh);
5079                DataGridHeader(lockedColumnHeader).needRightSeparator = true;
5080                DataGridHeader(lockedColumnHeader).needRightSeparatorEvents = true;
5081            }
5082            header.visible = true;
5083            hcx = viewMetrics.left + lockedColumnWidth + Math.min(DataGridHeader(header).leftOffset, 0);
5084            header.move(hcx, viewMetrics.top);
5085            // If we have a vScroll only, we want the scrollbar to be below
5086            // the header.
5087            if (verticalScrollBar != null && verticalScrollBar.visible &&
5088               (horizontalScrollBar == null || !horizontalScrollBar.visible) && headerVisible &&
5089               roomForScrollBar(verticalScrollBar, unscaledWidth, unscaledHeight-header.height))
5090                ww = Math.max(0, DataGridHeader(header).rightOffset) - hcx - borderMetrics.right;
5091            else
5092                ww = Math.max(0, DataGridHeader(header).rightOffset) - hcx - viewMetrics.right;
5093            hh = header.getExplicitOrMeasuredHeight();
5094            header.setActualSize(unscaledWidth + ww, hh);
5095            if (!skipHeaderUpdate)
5096            {
5097                header.headerItemsChanged = true;
5098                header.invalidateDisplayList(); // make sure it redraws, even if size didn't change
5099                // internal renderers could have changed
5100            }
5101        }
5102        else
5103        {
5104            header.visible = false;
5105            if (lockedColumnCount > 0)
5106                lockedColumnHeader.visible = false;
5107        }
5108
5109        if (lockedRowCount > 0 && lockedRowContent && lockedRowContent.iterator)
5110        {
5111            try
5112            {
5113                lockedRowContent.iterator.seek(CursorBookmark.FIRST);
5114                var pt:Point = makeRows(lockedRowContent, 0, 0, unscaledWidth, unscaledHeight, 0, 0, true, lockedRowCount, true);
5115
5116                if (lockedColumnCount > 0)
5117                {
5118                    lcx = viewMetrics.left + Math.min(lockedColumnAndRowContent.leftOffset, 0);
5119                    lcy = viewMetrics.top + Math.min(lockedColumnAndRowContent.topOffset, 0) + Math.ceil(hh);
5120                    lockedColumnAndRowContent.move(lcx, lcy);
5121                    lockedColumnAndRowContent.setActualSize(lockedColumnWidth, lockedColumnAndRowContent.getExplicitOrMeasuredHeight());
5122                }
5123                lcx = viewMetrics.left + lockedColumnWidth + Math.min(lockedRowContent.leftOffset, 0);
5124                lcy = viewMetrics.top + Math.min(lockedRowContent.topOffset, 0) + Math.ceil(hh);
5125                lockedRowContent.move(lcx, lcy);
5126                ww = Math.max(0, lockedRowContent.rightOffset) - lcx - viewMetrics.right;
5127                lockedRowContent.setActualSize(unscaledWidth + ww, lockedRowContent.getExplicitOrMeasuredHeight());
5128                hh += lockedRowContent.getExplicitOrMeasuredHeight();
5129            }
5130            catch (e:ItemPendingError)
5131            {
5132                e.addResponder(new ItemResponder(lockedRowSeekPendingResultHandler, seekPendingFailureHandler,
5133                                                    null));
5134
5135            }
5136        }
5137
5138        if (lockedColumnCount > 0)
5139        {
5140            lcx = viewMetrics.left + Math.min(lockedColumnContent.leftOffset, 0);
5141            lcy = viewMetrics.top + Math.min(lockedColumnContent.topOffset, 0) + Math.ceil(hh);
5142            lockedColumnContent.move(lcx, lcy);
5143            ww = lockedColumnWidth + lockedColumnContent.rightOffset - lockedColumnContent.leftOffset;
5144            lockedColumnContent.setActualSize(ww,
5145                    unscaledHeight + Math.max(0, lockedColumnContent.bottomOffset) - lcy - viewMetrics.bottom);
5146        }
5147        lcx = viewMetrics.left + lockedColumnWidth + Math.min(listContent.leftOffset, 0);
5148        lcy = viewMetrics.top + Math.min(listContent.topOffset, 0) + Math.ceil(hh);
5149        listContent.move(lcx, lcy);
5150        ww = Math.max(0, listContent.rightOffset) - lcx - viewMetrics.right;
5151        hh = Math.max(0, listContent.bottomOffset) - lcy - viewMetrics.bottom;
5152        listContent.setActualSize(Math.max(0, unscaledWidth + ww), Math.max(0, unscaledHeight + hh));
5153
5154    }
5155
5156    private function lockedRowSeekPendingResultHandler(data:Object,
5157                                                info:ListBaseSeekPending):void
5158    {
5159        try
5160        {
5161            lockedRowContent.iterator.seek(CursorBookmark.FIRST);
5162        }
5163        catch(e:ItemPendingError)
5164        {
5165            e.addResponder(new ItemResponder(lockedRowSeekPendingResultHandler, seekPendingFailureHandler,
5166                                                null));
5167        }
5168        itemsSizeChanged = true;
5169        invalidateDisplayList();
5170    }
5171
5172
5173    /**
5174     *  @inheritDoc
5175     */
5176    override protected function scrollPositionToIndex(horizontalScrollPosition:int,
5177                                             verticalScrollPosition:int):int
5178    {
5179        return iterator ? verticalScrollPosition + lockedRowCount : -1;
5180    }
5181
5182    /**
5183     *  @inheritDoc
5184     */
5185    override protected function scrollVertically(pos:int, deltaPos:int,
5186                                        scrollUp:Boolean):void
5187    {
5188        super.scrollVertically(pos, deltaPos, scrollUp);
5189        if (getStyle("horizontalGridLines"))
5190        {
5191            drawLinesAndColumnGraphics(listContent, visibleColumns, {});
5192            if (lockedColumnCount)
5193            {
5194                drawLinesAndColumnGraphics(lockedColumnContent, visibleLockedColumns, { right: 1})
5195            }
5196        }
5197    }
5198
5199    private var _focusPane:Sprite;
5200
5201    /**
5202     *  @private
5203     */
5204    override public function set focusPane(value:Sprite):void
5205    {
5206        super.focusPane = value;
5207        if (!value && _focusPane)
5208            _focusPane.mask = null;
5209        _focusPane = value;
5210    }
5211
5212    mx_internal var lockedColumnDropIndicator:IFlexDisplayObject;
5213
5214    /**
5215     *  @private
5216     */
5217    override public function showDropFeedback(event:DragEvent):void
5218    {
5219        super.showDropFeedback(event);
5220
5221        if (lockedColumnCount > 0)
5222        {
5223            if (!lockedColumnDropIndicator)
5224            {
5225                var dropIndicatorClass:Class = getStyle("dropIndicatorSkin");
5226                if (!dropIndicatorClass)
5227                    dropIndicatorClass = ListDropIndicator;
5228                lockedColumnDropIndicator = IFlexDisplayObject(new dropIndicatorClass());
5229
5230                lockedColumnDropIndicator.x = 2;
5231                lockedColumnDropIndicator.setActualSize(lockedColumnContent.width - 2, 4);
5232                lockedColumnDropIndicator.visible = true;
5233            }
5234            if (dropIndicator.parent == listContent)
5235                lockedColumnContent.addChild(DisplayObject(lockedColumnDropIndicator));
5236            else
5237                lockedColumnAndRowContent.addChild(DisplayObject(lockedColumnDropIndicator));
5238
5239
5240            lockedColumnDropIndicator.y = dropIndicator.y;
5241        }
5242    }
5243
5244    /**
5245     *  @private
5246     */
5247    override public function hideDropFeedback(event:DragEvent):void
5248    {
5249        super.hideDropFeedback(event);
5250
5251        if (lockedColumnDropIndicator)
5252        {
5253            DisplayObject(lockedColumnDropIndicator).parent.removeChild(DisplayObject(lockedColumnDropIndicator));
5254            lockedColumnDropIndicator = null;
5255        }
5256    }
5257
5258}
5259
5260}
5261