| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // Licensed to the Apache Software Foundation (ASF) under one or more |
| // contributor license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright ownership. |
| // The ASF licenses this file to You under the Apache License, Version 2.0 |
| // (the "License"); you may not use this file except in compliance with |
| // the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| package mx.controls |
| { |
| |
| import flash.display.DisplayObject; |
| import flash.display.DisplayObjectContainer; |
| import flash.display.InteractiveObject; |
| import flash.display.Graphics; |
| import flash.display.Shape; |
| import flash.display.Sprite; |
| import flash.events.Event; |
| import flash.events.FocusEvent; |
| import flash.events.KeyboardEvent; |
| import flash.events.MouseEvent; |
| import flash.geom.Point; |
| import flash.ui.Keyboard; |
| import flash.utils.Dictionary; |
| import flash.utils.describeType; |
| |
| import mx.collections.CursorBookmark; |
| import mx.collections.ICollectionView; |
| import mx.collections.ISort; |
| import mx.collections.ISortField; |
| import mx.collections.ItemResponder; |
| import mx.collections.Sort; |
| import mx.collections.SortField; |
| import mx.collections.errors.ItemPendingError; |
| import mx.controls.dataGridClasses.DataGridBase; |
| import mx.controls.dataGridClasses.DataGridColumn; |
| import mx.controls.dataGridClasses.DataGridDragProxy; |
| import mx.controls.dataGridClasses.DataGridHeader; |
| import mx.controls.dataGridClasses.DataGridItemRenderer; |
| import mx.controls.dataGridClasses.DataGridListData; |
| import mx.controls.listClasses.IDropInListItemRenderer; |
| import mx.controls.listClasses.IListItemRenderer; |
| import mx.controls.listClasses.ListBaseContentHolder; |
| import mx.controls.listClasses.ListBaseSeekPending; |
| import mx.controls.listClasses.ListRowInfo; |
| import mx.controls.scrollClasses.ScrollBar; |
| import mx.core.ContextualClassFactory; |
| import mx.core.EdgeMetrics; |
| import mx.core.EventPriority; |
| import mx.core.FlexShape; |
| import mx.core.FlexSprite; |
| import mx.core.IFactory; |
| import mx.core.IFlexDisplayObject; |
| import mx.core.IFlexModuleFactory; |
| import mx.core.IIMESupport; |
| import mx.core.IInvalidating; |
| import mx.core.IPropertyChangeNotifier; |
| import mx.core.IRectangularBorder; |
| import mx.core.IUIComponent; |
| import mx.core.ScrollPolicy; |
| import mx.core.UIComponent; |
| import mx.core.UIComponentGlobals; |
| import mx.core.mx_internal; |
| import mx.events.CollectionEvent; |
| import mx.events.CollectionEventKind; |
| import mx.events.DataGridEvent; |
| import mx.events.DataGridEventReason; |
| import mx.events.DragEvent; |
| import mx.events.IndexChangedEvent; |
| import mx.events.ListEvent; |
| import mx.events.SandboxMouseEvent; |
| import mx.events.ScrollEvent; |
| import mx.events.ScrollEventDetail; |
| import mx.managers.IFocusManager; |
| import mx.managers.IFocusManagerComponent; |
| import mx.skins.halo.ListDropIndicator; |
| import mx.styles.ISimpleStyleClient; |
| import mx.utils.ObjectUtil; |
| import mx.utils.StringUtil; |
| |
| use namespace mx_internal; |
| |
| //-------------------------------------- |
| // Events |
| //-------------------------------------- |
| |
| /** |
| * Dispatched when the user releases the mouse button while over an item |
| * renderer, tabs to the DataGrid control or within the DataGrid control, |
| * or in any other way attempts to edit an item. |
| * |
| * @eventType mx.events.DataGridEvent.ITEM_EDIT_BEGINNING |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="itemEditBeginning", type="mx.events.DataGridEvent")] |
| |
| /** |
| * Dispatched when the <code>editedItemPosition</code> property has been set |
| * and the item can be edited. |
| * |
| * @eventType mx.events.DataGridEvent.ITEM_EDIT_BEGIN |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="itemEditBegin", type="mx.events.DataGridEvent")] |
| |
| /** |
| * Dispatched when the item editor has just been instantiated. |
| * |
| * @eventType mx.events.DataGridEvent.ITEM_EDITOR_CREATE |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="itemEditorCreate", type="mx.events.DataGridEvent")] |
| |
| /** |
| * Dispatched when an item editing session ends for any reason. |
| * |
| * @eventType mx.events.DataGridEvent.ITEM_EDIT_END |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="itemEditEnd", type="mx.events.DataGridEvent")] |
| |
| /** |
| * Dispatched when an item renderer gets focus, which can occur if the user |
| * clicks on an item in the DataGrid control or navigates to the item using |
| * a keyboard. Only dispatched if the item is editable. |
| * |
| * @eventType mx.events.DataGridEvent.ITEM_FOCUS_IN |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="itemFocusIn", type="mx.events.DataGridEvent")] |
| |
| /** |
| * Dispatched when an item renderer loses focus, which can occur if the user |
| * clicks another item in the DataGrid control or clicks outside the control, |
| * or uses the keyboard to navigate to another item in the DataGrid control |
| * or outside the control. |
| * Only dispatched if the item is editable. |
| * |
| * @eventType mx.events.DataGridEvent.ITEM_FOCUS_OUT |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="itemFocusOut", type="mx.events.DataGridEvent")] |
| |
| /** |
| * Dispatched when a user changes the width of a column, indicating that the |
| * amount of data displayed in that column may have changed. |
| * If <code>horizontalScrollPolicy</code> is <code>"off"</code>, other |
| * columns shrink or expand to compensate for the columns' resizing, |
| * and they also dispatch this event. |
| * |
| * @eventType mx.events.DataGridEvent.COLUMN_STRETCH |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="columnStretch", type="mx.events.DataGridEvent")] |
| |
| /** |
| * Dispatched when the user releases the mouse button on a column header |
| * to request the control to sort |
| * the grid contents based on the contents of the column. |
| * Only dispatched if the column is sortable and the data provider supports |
| * sorting. The DataGrid control has a default handler for this event that implements |
| * a single-column sort. Multiple-column sort can be implemented by calling the |
| * <code>preventDefault()</code> method to prevent the single column sort and setting |
| * the <code>sort</code> property of the data provider. |
| * <p> |
| * <b>Note</b>: The sort arrows are defined by the default event handler for |
| * the headerRelease event. If you call the <code>preventDefault()</code> method |
| * in your event handler, the arrows are not drawn. |
| * </p> |
| * |
| * @eventType mx.events.DataGridEvent.HEADER_RELEASE |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="headerRelease", type="mx.events.DataGridEvent")] |
| |
| /** |
| * Dispatched when the user releases the mouse button on a column header after |
| * having dragged the column to a new location resulting in shifting the column |
| * to a new index. |
| * |
| * @eventType mx.events.IndexChangedEvent.HEADER_SHIFT |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="headerShift", type="mx.events.IndexChangedEvent")] |
| |
| //-------------------------------------- |
| // Styles |
| //-------------------------------------- |
| |
| include "../styles/metadata/IconColorStyles.as" |
| |
| /** |
| * Name of the class of the itemEditor to be used if one is not |
| * specified for a column. This is a way to set |
| * an item editor for a group of DataGrids instead of having to |
| * set each one individually. If you set the DataGridColumn's itemEditor |
| * property, it supercedes this value. |
| * @default null |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| [Style(name="defaultDataGridItemEditor", type="Class", inherit="no")] |
| |
| /** |
| * Name of the class of the itemRenderer to be used if one is not |
| * specified for a column or its header. This is a way to set |
| * an itemRenderer for a group of DataGrids instead of having to |
| * set each one individually. If you set the DataGrid's itemRenderer |
| * property, it supercedes this value. |
| * @default null |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| [Style(name="defaultDataGridItemRenderer", type="Class", inherit="no")] |
| |
| /** |
| * A flag that indicates whether to show vertical grid lines between |
| * the columns. |
| * If <code>true</code>, shows vertical grid lines. |
| * If <code>false</code>, hides vertical grid lines. |
| * @default true |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="verticalGridLines", type="Boolean", inherit="no")] |
| |
| /** |
| * A flag that indicates whether to show horizontal grid lines between |
| * the rows. |
| * If <code>true</code>, shows horizontal grid lines. |
| * If <code>false</code>, hides horizontal grid lines. |
| * @default false |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="horizontalGridLines", type="Boolean", inherit="no")] |
| |
| /** |
| * The color of the vertical grid lines. |
| * |
| * The default value for the Halo theme is <code>0xCCCCCC</code>. |
| * The default value for the Spark theme is <code>0x696969</code>. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="verticalGridLineColor", type="uint", format="Color", inherit="yes")] |
| |
| /** |
| * The color of the horizontal grid lines. |
| * @default 0xF7F7F7 |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="horizontalGridLineColor", type="uint", format="Color", inherit="yes")] |
| |
| /** |
| * An array of two colors used to draw the header background gradient. |
| * The first color is the top color. |
| * The second color is the bottom color. |
| * @default [0xFFFFFF, 0xE6E6E6] |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="headerColors", type="Array", arrayType="uint", format="Color", inherit="yes", theme="halo")] |
| |
| /** |
| * The color of the row background when the user rolls over the row. |
| * |
| * The default value for the Halo theme is <code>0xB2E1FF</code>. |
| * The default value for the Spark theme is <code>0xCEDBEF</code>. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="rollOverColor", type="uint", format="Color", inherit="yes")] |
| |
| /** |
| * The color of the background for the row when the user selects |
| * an item renderer in the row. |
| * |
| * The default value for the Halo theme is <code>0x7FCEFF</code>. |
| * The default value for the Spark theme is <code>0xA8C6EE</code>. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="selectionColor", type="uint", format="Color", inherit="yes")] |
| |
| /** |
| * The name of a CSS style declaration for controlling other aspects of |
| * the appearance of the column headers. |
| * @default "dataGridStyles" |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="headerStyleName", type="String", inherit="no")] |
| |
| /** |
| * The class to use as the skin for a column that is being resized. |
| * |
| * @default mx.skins.halo.DataGridColumnResizeSkin (for both Halo and Spark themes) |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="columnResizeSkin", type="Class", inherit="no")] |
| |
| |
| /** |
| * The class to use as the skin that defines the appearance of the |
| * background of the column headers in a DataGrid control. |
| * The default value for the Halo theme is <code>mx.skins.halo.DataGridHeaderBackgroundSkin</code>. |
| * The default value for the Spark theme is <code>mx.skins.spark.DataGridHeaderBackgroundSkin</code>. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="headerBackgroundSkin", type="Class", inherit="no")] |
| |
| /** |
| * The class to use as the skin that defines the appearance of the |
| * separator between column headers in a DataGrid control. |
| * The default value for the Halo theme is <code>mx.skins.halo.DataGridHeaderSeparator</code>. |
| * The default value for the Spark theme is <code>mx.skins.spark.DataGridHeaderSeparatorSkin</code>. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="headerSeparatorSkin", type="Class", inherit="no")] |
| |
| /** |
| * The class to use as the skin that defines the appearance of the |
| * separator between rows in a DataGrid control. |
| * By default, the DataGrid control uses the |
| * <code>drawHorizontalLine()</code> and <code>drawVerticalLine()</code> methods |
| * to draw the separators. |
| * |
| * @default undefined |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="horizontalSeparatorSkin", type="Class", inherit="no")] |
| |
| /** |
| * The class to use as the skin that defines the appearance of the |
| * separator between the locked and unlocked rows in a DataGrid control. |
| * By default, the DataGrid control uses the |
| * <code>drawHorizontalLine()</code> and <code>drawVerticalLine()</code> methods |
| * to draw the separators. |
| * |
| * @default undefined |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="horizontalLockedSeparatorSkin", type="Class", inherit="no")] |
| |
| /** |
| * The class to use as the skin that defines the appearance of the |
| * separators between columns in a DataGrid control. |
| * By default, the DataGrid control uses the |
| * <code>drawHorizontalLine()</code> and <code>drawVerticalLine()</code> methods |
| * to draw the separators. |
| * |
| * @default undefined |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="verticalSeparatorSkin", type="Class", inherit="no")] |
| |
| /** |
| * The class to use as the skin that defines the appearance of the |
| * separator between the locked and unlocked columns in a DataGrid control. |
| * By default, the DataGrid control uses the |
| * <code>drawHorizontalLine()</code> and <code>drawVerticalLine()</code> methods |
| * to draw the separators. |
| * |
| * @default undefined |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="verticalLockedSeparatorSkin", type="Class", inherit="no")] |
| |
| /** |
| * The class to use as the skin for the arrow that indicates the column sort |
| * direction. |
| * The default value for the Halo theme is <code>mx.skins.halo.DataGridSortArrow</code>. |
| * The default value for the Spark theme is <code>mx.skins.spark.DataGridSortArrow</code>. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="sortArrowSkin", type="Class", inherit="no")] |
| |
| /** |
| * The class to use as the skin for the cursor that indicates that a column |
| * can be resized. |
| * The default value is the "cursorStretch" symbol from the Assets.swf file. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="stretchCursor", type="Class", inherit="no")] |
| |
| /** |
| * The class to use as the skin that indicates that |
| * a column can be dropped in the current location. |
| * |
| * @default mx.skins.halo.DataGridColumnDropIndicator (for both Halo and Spark themes) |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="columnDropIndicatorSkin", type="Class", inherit="no")] |
| |
| /** |
| * The name of a CSS style declaration for controlling aspects of the |
| * appearance of column when the user is dragging it to another location. |
| * |
| * @default "headerDragProxyStyle" |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="headerDragProxyStyleName", type="String", inherit="no")] |
| |
| //-------------------------------------- |
| // Excluded APIs |
| //-------------------------------------- |
| |
| [Exclude(name="columnCount", kind="property")] |
| [Exclude(name="columnWidth", kind="property")] |
| [Exclude(name="iconField", kind="property")] |
| [Exclude(name="iconFunction", kind="property")] |
| [Exclude(name="labelField", kind="property")] |
| [Exclude(name="offscreenExtraRowsOrColumns", kind="property")] |
| [Exclude(name="offscreenExtraRows", kind="property")] |
| [Exclude(name="offscreenExtraRowsTop", kind="property")] |
| [Exclude(name="offscreenExtraRowsBottom", kind="property")] |
| [Exclude(name="offscreenExtraColumns", kind="property")] |
| [Exclude(name="offscreenExtraColumnsLeft", kind="property")] |
| [Exclude(name="offscreenExtraColumnsRight", kind="property")] |
| [Exclude(name="offscreenExtraRowsOrColumnsChanged", kind="property")] |
| [Exclude(name="showDataTips", kind="property")] |
| [Exclude(name="cornerRadius", kind="style")] |
| |
| //-------------------------------------- |
| // Other metadata |
| //-------------------------------------- |
| |
| [AccessibilityClass(implementation="mx.accessibility.DataGridAccImpl")] |
| |
| [DataBindingInfo("acceptedTypes", "{ dataProvider: "String" }")] |
| |
| [DefaultBindingProperty(source="selectedItem", destination="dataProvider")] |
| |
| [DefaultProperty("dataProvider")] |
| |
| [DefaultTriggerEvent("change")] |
| |
| [IconFile("DataGrid.png")] |
| |
| [RequiresDataBinding(true)] |
| |
| [Alternative(replacement="spark.components.DataGrid", since="4.5")] |
| |
| /** |
| * The <code>DataGrid</code> control is like a List except that it can |
| * show more than one column of data making it suited for showing |
| * objects with multiple properties. |
| * <p> |
| * The DataGrid control provides the following features: |
| * <ul> |
| * <li>Columns of different widths or identical fixed widths</li> |
| * <li>Columns that the user can resize at runtime </li> |
| * <li>Columns that the user can reorder at runtime </li> |
| * <li>Optional customizable column headers</li> |
| * <li>Ability to use a custom item renderer for any column to display |
| * data |
| * other than text</li> |
| * <li>Support for sorting the data by clicking on a column</li> |
| * </ul> |
| * </p> |
| * The DataGrid control is intended for viewing data, and not as a |
| * layout tool like an HTML table. |
| * The mx.containers package provides those layout tools. |
| * |
| * <p>The DataGrid control has the following default sizing |
| * characteristics:</p> |
| * <table class="innertable"> |
| * <tr> |
| * <th>Characteristic</th> |
| * <th>Description</th> |
| * </tr> |
| * <tr> |
| * <td>Default size</td> |
| * <td>If the columns are empty, the default width is 300 |
| * pixels. If the columns contain information but define |
| * no explicit widths, the default width is 100 pixels |
| * per column. The DataGrid width is sized to fit the |
| * width of all columns, if possible. |
| * The default number of displayed rows, including the |
| * header is 7, and each row, by default, is 20 pixels |
| * high. |
| * </td> |
| * </tr> |
| * <tr> |
| * <td>Minimum size</td> |
| * <td>0 pixels.</td> |
| * </tr> |
| * <tr> |
| * <td>Maximum size</td> |
| * <td>5000 by 5000.</td> |
| * </tr> |
| * </table> |
| * |
| * @mxml |
| * <p> |
| * The <code><mx:DataGrid></code> tag inherits all of the tag |
| * attributes of its superclass, except for <code>labelField</code>, |
| * <code>iconField</code>, and <code>iconFunction</code>, and adds the |
| * following tag attributes: |
| * </p> |
| * <pre> |
| * <mx:DataGrid |
| * <b>Properties</b> |
| * columns="<i>From dataProvider</i>" |
| * draggableColumns="true|false" |
| * editable="false|true" |
| * editedItemPosition="<code>null</code>" |
| * horizontalScrollPosition="null" |
| * imeMode="null" |
| * itemEditorInstance="null" |
| * minColumnWidth="<code>NaN</code>" |
| * resizableColumns="true|false" |
| * sortableColumns="true|false" |
| * |
| * <b>Styles</b> |
| * backgroundDisabledColor="0xEFEEEF" |
| * columnDropIndicatorSkin="DataGridColumnDropIndicator" |
| * columnResizeSkin="DataGridColumnResizeSkin" |
| * disabledIconColor="0x999999" |
| * headerColors="[#FFFFFF, #E6E6E6]" |
| * headerDragProxyStyleName="headerDragProxyStyle" |
| * headerSeparatorSkin="DataGridHeaderSeparator" |
| * headerStyleName="dataGridStyles" |
| * horizontalGridLineColor="0xF7F7F7" |
| * horizontalGridLines="false|true" |
| * horizontalLockedSeparatorSkin="undefined" |
| * horizontalSeparatorSkin="undefined" |
| * iconColor="0x111111" |
| * rollOverColor="0xB2E1FF" |
| * selectionColor="0x7FCEFF" |
| * sortArrowSkin="DataGridSortArrow" |
| * stretchCursor="<i>"cursorStretch" symbol from the Assets.swf file</i>" |
| * verticalGridLineColor="0xCCCCCC" |
| * verticalGridLines="false|true" |
| * verticalLockedSeparatorSkin="undefined" |
| * verticalSeparatorSkin="undefined" |
| * |
| * <b>Events</b> |
| * columnStretch="<i>No default</i>" |
| * headerRelease="<i>No default</i>" |
| * headerShift="<i>No default</i>" |
| * itemEditBegin="<i>No default</i>" |
| * itemEditBeginning="<i>No default</i>" |
| * itemEditEnd="<i>No default</i>" |
| * itemFocusIn="<i>No default</i>" |
| * itemFocusOut="<i>No default</i>" |
| * /> |
| * |
| * <b>The following DataGrid code sample specifies the column order:</b> |
| * <mx:DataGrid> |
| * <mx:dataProvider> |
| * <mx:Object Artist="Pavement" Price="11.99" |
| * Album="Slanted and Enchanted"/> |
| * <mx:Object Artist="Pavement" |
| * Album="Brighten the Corners" Price="11.99"/> |
| * </mx:dataProvider> |
| * <mx:columns> |
| * <mx:DataGridColumn dataField="Album"/> |
| * <mx:DataGridColumn dataField="Price"/> |
| * </mx:columns> |
| * </mx:DataGrid> |
| * </pre> |
| * </p> |
| * |
| * @see mx.controls.dataGridClasses.DataGridItemRenderer |
| * @see mx.controls.dataGridClasses.DataGridColumn |
| * @see mx.controls.dataGridClasses.DataGridDragProxy |
| * @see mx.events.DataGridEvent |
| * |
| * @includeExample examples/SimpleDataGrid.mxml |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public class DataGrid extends DataGridBase implements IIMESupport |
| { |
| include "../core/Version.as"; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Class mixins |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Placeholder for mixin by DataGridAccImpl. |
| */ |
| mx_internal static var createAccessibilityImplementation:Function; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Constructor. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function DataGrid() |
| { |
| super(); |
| |
| _columns = []; |
| |
| // pick a default row height |
| setRowHeight(20); |
| |
| // Register default handlers for item editing and sorting events. |
| |
| addEventListener(DataGridEvent.ITEM_EDIT_BEGINNING, |
| itemEditorItemEditBeginningHandler, |
| false, EventPriority.DEFAULT_HANDLER); |
| |
| addEventListener(DataGridEvent.ITEM_EDIT_BEGIN, |
| itemEditorItemEditBeginHandler, |
| false, EventPriority.DEFAULT_HANDLER); |
| |
| addEventListener(DataGridEvent.ITEM_EDIT_END, |
| itemEditorItemEditEndHandler, |
| false, EventPriority.DEFAULT_HANDLER); |
| |
| addEventListener(DataGridEvent.HEADER_RELEASE, |
| headerReleaseHandler, |
| false, EventPriority.DEFAULT_HANDLER); |
| |
| addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| [Inspectable(environment="none")] |
| |
| /** |
| * A reference to the currently active instance of the item editor, |
| * if it exists. |
| * |
| * <p>To access the item editor instance and the new item value when an |
| * item is being edited, you use the <code>itemEditorInstance</code> |
| * property. The <code>itemEditorInstance</code> property |
| * is not valid until after the event listener for |
| * the <code>itemEditBegin</code> event executes. Therefore, you typically |
| * only access the <code>itemEditorInstance</code> property from within |
| * the event listener for the <code>itemEditEnd</code> event.</p> |
| * |
| * <p>The <code>DataGridColumn.itemEditor</code> property defines the |
| * class of the item editor |
| * and, therefore, the data type of the item editor instance.</p> |
| * |
| * <p>You do not set this property in MXML.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public var itemEditorInstance:IListItemRenderer; |
| |
| /** |
| * A reference to the item renderer |
| * in the DataGrid control whose item is currently being edited. |
| * |
| * <p>From within an event listener for the <code>itemEditBegin</code> |
| * and <code>itemEditEnd</code> events, |
| * you can access the current value of the item being edited |
| * using the <code>editedItemRenderer.data</code> property.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get editedItemRenderer():IListItemRenderer |
| { |
| if (!itemEditorInstance) return null; |
| |
| return actualContentHolder.listItems[actualRowIndex][actualColIndex]; |
| } |
| |
| /** |
| * @private |
| * true if we want to skip updating the headers during adjustListContent |
| */ |
| private var skipHeaderUpdate:Boolean = false; |
| |
| /** |
| * @private |
| * true if we want to block editing on mouseUp |
| */ |
| private var dontEdit:Boolean = false; |
| |
| /** |
| * @private |
| * true if we want to block editing on mouseUp |
| */ |
| private var losingFocus:Boolean = false; |
| |
| /** |
| * @private |
| * true if we're in the endEdit call. Used to handle |
| * some timing issues with collection updates |
| */ |
| private var inEndEdit:Boolean = false; |
| |
| /** |
| * @private |
| * true if we've disabled updates in the collection |
| */ |
| private var collectionUpdatesDisabled:Boolean = false; |
| |
| /** |
| * @private |
| * The index of the column being sorted. |
| */ |
| mx_internal var sortIndex:int = -1; |
| |
| /** |
| * @private |
| * The column being sorted. |
| */ |
| private var sortColumn:DataGridColumn; |
| |
| /** |
| * @private |
| * The direction of the sort |
| */ |
| mx_internal var sortDirection:String; |
| |
| /** |
| * @private |
| * The index of the last column being sorted on. |
| */ |
| mx_internal var lastSortIndex:int = -1; |
| |
| /** |
| * @private |
| */ |
| private var lastItemDown:IListItemRenderer; |
| |
| /** |
| * @private |
| */ |
| private var lastItemFocused:DisplayObject; |
| |
| /** |
| * @private |
| */ |
| private var displayWidth:Number; |
| |
| /** |
| * @private |
| */ |
| private var lockedColumnWidth:Number = 0; |
| |
| /** |
| * @private |
| * The column that is being moved. |
| */ |
| mx_internal var movingColumn:DataGridColumn; |
| |
| /** |
| * @private |
| * The column that is being resized. |
| */ |
| mx_internal var resizingColumn:DataGridColumn; |
| |
| /** |
| * @private |
| * Columns with visible="true" |
| */ |
| private var displayableColumns:Array; |
| |
| /** |
| * @private |
| * Whether we have auto-generated the set of columns |
| * Defaults to true so we'll run the auto-generation at init time if needed |
| */ |
| private var generatedColumns:Boolean = true; |
| |
| // last known position of item editor instance |
| private var actualRowIndex:int; |
| private var actualColIndex:int; |
| private var actualContentHolder:ListBaseContentHolder; |
| |
| /** |
| * @private |
| * Flag to indicate whether sorting is manual or programmatic. If it's |
| * not manual, we try to draw the sort arrow on the right column header. |
| */ |
| private var manualSort:Boolean; |
| |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // baselinePosition |
| //---------------------------------- |
| |
| /** |
| * @private |
| * The baselinePosition of a DataGrid is calculated |
| * for its first column header. |
| * If the headers aren't shown, it is calculated as for List. |
| */ |
| override public function get baselinePosition():Number |
| { |
| if (!validateBaselinePosition()) |
| return NaN; |
| |
| if (!showHeaders) |
| return super.baselinePosition; |
| |
| var header0:IUIComponent = DataGridHeader(header).rendererArray[0]; |
| if (!header0) |
| return super.baselinePosition; |
| |
| return header.y + header0.y + header0.baselinePosition; |
| } |
| |
| /** |
| * @private |
| * Number of columns that can be displayed. |
| * Some may be offscreen depending on horizontalScrollPolicy |
| * and the width of the DataGrid. |
| */ |
| override public function get columnCount():int |
| { |
| if (_columns) |
| return _columns.length; |
| else |
| return 0; |
| } |
| |
| //---------------------------------- |
| // enabled |
| //---------------------------------- |
| |
| [Inspectable(category="General", enumeration="true,false", defaultValue="true")] |
| |
| /** |
| * @private |
| */ |
| override public function set enabled(value:Boolean):void |
| { |
| super.enabled = value; |
| |
| if (header) |
| header.enabled = value; |
| |
| if (itemEditorInstance) |
| endEdit(DataGridEventReason.OTHER); |
| |
| invalidateDisplayList(); |
| } |
| |
| //---------------------------------- |
| // headerHeight |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| override public function set headerHeight(value:Number):void |
| { |
| super.headerHeight = value; |
| _originalHeaderHeight = isNaN(value) ? 22 : value; |
| _originalExplicitHeaderHeight = !isNaN(value); |
| } |
| |
| //---------------------------------- |
| // horizontalScrollPosition |
| //---------------------------------- |
| |
| /** |
| * The offset into the content from the left edge. |
| * This can be a pixel offset in some subclasses or some other metric |
| * like the number of columns in a DataGrid control. |
| * |
| * The DataGrid scrolls by columns so the value of the |
| * <code>horizontalScrollPosition</code> property is always |
| * in the range of 0 to the index of the columns |
| * that will make the last column visible. This is different from the |
| * List control that scrolls by pixels. The DataGrid control always aligns the left edge |
| * of a column with the left edge of the DataGrid control. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| override public function set horizontalScrollPosition(value:Number):void |
| { |
| // if not init or no data; |
| if (!initialized || listItems.length == 0) |
| { |
| super.horizontalScrollPosition = value; |
| return; |
| } |
| |
| var oldValue:int = super.horizontalScrollPosition; |
| super.horizontalScrollPosition = value; |
| |
| // columns have variable width so we need to recalc scroll parms |
| scrollAreaChanged = true; |
| |
| columnsInvalid = true; |
| calculateColumnSizes(); |
| |
| // we are going to get a full repaint so don't repaint now |
| if (itemsSizeChanged) |
| return; |
| |
| if (oldValue != value) |
| { |
| removeClipMask(); |
| |
| var bookmark:CursorBookmark; |
| |
| if (iterator) |
| bookmark = iterator.bookmark; |
| |
| clearIndicators(); |
| clearVisibleData(); |
| //if we scrolled more than the number of scrollable columns |
| makeRowsAndColumns(0, 0, listContent.width, listContent.height, 0, 0); |
| if (lockedRowCount) |
| { |
| var cursorPos:CursorBookmark; |
| cursorPos = lockedRowContent.iterator.bookmark; |
| makeRows(lockedRowContent, 0, 0, unscaledWidth, unscaledHeight, 0, 0, true, lockedRowCount); |
| if (iteratorValid) |
| lockedRowContent.iterator.seek(cursorPos, 0); |
| } |
| |
| if (headerVisible && header) |
| { |
| header.visibleColumns = visibleColumns; |
| header.headerItemsChanged = true; |
| header.invalidateSize(); |
| header.validateNow(); |
| } |
| |
| if (iterator && bookmark) |
| iterator.seek(bookmark, 0); |
| |
| invalidateDisplayList(); |
| |
| addClipMask(false); |
| } |
| } |
| |
| //---------------------------------- |
| // horizontalScrollPolicy |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Accomodates ScrollPolicy.AUTO. |
| * Makes sure column widths stay in synch. |
| * |
| * @param policy on, off, or auto |
| */ |
| override public function set horizontalScrollPolicy(value:String):void |
| { |
| super.horizontalScrollPolicy = value; |
| columnsInvalid = true; |
| itemsSizeChanged = true; |
| invalidateDisplayList(); |
| } |
| |
| //---------------------------------- |
| // itemRenderer |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| override public function set itemRenderer(value:IFactory):void |
| { |
| super.itemRenderer = value; |
| if (_columns) |
| { |
| var n:int = _columns.length; |
| for (var i:int = 0; i < n; i++) |
| { |
| _columns[i].resetFactories(); |
| } |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override public function set verticalScrollPosition(value:Number):void |
| { |
| skipHeaderUpdate = true; |
| |
| var oldValue:Number = super.verticalScrollPosition; |
| super.verticalScrollPosition = value; |
| if (oldValue != value) |
| { |
| if (lockedColumnContent) |
| drawRowGraphics(lockedColumnContent) |
| } |
| skipHeaderUpdate = false; |
| } |
| |
| /** |
| * @private |
| * |
| */ |
| override protected function createChildren():void |
| { |
| super.createChildren(); |
| |
| if (!header) |
| { |
| header = new headerClass(); |
| header.styleName = this; |
| addChild(header); |
| } |
| } |
| |
| //---------------------------------- |
| // enableIME |
| //---------------------------------- |
| |
| /** |
| * A flag that indicates whether the IME should |
| * be enabled when the component receives focus. |
| * |
| * If the editor is up, it will set enableIME |
| * accordingly. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function get enableIME():Boolean |
| { |
| return false; |
| } |
| |
| //---------------------------------- |
| // imeMode |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _imeMode:String = null; |
| |
| [Inspectable(environment="none")] |
| |
| /** |
| * Specifies the IME (input method editor) mode. |
| * The IME enables users to enter text in Chinese, Japanese, and Korean. |
| * Flex sets the specified IME mode when the control gets the focus, |
| * and sets it back to the previous value when the control loses the focus. |
| * |
| * <p>The flash.system.IMEConversionMode class defines constants for the |
| * valid values for this property. |
| * You can also specify <code>null</code> to specify no IME.</p> |
| * |
| * @see flash.system.IMEConversionMode |
| * |
| * @default null |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get imeMode():String |
| { |
| return _imeMode; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set imeMode(value:String):void |
| { |
| _imeMode = value; |
| } |
| |
| //---------------------------------- |
| // itemRenderer |
| //---------------------------------- |
| |
| /** |
| * @private |
| * |
| * Defer creations of the class factory |
| * to give a chance for the moduleFactory to be set. |
| */ |
| override public function get itemRenderer():IFactory |
| { |
| if (super.itemRenderer == null) |
| { |
| var fontName:String = |
| StringUtil.trimArrayElements(getStyle("fontFamily"), ","); |
| var fontWeight:String = getStyle("fontWeight"); |
| var fontStyle:String = getStyle("fontStyle"); |
| var bold:Boolean = (fontWeight == "bold"); |
| var italic:Boolean = (fontStyle == "italic"); |
| |
| var flexModuleFactory:IFlexModuleFactory = |
| getFontContext(fontName, bold, italic); |
| |
| var c:Class = getStyle("defaultDataGridItemRenderer"); |
| if (!c) |
| c = DataGridItemRenderer; |
| super.itemRenderer = new ContextualClassFactory( |
| c, flexModuleFactory); |
| } |
| |
| return super.itemRenderer; |
| } |
| |
| //---------------------------------- |
| // minColumnWidth |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _minColumnWidth:Number; |
| |
| /** |
| * @private |
| */ |
| private var minColumnWidthInvalid:Boolean = false; |
| |
| [Inspectable(defaultValue="NaN")] |
| |
| /** |
| * The minimum width of the columns, in pixels. If not NaN, |
| * the DataGrid control applies this value as the minimum width for |
| * all columns. Otherwise, individual columns can have |
| * their own minimum widths. |
| * |
| * @default NaN |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get minColumnWidth():Number |
| { |
| return _minColumnWidth; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set minColumnWidth(value:Number):void |
| { |
| _minColumnWidth = value; |
| minColumnWidthInvalid = true; |
| itemsSizeChanged = true; |
| columnsInvalid = true; |
| invalidateDisplayList(); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // columns |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _columns:Array; // the array of our DataGridColumns |
| |
| [Bindable("columnsChanged")] |
| [Inspectable(category="General", arrayType="mx.controls.dataGridClasses.DataGridColumn")] |
| |
| /** |
| * An array of DataGridColumn objects, one for each column that |
| * can be displayed. If not explicitly set, the DataGrid control |
| * attempts to examine the first data provider item to determine the |
| * set of properties and display those properties in alphabetic |
| * order. |
| * |
| * <p>If you want to change the set of columns, you must get this array, |
| * make modifications to the columns and order of columns in the array, |
| * and then assign the new array to the columns property. This is because |
| * the DataGrid control returned a new copy of the array of columns and therefore |
| * did not notice the changes.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| override public function get columns():Array |
| { |
| return _columns.slice(0); |
| } |
| |
| /** |
| * @private |
| */ |
| override public function set columns(value:Array):void |
| { |
| var n:int; |
| var i:int; |
| |
| n = _columns.length; |
| for (i = 0; i < n; i++) |
| { |
| columnRendererChanged(_columns[i]); |
| } |
| |
| freeItemRenderersTable = new Dictionary(false); |
| columnMap = {}; |
| |
| _columns = value.slice(0); |
| columnsInvalid = true; |
| generatedColumns = false; |
| |
| n = value.length; |
| for (i = 0; i < n; i++) |
| { |
| var column:DataGridColumn = _columns[i]; |
| column.owner = this; |
| column.colNum = i; |
| if (column.cachedHeaderRenderer) |
| { |
| var item:DisplayObject = column.cachedHeaderRenderer as DisplayObject |
| if (item.parent) |
| item.parent.removeChild(item); |
| column.cachedHeaderRenderer = null; |
| } |
| } |
| |
| updateSortIndexAndDirection(); |
| |
| fontContextChanged = true; |
| invalidateProperties(); |
| itemsSizeChanged = true; |
| invalidateDisplayList(); |
| dispatchEvent(new Event("columnsChanged")); |
| } |
| |
| /** |
| * An array of DataGridColumn objects, one for each column that |
| * can be displayed. |
| * |
| * <p>Used internally instead of using <code>columns<code> when higher |
| * performance is required.</p> |
| * |
| * <p>Use externally with caution and don't modify the array that comes |
| * back or you may get unexpected issues.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 4.11 |
| * |
| * @private |
| */ |
| mx_internal function get rawColumns():Array { |
| return _columns; |
| } |
| |
| //---------------------------------- |
| // draggableColumns |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the draggableColumns property. |
| */ |
| private var _draggableColumns:Boolean = true; |
| |
| [Inspectable(defaultValue="true")] |
| |
| /** |
| * A flag that indicates whether the user is allowed to reorder columns. |
| * If <code>true</code>, the user can reorder the columns |
| * of the DataGrid control by dragging the header cells. |
| * |
| * @default true |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get draggableColumns():Boolean |
| { |
| return _draggableColumns; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set draggableColumns(value:Boolean):void |
| { |
| _draggableColumns = value; |
| } |
| |
| //---------------------------------- |
| // editable |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for the draggableColumns property. |
| */ |
| private var _editable:Boolean = false; |
| |
| [Inspectable(category="General")] |
| |
| /** |
| * A flag that indicates whether or not the user can edit |
| * items in the data provider. |
| * If <code>true</code>, the item renderers in the control are editable. |
| * The user can click on an item renderer to open an editor. |
| * |
| * <p>You can turn off editing for individual columns of the |
| * DataGrid control using the <code>DataGridColumn.editable</code> property, |
| * or by handling the <code>itemEditBeginning</code> and |
| * <code>itemEditBegin</code> events</p> |
| * |
| * @default false |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get editable():Boolean |
| { |
| return _editable; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set editable(value:Boolean):void |
| { |
| _editable = value; |
| } |
| |
| //---------------------------------- |
| // editedItemPosition |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var bEditedItemPositionChanged:Boolean = false; |
| |
| /** |
| * @private |
| * undefined means we've processed it |
| * null means don't put up an editor |
| * {} is the coordinates for the editor |
| */ |
| private var _proposedEditedItemPosition:*; |
| |
| /** |
| * @private |
| * the last editedItemPosition and the last |
| * position where editing was attempted if editing |
| * was cancelled. We restore editing |
| * to this point if we get focus from the TAB key |
| */ |
| private var lastEditedItemPosition:*; |
| |
| /** |
| * @private |
| */ |
| private var _editedItemPosition:Object; |
| |
| /** |
| * @private |
| */ |
| private var itemEditorPositionChanged:Boolean = false; |
| |
| |
| [Bindable("itemFocusIn")] |
| |
| /** |
| * The column and row index of the item renderer for the |
| * data provider item being edited, if any. |
| * |
| * <p>This Object has two fields, <code>columnIndex</code> and |
| * <code>rowIndex</code>, |
| * the zero-based column and row indexes of the item. |
| * For example: {columnIndex:2, rowIndex:3}</p> |
| * |
| * <p>Setting this property scrolls the item into view and |
| * dispatches the <code>itemEditBegin</code> event to |
| * open an item editor on the specified item renderer.</p> |
| * |
| * @default null |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get editedItemPosition():Object |
| { |
| if (_editedItemPosition) |
| return {rowIndex: _editedItemPosition.rowIndex, |
| columnIndex: _editedItemPosition.columnIndex}; |
| else |
| return _editedItemPosition; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set editedItemPosition(value:Object):void |
| { |
| if (!value) |
| { |
| setEditedItemPosition(null); |
| return; |
| } |
| |
| var newValue:Object = {rowIndex: value.rowIndex, |
| columnIndex: value.columnIndex}; |
| |
| setEditedItemPosition(newValue); |
| } |
| |
| |
| //---------------------------------- |
| // resizableColumns |
| //---------------------------------- |
| |
| [Inspectable(category="General")] |
| |
| /** |
| * A flag that indicates whether the user can change the size of the |
| * columns. |
| * If <code>true</code>, the user can stretch or shrink the columns of |
| * the DataGrid control by dragging the grid lines between the header cells. |
| * If <code>true</code>, individual columns must also have their |
| * <code>resizable</code> properties set to <code>false</code> to |
| * prevent the user from resizing a particular column. |
| * |
| * @default true |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public var resizableColumns:Boolean = true; |
| |
| //---------------------------------- |
| // sortableColumns |
| //---------------------------------- |
| |
| [Inspectable(category="General")] |
| |
| /** |
| * A flag that indicates whether the user can sort the data provider items |
| * by clicking on a column header cell. |
| * If <code>true</code>, the user can sort the data provider items by |
| * clicking on a column header cell. |
| * The <code>DataGridColumn.dataField</code> property of the column |
| * or the <code>DataGridColumn.sortCompareFunction</code> property |
| * of the column is used as the sort field. |
| * If a column is clicked more than once |
| * the sort alternates between ascending and descending order. |
| * If <code>true</code>, individual columns can be made to not respond |
| * to a click on a header by setting the column's <code>sortable</code> |
| * property to <code>false</code>. |
| * |
| * <p>When a user releases the mouse button over a header cell, the DataGrid |
| * control dispatches a <code>headerRelease</code> event if both |
| * this property and the column's sortable property are <code>true</code>. |
| * If no handler calls the <code>preventDefault()</code> method on the event, the |
| * DataGrid sorts using that column's <code>DataGridColumn.dataField</code> or |
| * <code>DataGridColumn.sortCompareFunction</code> properties.</p> |
| * |
| * @default true |
| * |
| * @see mx.controls.dataGridClasses.DataGridColumn#dataField |
| * @see mx.controls.dataGridClasses.DataGridColumn#sortCompareFunction |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public var sortableColumns:Boolean = true; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| override public function invalidateDisplayList():void |
| { |
| super.invalidateDisplayList(); |
| if (header) |
| { |
| header.headerItemsChanged = true; |
| header.invalidateSize(); |
| header.invalidateDisplayList(); |
| } |
| if (lockedColumnHeader) |
| { |
| lockedColumnHeader.headerItemsChanged = true; |
| lockedColumnHeader.invalidateSize(); |
| lockedColumnHeader.invalidateDisplayList(); |
| } |
| } |
| |
| [Inspectable(category="Data", defaultValue="undefined")] |
| |
| /** |
| * @private |
| */ |
| override public function set dataProvider(value:Object):void |
| { |
| if (itemEditorInstance) |
| endEdit(DataGridEventReason.OTHER); |
| |
| lastEditedItemPosition = null; |
| |
| super.dataProvider = value; |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function initializeAccessibility():void |
| { |
| if (DataGrid.createAccessibilityImplementation != null) |
| DataGrid.createAccessibilityImplementation(this); |
| } |
| |
| /** |
| * @private |
| * Measures the DataGrid based on its contents, |
| * summing the total of the visible column widths. |
| */ |
| override protected function measure():void |
| { |
| super.measure(); |
| |
| if (explicitRowCount != -1) |
| { |
| measuredHeight += _explicitHeaderHeight ? headerHeight : header.getExplicitOrMeasuredHeight(); |
| measuredMinHeight += _explicitHeaderHeight ? headerHeight : header.getExplicitOrMeasuredHeight(); |
| } |
| |
| var o:EdgeMetrics = viewMetrics; |
| |
| var n:int = _columns.length; |
| if (n == 0) |
| { |
| measuredWidth = DEFAULT_MEASURED_WIDTH; |
| measuredMinWidth = DEFAULT_MEASURED_MIN_WIDTH; |
| return; |
| } |
| |
| var columnWidths:Number = 0; |
| var columnMinWidths:Number = 0; |
| for (var i:int = 0; i < n; i++) |
| { |
| if (_columns[i].visible) |
| { |
| columnWidths += _columns[i].preferredWidth; |
| if (isNaN(_minColumnWidth)) |
| columnMinWidths += _columns[i].minWidth; |
| } |
| } |
| |
| if (!isNaN(_minColumnWidth)) |
| columnMinWidths = n * _minColumnWidth; |
| |
| measuredWidth = columnWidths + o.left + o.right; |
| measuredMinWidth = columnMinWidths + o.left + o.right; |
| |
| // factor out scrollbars if policy == AUTO. See Container.viewMetrics |
| if (verticalScrollPolicy == ScrollPolicy.AUTO && |
| verticalScrollBar && verticalScrollBar.visible) |
| { |
| measuredWidth -= verticalScrollBar.minWidth; |
| measuredMinWidth -= verticalScrollBar.minWidth; |
| } |
| if (horizontalScrollPolicy == ScrollPolicy.AUTO && |
| horizontalScrollBar && horizontalScrollBar.visible) |
| { |
| measuredHeight -= horizontalScrollBar.minHeight; |
| measuredMinHeight -= horizontalScrollBar.minHeight; |
| } |
| |
| } |
| |
| /** |
| * @private |
| * Sizes and positions the column headers, columns, and items based on the |
| * size of the DataGrid. |
| */ |
| override protected function updateDisplayList(unscaledWidth:Number, |
| unscaledHeight:Number):void |
| { |
| // Note: We can't immediately call super.updateDisplayList() |
| // because the visibleColumns array must be populated first. |
| |
| // trace(">>updateDisplayList"); |
| if (displayWidth != unscaledWidth - viewMetrics.right - viewMetrics.left) |
| { |
| displayWidth = unscaledWidth - viewMetrics.right - viewMetrics.left; |
| columnsInvalid = true; |
| } |
| |
| calculateColumnSizes(); |
| |
| if (itemEditorPositionChanged) |
| { |
| itemEditorPositionChanged = false; |
| // don't do this if mouse is down on an item |
| // on mouse up, we'll let the edit session logic |
| // request a new position |
| if (!lastItemDown) |
| scrollToEditedItem(editedItemPosition.rowIndex, editedItemPosition.colIndex); |
| } |
| |
| super.updateDisplayList(unscaledWidth, unscaledHeight); |
| |
| if (collection && collection.length) |
| { |
| setRowCount(listItems.length); |
| |
| if (listItems.length) |
| setColumnCount(listItems[0].length); |
| else |
| setColumnCount(0); |
| } |
| |
| // If we have a vScroll only, we want the scrollbar to be below |
| // the header. |
| if (verticalScrollBar != null && verticalScrollBar.visible && |
| (horizontalScrollBar == null || !horizontalScrollBar.visible) && |
| headerVisible) |
| { |
| var hh:Number = header.height; |
| var bm:EdgeMetrics = borderMetrics; |
| |
| if (roomForScrollBar(verticalScrollBar, |
| unscaledWidth-bm.left-bm.right, |
| unscaledHeight-hh-bm.top-bm.bottom)) |
| { |
| verticalScrollBar.move(verticalScrollBar.x, viewMetrics.top + hh); |
| verticalScrollBar.setActualSize( |
| verticalScrollBar.width, |
| unscaledHeight - viewMetrics.top - viewMetrics.bottom - hh); |
| verticalScrollBar.visible = true; |
| headerMask.width += verticalScrollBar.getExplicitOrMeasuredWidth(); |
| |
| if (!DataGridHeader(header).needRightSeparator) |
| { |
| header.invalidateDisplayList(); |
| DataGridHeader(header).needRightSeparator = true; |
| } |
| } |
| else |
| { |
| if (DataGridHeader(header).needRightSeparator) |
| { |
| header.invalidateDisplayList(); |
| DataGridHeader(header).needRightSeparator = false; |
| } |
| } |
| } |
| else |
| { |
| if (DataGridHeader(header).needRightSeparator) |
| { |
| header.invalidateDisplayList(); |
| DataGridHeader(header).needRightSeparator = false; |
| } |
| } |
| |
| if (bEditedItemPositionChanged) |
| { |
| bEditedItemPositionChanged = false; |
| // don't do this if mouse is down on an item |
| // on mouse up, we'll let the edit session logic |
| // request a new position |
| if (!lastItemDown) |
| commitEditedItemPosition(_proposedEditedItemPosition); |
| _proposedEditedItemPosition = undefined; |
| itemsSizeChanged = false; |
| } |
| |
| drawRowBackgrounds(); |
| drawLinesAndColumnBackgrounds(); |
| |
| if (lockedRowCount && lockedRowContent) |
| { |
| drawRowGraphics(lockedRowContent); |
| drawLinesAndColumnGraphics(lockedRowContent, visibleColumns, { bottom: 1}); |
| if (lockedColumnCount) |
| { |
| drawRowGraphics(lockedColumnAndRowContent); |
| drawLinesAndColumnGraphics(lockedColumnAndRowContent, visibleLockedColumns, { right: 1, bottom: 1}); |
| } |
| } |
| if (lockedColumnCount) |
| { |
| drawRowGraphics(lockedColumnContent) |
| drawLinesAndColumnGraphics(lockedColumnContent, visibleLockedColumns, { right: 1}) |
| } |
| |
| // trace("<<updateDisplayList"); |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function makeRowsAndColumns(left:Number, top:Number, |
| right:Number, bottom:Number, |
| firstCol:int, firstRow:int, |
| byCount:Boolean = false, rowsNeeded:uint = 0):Point |
| { |
| allowItemSizeChangeNotification = false; |
| |
| var pt:Point = super.makeRowsAndColumns(left, top, right, bottom, |
| firstCol, firstRow, byCount, rowsNeeded); |
| if (itemEditorInstance) |
| { |
| actualContentHolder.setChildIndex(DisplayObject(itemEditorInstance), |
| actualContentHolder.numChildren - 1); |
| var col:DataGridColumn; |
| if (lockedColumnCount && editedItemPosition.columnIndex <= visibleLockedColumns[lockedColumnCount - 1].colNum) |
| col = visibleLockedColumns[actualColIndex]; |
| else |
| col = visibleColumns[actualColIndex]; |
| |
| var item:IListItemRenderer = actualContentHolder.listItems[actualRowIndex][actualColIndex]; |
| var rowData:ListRowInfo = actualContentHolder.rowInfo[actualRowIndex]; |
| if (item && !col.rendererIsEditor) |
| { |
| var dx:Number = col.editorXOffset; |
| var dy:Number = col.editorYOffset; |
| var dw:Number = col.editorWidthOffset; |
| var dh:Number = col.editorHeightOffset; |
| itemEditorInstance.move(item.x + dx, rowData.y + dy); |
| itemEditorInstance.setActualSize(Math.min(col.width + dw, actualContentHolder.width - itemEditorInstance.x), |
| Math.min(rowData.height + dh, actualContentHolder.height - itemEditorInstance.y)); |
| item.visible = false; |
| |
| } |
| |
| var lines:Sprite = Sprite(actualContentHolder.getChildByName("lines")); |
| if (lines) |
| actualContentHolder.setChildIndex(lines, actualContentHolder.numChildren - 1); |
| } |
| |
| allowItemSizeChangeNotification = variableRowHeight; |
| return pt; |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function commitProperties():void |
| { |
| var c:DataGridColumn; |
| var n:int; |
| var j:int; |
| |
| super.commitProperties(); |
| |
| if (fontContextChanged) |
| { |
| fontContextChanged = false; |
| n = _columns.length; |
| for (j = 0; j < n; j++) |
| { |
| c = _columns[j]; |
| c.determineFontContext(); |
| } |
| } |
| |
| if (itemsNeedMeasurement) |
| { |
| itemsNeedMeasurement = false; |
| if (isNaN(explicitRowHeight)) |
| { |
| if (iterator && _columns.length > 0) |
| { |
| //set DataGridBase.visibleColumns to the set of |
| //all columns |
| visibleColumns = columns; |
| columnsInvalid = true; |
| |
| var paddingTop:Number = getStyle("paddingTop"); |
| var paddingBottom:Number = getStyle("paddingBottom"); |
| |
| var data:Object = iterator.current; |
| var item:IListItemRenderer; |
| var ch:Number = 0; |
| n = _columns.length; |
| for (j = 0; j < n; j++) |
| { |
| c = _columns[j]; |
| |
| if (!c.visible) |
| continue; |
| |
| item = c.getMeasuringRenderer(false, data); |
| if (DisplayObject(item).parent == null) |
| listContent.addChild(DisplayObject(item)); |
| setupRendererFromData(c, item, data); |
| ch = Math.max(ch, item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop); |
| } |
| |
| // unless specified otherwise, rowheight defaults to 20 |
| setRowHeight(Math.max(ch, 20)); |
| } |
| else |
| { |
| setRowHeight(20); |
| } |
| |
| // If we don't have data yet or any column info we |
| // want to make sure to make another measurement pass when |
| // we eventually do have valid data. |
| if (!collection || collection.length == 0 || _columns.length == 0) |
| itemsNeedMeasurement = true; |
| } |
| } |
| } |
| |
| /** |
| * @private |
| * Instead of measuring the items, we measure the visible columns instead. |
| */ |
| override public function measureWidthOfItems(index:int = -1, count:int = 0):Number |
| { |
| var w:Number = 0; |
| |
| var n:int = _columns ? _columns.length : 0; |
| for (var i:int = 0; i < n; i++) |
| { |
| if (_columns[i].visible) |
| w += _columns[i].width; |
| } |
| |
| return w; |
| } |
| |
| /** |
| * @private |
| */ |
| |
| mx_internal function setupRendererFromData(c:DataGridColumn, item:IListItemRenderer, data:Object):void |
| { |
| var rowData:DataGridListData = DataGridListData(makeListData(data, itemToUID(data), 0, c.colNum, c)); |
| if (item is IDropInListItemRenderer) |
| IDropInListItemRenderer(item).listData = (data != null) ? rowData : null; |
| item.data = data; |
| item.explicitWidth = getWidthOfItem(item, c); |
| UIComponentGlobals.layoutManager.validateClient(item, true); |
| } |
| |
| /** |
| * @private |
| */ |
| override public function measureHeightOfItems(index:int = -1, count:int = 0):Number |
| { |
| return measureHeightOfItemsUptoMaxHeight(index, count); |
| } |
| |
| /** |
| * @private |
| */ |
| mx_internal function measureHeightOfItemsUptoMaxHeight(index:int = -1, count:int = 0, maxHeight:Number = -1):Number |
| { |
| if (!columns.length) |
| return rowHeight * count; |
| |
| var h:Number = 0; |
| |
| var item:IListItemRenderer; |
| var c:DataGridColumn; |
| var ch:Number = 0; |
| var n:int; |
| var j:int; |
| |
| var paddingTop:Number = getStyle("paddingTop"); |
| var paddingBottom:Number = getStyle("paddingBottom"); |
| |
| var lockedCount:int = lockedRowCount; |
| |
| if (headerVisible && count > 0 && index == -1) |
| { |
| h = calculateHeaderHeight(); |
| |
| if (maxHeight != -1 && h > maxHeight) |
| { |
| setRowCount(0); |
| return 0; |
| } |
| |
| // trace(this + " header preferredHeight = " + h); |
| count --; |
| index = 0; |
| } |
| |
| var bookmark:CursorBookmark = (iterator) ? iterator.bookmark : null; |
| |
| var bMore:Boolean = iterator != null; |
| if (index != -1 && iterator) |
| { |
| try |
| { |
| iterator.seek(CursorBookmark.FIRST, index); |
| } |
| catch(e:ItemPendingError) |
| { |
| bMore = false; |
| } |
| } |
| |
| if (lockedCount > 0) |
| { |
| try |
| { |
| collectionIterator.seek(CursorBookmark.FIRST,0); |
| } |
| catch(e:ItemPendingError) |
| { |
| bMore = false; |
| } |
| } |
| |
| for (var i:int = 0; i < count; i++) |
| { |
| var data:Object; |
| if (bMore) |
| { |
| data = (lockedCount > 0) ? collectionIterator.current : iterator.current; |
| ch = 0; |
| n = _columns.length; |
| for (j = 0; j < n; j++) |
| { |
| c = _columns[j]; |
| |
| if (!c.visible) |
| continue; |
| |
| item = c.getMeasuringRenderer(false, data); |
| if (DisplayObject(item).parent == null) |
| listContent.addChild(DisplayObject(item)); |
| setupRendererFromData(c, item, data); |
| ch = Math.ceil(Math.max(ch, variableRowHeight ? item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop : rowHeight)); |
| } |
| } |
| |
| if (maxHeight != -1 && (h + ch > maxHeight || !bMore)) |
| { |
| try |
| { |
| if (iterator) |
| iterator.seek(bookmark, 0); |
| } |
| catch(e:ItemPendingError) |
| { |
| // we don't recover here since we'd only get here if the first seek failed. |
| } |
| count = (headerVisible) ? i + 1 : i; |
| setRowCount(count); |
| return h; |
| } |
| |
| h += ch; |
| if (iterator) |
| { |
| try |
| { |
| bMore = iterator.moveNext(); |
| if (lockedCount > 0) |
| { |
| collectionIterator.moveNext(); |
| lockedCount--; |
| } |
| } |
| catch(e:ItemPendingError) |
| { |
| // if we run out of data, assume all remaining rows are the size of the previous row |
| bMore = false; |
| } |
| } |
| } |
| |
| if (iterator) |
| { |
| try |
| { |
| iterator.seek(bookmark, 0); |
| } |
| catch(e:ItemPendingError) |
| { |
| // we don't recover here since we'd only get here if the first seek failed. |
| } |
| } |
| |
| // trace("calcheight = " + h); |
| return h; |
| } |
| |
| /** |
| * @private |
| */ |
| mx_internal function calculateHeaderHeight():Number |
| { |
| if (!columns.length) |
| return rowHeight; |
| |
| // block bad behavior from PDG |
| if (!listContent) |
| return rowHeight; |
| |
| var item:IListItemRenderer; |
| var c:DataGridColumn; |
| var rowData:DataGridListData; |
| var ch:Number = 0; |
| var n:int; |
| var j:int; |
| |
| var paddingTop:Number = getStyle("paddingTop"); |
| var paddingBottom:Number = getStyle("paddingBottom"); |
| |
| if (showHeaders) |
| { |
| ch = 0; |
| n = _columns.length; |
| |
| if (_headerWordWrapPresent) |
| { |
| _headerHeight = _originalHeaderHeight; |
| _explicitHeaderHeight = _originalExplicitHeaderHeight; |
| } |
| |
| for (j = 0; j < n; j++) |
| { |
| c = _columns[j]; |
| |
| if (!c.visible) |
| continue; |
| |
| item = c.cachedHeaderRenderer; |
| if (!item) |
| { |
| item = createColumnItemRenderer(c, true, c); |
| item.styleName = c; |
| c.cachedHeaderRenderer = item; |
| } |
| if (DisplayObject(item).parent == null) |
| { |
| listContent.addChild(DisplayObject(item)); |
| item.visible = false; |
| } |
| rowData = DataGridListData(makeListData(c, uid, 0, c.colNum, c)); |
| rowMap[item.name] = rowData; |
| if (item is IDropInListItemRenderer) |
| IDropInListItemRenderer(item).listData = rowData; |
| item.data = c; |
| item.explicitWidth = c.width; |
| UIComponentGlobals.layoutManager.validateClient(item, true); |
| ch = Math.max(ch, _explicitHeaderHeight ? headerHeight : item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop); |
| |
| if (columnHeaderWordWrap(c)) |
| _headerWordWrapPresent = true; |
| } |
| |
| if (_headerWordWrapPresent) |
| { |
| // take backups |
| _originalHeaderHeight = _headerHeight; |
| _originalExplicitHeaderHeight = _explicitHeaderHeight; |
| |
| _headerHeight = ch; |
| _explicitHeaderHeight = true; |
| } |
| } |
| return ch; |
| } |
| |
| private var _headerWordWrapPresent:Boolean = false; |
| private var _originalExplicitHeaderHeight:Boolean = false; |
| private var _originalHeaderHeight:Number = 0; |
| |
| /** |
| * @private |
| */ |
| override protected function calculateRowHeight(data:Object, hh:Number, skipVisible:Boolean = false):Number |
| { |
| var item:IListItemRenderer; |
| var c:DataGridColumn; |
| |
| var n:int = columns.length; |
| var j:int; |
| var k:int = 0; |
| var l:int = visibleLockedColumns.length; |
| |
| if (skipVisible && visibleColumns.length == _columns.length) |
| return hh; |
| |
| var paddingTop:Number = getStyle("paddingTop"); |
| var paddingBottom:Number = getStyle("paddingBottom"); |
| |
| for (j = 0; j < n; j++) |
| { |
| // skip any columns that are visible |
| if (skipVisible && k < l && visibleLockedColumns[k].colNum == _columns[j].colNum) |
| { |
| k++; |
| continue; |
| } |
| if (skipVisible && k - l < visibleColumns.length && visibleColumns[k - l].colNum == _columns[j].colNum) |
| { |
| k++; |
| continue; |
| } |
| c = _columns[j]; |
| |
| if (!c.visible) |
| continue; |
| |
| item = c.getMeasuringRenderer(false, data); |
| if (DisplayObject(item).parent == null) |
| listContent.addChild(DisplayObject(item)); |
| setupRendererFromData(c, item, data); |
| hh = Math.max(hh, item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop); |
| } |
| return hh; |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function scrollHandler(event:Event):void |
| { |
| if (event.target == verticalScrollBar || |
| event.target == horizontalScrollBar) |
| { |
| // TextField.scroll bubbles so you might see it here |
| if (event is ScrollEvent) |
| { |
| if (!liveScrolling && |
| ScrollEvent(event).detail == ScrollEventDetail.THUMB_TRACK) |
| { |
| return; |
| } |
| |
| if (itemEditorInstance) |
| endEdit(DataGridEventReason.OTHER); |
| |
| var scrollBar:ScrollBar = ScrollBar(event.target); |
| var pos:Number = scrollBar.scrollPosition; |
| |
| if (scrollBar == verticalScrollBar) |
| verticalScrollPosition = pos; |
| else if (scrollBar == horizontalScrollBar) |
| horizontalScrollPosition = pos; |
| |
| super.scrollHandler(event); |
| } |
| } |
| } |
| |
| private function displayingPartialRow():Boolean |
| { |
| var index:int = listItems.length - 1 - offscreenExtraRowsBottom; |
| if (rowInfo[index].y + rowInfo[index].height > listContent.heightExcludingOffsets - listContent.topOffset) |
| return true; |
| return false; |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function configureScrollBars():void |
| { |
| if (columnsInvalid) |
| return; |
| |
| if (!displayableColumns) |
| return; |
| |
| // for purposes of computing rows, we need to accomodate |
| // the case where all the visible columns are locked columns |
| var countableContentListItems:Array = this.listItems; |
| if (visibleColumns && !visibleColumns.length && visibleLockedColumns && visibleLockedColumns.length) |
| countableContentListItems = lockedColumnContent.listItems; |
| |
| var oldHorizontalScrollBar:Object = horizontalScrollBar; |
| var oldVerticalScrollBar:Object = verticalScrollBar; |
| |
| var rowCount:int = countableContentListItems.length; |
| if (rowCount == 0) |
| { |
| // Get rid of any existing scrollbars. |
| if (oldHorizontalScrollBar || oldVerticalScrollBar) |
| // protect against situation where the scrollbars |
| // cause re-layout and the listContent is sized |
| // to zero because of number of lockedRowCount |
| if (listContent.height) |
| setScrollBarProperties(0, 0, 0, 0); |
| |
| return; |
| } |
| |
| // partial last rows don't count |
| if (rowCount > 1 && displayingPartialRow()) |
| rowCount--; |
| |
| // offset, when added to rowCount, is the index of the dataProvider |
| // item for that row. IOW, row 10 in listItems is showing dataProvider |
| // item 10 + verticalScrollPosition - lockedRowCount; |
| var offset:int = verticalScrollPosition; |
| // don't count filler rows at the bottom either. |
| var fillerRows:int = 0; |
| while (rowCount && countableContentListItems[rowCount - 1].length == 0) |
| { |
| // as long as we're past the end of the collection, add up |
| // fillerRows |
| if (collection && rowCount + offset >= collection.length - lockedRowCount) |
| { |
| rowCount--; |
| ++fillerRows; |
| } |
| else |
| break; |
| } |
| |
| // we have to scroll up. We can't have filler rows unless the scrollPosition is 0 |
| if (verticalScrollPosition > 0 && fillerRows > 0) |
| { |
| var bookmark:CursorBookmark = iterator.bookmark; |
| var rowIndex:int = bookmark.getViewIndex(); |
| if (verticalScrollPosition != rowIndex - lockedRowCount) |
| { |
| // we got totally out of sync, probably because a filter |
| // removed or added rows |
| super.verticalScrollPosition = Math.max(rowIndex - lockedRowCount, 0); |
| } |
| |
| if (adjustVerticalScrollPositionDownward(Math.max(rowCount, 1))) |
| return; |
| } |
| |
| rowCount -= (offscreenExtraRowsTop + offscreenExtraRowsBottom); |
| |
| var collectionHasRows:Boolean = collection && collection.length > 0; |
| |
| var colCount:int = (collectionHasRows && rowCount > 0) ? listItems[0].length : visibleColumns.length; |
| |
| // if the last column is visible and partially offscreen (but it isn't the only |
| // column) then adjust the column count so we can scroll to see it |
| if (collectionHasRows && rowCount > 0 && colCount > 1 && |
| listItems[0][colCount - 1].x + |
| visibleColumns[colCount - 1].width > (displayWidth - listContent.x + viewMetrics.left)) |
| colCount--; |
| else if (colCount > 1 && !collectionHasRows) |
| { |
| // the slower computation requires adding up the previous columns |
| var colX:int = 0; |
| for (var i:int = 0; i < visibleColumns.length; i++) |
| { |
| colX += visibleColumns[i].width; |
| } |
| if (colX > (displayWidth - listContent.x + viewMetrics.left)) |
| colCount--; |
| } |
| |
| // trace("configureSB", verticalScrollPosition); |
| |
| setScrollBarProperties(displayableColumns.length - lockedColumnCount, Math.max(colCount, 1), |
| collection ? collection.length - lockedRowCount : 0, |
| Math.max(rowCount, 1)); |
| |
| if ((!verticalScrollBar || !verticalScrollBar.visible) && collection && |
| collection.length - lockedRowCount > rowCount) |
| maxVerticalScrollPosition = collection.length - lockedRowCount - rowCount; |
| if ((!horizontalScrollBar || !horizontalScrollBar.visible) && |
| displayableColumns.length - lockedColumnCount > colCount - lockedColumnCount) |
| maxHorizontalScrollPosition = displayableColumns.length - lockedColumnCount - colCount; |
| } |
| |
| /** |
| * @private |
| * Makes verticalScrollPosition smaller until it is 0 or there |
| * are no empty rows. This is needed if we're scrolled to the |
| * bottom and something is deleted or the rows resize so more |
| * rows can be shown. |
| */ |
| private function adjustVerticalScrollPositionDownward(rowCount:int):Boolean |
| { |
| var bookmark:CursorBookmark = iterator.bookmark; |
| |
| // add up how much space we're currently taking with valid items |
| var h:Number = 0; |
| |
| var item:IListItemRenderer; |
| var c:DataGridColumn; |
| var ch:Number = 0; |
| var n:int; |
| var j:int; |
| |
| var paddingTop:Number = getStyle("paddingTop"); |
| var paddingBottom:Number = getStyle("paddingBottom"); |
| |
| h = rowInfo[rowCount - 1].y + rowInfo[rowCount - 1].height; |
| h = listContent.heightExcludingOffsets - listContent.topOffset - h; |
| |
| // back up one |
| var numRows:int = 0; |
| try |
| { |
| if (iterator.afterLast) |
| iterator.seek(CursorBookmark.LAST, 0) |
| else |
| var bMore:Boolean = iterator.movePrevious(); |
| } |
| catch(e:ItemPendingError) |
| { |
| bMore = false; |
| } |
| if (!bMore) |
| { |
| // reset to 0; |
| super.verticalScrollPosition = 0; |
| try |
| { |
| iterator.seek(CursorBookmark.FIRST, 0); |
| // sometimes, if the iterator is invalid we'll get lucky and succeed |
| // here, then we have to make the iterator valid again |
| if (!iteratorValid) |
| { |
| iteratorValid = true; |
| lastSeekPending = null; |
| } |
| } |
| catch(e:ItemPendingError) |
| { |
| lastSeekPending = new ListBaseSeekPending(CursorBookmark.FIRST, 0); |
| e.addResponder(new ItemResponder(seekPendingResultHandler, seekPendingFailureHandler, |
| lastSeekPending)); |
| iteratorValid = false; |
| invalidateList(); |
| return true; |
| } |
| updateList(); |
| return true; |
| } |
| |
| // now work backwards to see how many more rows we need to create |
| while (h > 0 && bMore) |
| { |
| var data:Object; |
| if (bMore) |
| { |
| data = iterator.current; |
| ch = 0; |
| n = _columns.length; |
| for (j = 0; j < n; j++) |
| { |
| c = _columns[j]; |
| |
| if (!c.visible) |
| continue; |
| |
| if (variableRowHeight) |
| { |
| item = c.getMeasuringRenderer(false, data); |
| if (DisplayObject(item).parent == null) |
| listContent.addChild(DisplayObject(item)); |
| setupRendererFromData(c, item, data); |
| } |
| ch = Math.max(ch, variableRowHeight ? item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop : rowHeight); |
| } |
| } |
| h -= ch; |
| try |
| { |
| bMore = iterator.movePrevious(); |
| numRows++; |
| } |
| catch(e:ItemPendingError) |
| { |
| // if we run out of data, assume all remaining rows are the size of the previous row |
| bMore = false; |
| } |
| } |
| // if we overrun, go back one. |
| if (h < 0) |
| { |
| numRows--; |
| } |
| |
| iterator.seek(bookmark, 0); |
| verticalScrollPosition = Math.max(0, verticalScrollPosition - numRows); |
| |
| // make sure we get through configureScrollBars w/o coming in here. |
| if (numRows > 0 && !variableRowHeight) |
| configureScrollBars(); |
| |
| return (numRows > 0); |
| } |
| |
| /** |
| * @private |
| */ |
| override public function calculateDropIndex(event:DragEvent = null):int |
| { |
| if (event) |
| { |
| var item:IListItemRenderer; |
| var lastItem:IListItemRenderer; |
| var pt:Point = new Point(event.localX, event.localY); |
| pt = DisplayObject(event.target).localToGlobal(pt); |
| pt = listContent.globalToLocal(pt); |
| |
| var n:int = listItems.length; |
| for (var i:int = 0; i < n; i++) |
| { |
| if (listItems[i][0]) |
| lastItem = listItems[i][0]; |
| |
| if (rowInfo[i].y <= pt.y && pt.y < rowInfo[i].y + rowInfo[i].height) |
| { |
| item = listItems[i][0]; |
| break; |
| } |
| } |
| if (!item && lockedRowContent) |
| { |
| pt = listContent.localToGlobal(pt); |
| pt = lockedRowContent.globalToLocal(pt); |
| n = lockedRowContent.listItems.length; |
| for (i = 0; i < n; i++) |
| { |
| if (lockedRowContent.rowInfo[i].y <= pt.y && pt.y < lockedRowContent.rowInfo[i].y + lockedRowContent.rowInfo[i].height) |
| { |
| item = lockedRowContent.listItems[i][0]; |
| break; |
| } |
| } |
| } |
| |
| |
| if (item) |
| lastDropIndex = itemRendererToIndex(item); |
| else |
| { |
| if (lastItem) |
| lastDropIndex = itemRendererToIndex(lastItem) + 1; |
| else |
| lastDropIndex = collection ? collection.length : 0; |
| } |
| } |
| |
| return lastDropIndex; |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function drawRowBackgrounds():void |
| { |
| drawRowGraphics(listContent); |
| } |
| |
| /** |
| * @private |
| */ |
| protected function drawRowGraphics(contentHolder:ListBaseContentHolder):void |
| { |
| var rowBGs:Sprite = Sprite(contentHolder.getChildByName("rowBGs")); |
| if (!rowBGs) |
| { |
| rowBGs = new FlexSprite(); |
| rowBGs.mouseEnabled = false; |
| rowBGs.name = "rowBGs"; |
| contentHolder.addChildAt(rowBGs, 0); |
| } |
| |
| |
| var colors:Array; |
| var colorsStyle:Object = getStyle("alternatingItemColors"); |
| |
| if (colorsStyle) |
| colors = (colorsStyle is Array) ? (colorsStyle as Array) : [colorsStyle]; |
| |
| if (!colors || colors.length == 0) |
| { |
| while (rowBGs.numChildren > n) |
| { |
| rowBGs.removeChildAt(rowBGs.numChildren - 1); |
| } |
| return; |
| } |
| |
| styleManager.getColorNames(colors); |
| |
| var curRow:int = 0; |
| |
| var i:int = 0; |
| var actualRow:int = verticalScrollPosition; |
| var n:int = contentHolder.listItems.length; |
| |
| while (curRow < n) |
| { |
| drawRowBackground(rowBGs, i++, contentHolder.rowInfo[curRow].y, contentHolder.rowInfo[curRow].height, |
| colors[actualRow % colors.length], actualRow); |
| curRow++; |
| actualRow++; |
| } |
| |
| while (rowBGs.numChildren > i) |
| { |
| rowBGs.removeChildAt(rowBGs.numChildren - 1); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function mouseEventToItemRenderer(event:MouseEvent):IListItemRenderer |
| { |
| var r:IListItemRenderer; |
| |
| r = super.mouseEventToItemRenderer(event); |
| |
| return r == itemEditorInstance ? null : r; |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function get dragImage():IUIComponent |
| { |
| var image:DataGridDragProxy = new DataGridDragProxy(); |
| image.owner = this; |
| image.moduleFactory = moduleFactory; |
| return image; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| |
| /** |
| * @private |
| * Move a column to a new position in the columns array, shifting all |
| * other columns left or right and updating the sortIndex and |
| * lastSortIndex variables accordingly. |
| */ |
| mx_internal function shiftColumns(oldIndex:int, newIndex:int, |
| trigger:Event = null):void |
| { |
| if (newIndex >= 0 && oldIndex != newIndex) |
| { |
| var incr:int = oldIndex < newIndex ? 1 : -1; |
| for (var i:int = oldIndex; i != newIndex; i += incr) |
| { |
| var j:int = i + incr; |
| var c:DataGridColumn = _columns[i]; |
| _columns[i] = _columns[j]; |
| _columns[j] = c; |
| _columns[i].colNum = i; |
| _columns[j].colNum = j; |
| } |
| |
| if (sortIndex == oldIndex) |
| sortIndex += newIndex - oldIndex; |
| else if ((oldIndex < sortIndex && sortIndex <= newIndex) |
| || (newIndex <= sortIndex && sortIndex < oldIndex)) |
| sortIndex -= incr; |
| |
| if (lastSortIndex == oldIndex) |
| lastSortIndex += newIndex - oldIndex; |
| else if ((oldIndex < lastSortIndex |
| && lastSortIndex <= newIndex) |
| || (newIndex <= lastSortIndex |
| && lastSortIndex < oldIndex)) |
| lastSortIndex -= incr; |
| |
| columnsInvalid = true; |
| itemsSizeChanged = true; |
| invalidateDisplayList(); |
| if (lockedColumnHeader) |
| lockedColumnHeader.invalidateDisplayList(); |
| |
| var icEvent:IndexChangedEvent = |
| new IndexChangedEvent(IndexChangedEvent.HEADER_SHIFT); |
| icEvent.oldIndex = oldIndex; |
| icEvent.newIndex = newIndex; |
| icEvent.triggerEvent = trigger; |
| dispatchEvent(icEvent); |
| } |
| } |
| |
| /** |
| * @private |
| * Searches the iterator to determine columns. |
| */ |
| private function generateCols():void |
| { |
| if (collection.length > 0) |
| { |
| var col:DataGridColumn; |
| var newCols:Array = []; |
| var cols:Array; |
| if (dataProvider) |
| { |
| try |
| { |
| iterator.seek(CursorBookmark.FIRST); |
| if (!iteratorValid) |
| { |
| iteratorValid = true; |
| lastSeekPending = null; |
| } |
| } |
| catch(e:ItemPendingError) |
| { |
| lastSeekPending = new ListBaseSeekPending(CursorBookmark.FIRST, 0); |
| e.addResponder(new ItemResponder(generateColumnsPendingResultHandler, seekPendingFailureHandler, |
| lastSeekPending)); |
| iteratorValid = false; |
| return; |
| } |
| var info:Object = |
| ObjectUtil.getClassInfo(iterator.current, |
| ["uid", "mx_internal_uid"]); |
| |
| if (info) |
| cols = info.properties; |
| } |
| |
| if (!cols) |
| { |
| // introspect the first item and use its fields |
| var itmObj:Object = iterator.current; |
| for (var p:String in itmObj) |
| { |
| if (p != "uid") |
| { |
| col = new DataGridColumn(); |
| col.dataField = p; |
| newCols.push(col); |
| } |
| } |
| } |
| else |
| { |
| // this is an old recordset - use its columns |
| var n:int = cols.length; |
| var colName:Object; |
| for (var i:int = 0; i < n; i++) |
| { |
| colName = cols[i]; |
| if (colName is QName) |
| colName = QName(colName).localName; |
| col = new DataGridColumn(); |
| col.dataField = String(colName); |
| newCols.push(col); |
| } |
| } |
| |
| columns = newCols; |
| generatedColumns = true; |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| private function generateColumnsPendingResultHandler(data:Object, info:ListBaseSeekPending):void |
| { |
| // generate cols if we haven't successfully generated them |
| if (columns.length == 0) |
| generateCols(); |
| seekPendingResultHandler(data, info); |
| } |
| |
| /** |
| * @private |
| */ |
| private function calculateColumnSizes():void |
| { |
| var delta:Number; |
| var n:int; |
| var i:int; |
| var totalWidth:Number = 0; |
| var col:DataGridColumn; |
| var cw:Number; |
| |
| if (columns.length == 0) |
| { |
| visibleColumns = []; |
| visibleLockedColumns = []; |
| lockedColumnWidth = 0; |
| columnsInvalid = false; |
| return; |
| } |
| |
| // no columns are visible so figure out which ones |
| // to make visible |
| if (columnsInvalid) |
| { |
| columnsInvalid = false; |
| visibleColumns = []; |
| visibleLockedColumns = []; |
| lockedColumnWidth = 0; |
| |
| if (minColumnWidthInvalid) |
| { |
| n = _columns.length; |
| for (i = 0; i < n; i++) |
| { |
| _columns[i].minWidth = minColumnWidth; |
| } |
| minColumnWidthInvalid = false; |
| } |
| |
| displayableColumns = null; |
| n = _columns.length; |
| for (i = 0; i < n; i++) |
| { |
| if (displayableColumns && _columns[i].visible) |
| { |
| displayableColumns.push(_columns[i]); |
| } |
| else if (!displayableColumns && !_columns[i].visible) |
| { |
| displayableColumns = new Array(i); |
| for (var k:int = 0; k < i; k++) |
| displayableColumns[k] = _columns[k]; |
| } |
| } |
| |
| // If there are no hidden columns, displayableColumns points to |
| // _columns (we don't need a duplicate copy of _columns). |
| if (!displayableColumns) |
| displayableColumns = _columns; |
| |
| // if no hscroll, then pack columns in available space |
| if (horizontalScrollPolicy == ScrollPolicy.OFF) |
| { |
| n = displayableColumns.length; |
| for (i = 0; i < n; i++) |
| { |
| col = displayableColumns[i]; |
| |
| if (i < lockedColumnCount) |
| { |
| visibleLockedColumns.push(col); |
| } |
| else |
| visibleColumns.push(col); |
| } |
| } |
| else |
| { |
| n = displayableColumns.length; |
| for (i = 0; i < n; i++) |
| { |
| if (i >= lockedColumnCount && |
| i < lockedColumnCount + horizontalScrollPosition) |
| { |
| continue; |
| } |
| |
| col = displayableColumns[i]; |
| if (col.preferredWidth < col.minWidth) |
| col.preferredWidth = col.minWidth; |
| |
| if (totalWidth < displayWidth) |
| { |
| if (i < lockedColumnCount) |
| { |
| lockedColumnWidth += Math.max(isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth, col.minWidth); |
| visibleLockedColumns.push(col); |
| } |
| else |
| visibleColumns.push(col); |
| totalWidth += Math.max(isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth, col.minWidth); |
| if (col.width != col.preferredWidth) |
| col.setWidth(col.preferredWidth); |
| } |
| else |
| { |
| if (visibleColumns.length == 0) |
| visibleColumns.push(displayableColumns[0]); |
| break; |
| } |
| } |
| } |
| } |
| |
| var lastColumn:DataGridColumn; |
| var newSize:Number; |
| |
| // if no hscroll, then pack columns in available space |
| if (horizontalScrollPolicy == ScrollPolicy.OFF) |
| { |
| var numResizable:int = 0; |
| var fixedWidth:Number = 0; |
| |
| // trace("resizing columns"); |
| |
| // count how many resizable columns and how wide they are |
| n = visibleColumns.length; |
| for (i = 0; i < n; i++) |
| { |
| // trace("column " + i + " width = " + visibleColumns[i].width); |
| if (visibleColumns[i].resizable && !visibleColumns[i].newlyVisible) |
| { |
| // trace(" resizable"); |
| if (!isNaN(visibleColumns[i].explicitWidth)) |
| { |
| // trace(" explicit width " + visibleColumns[i].width); |
| fixedWidth += visibleColumns[i].width; |
| } |
| else |
| { |
| // trace(" implicitly resizable"); |
| numResizable++; |
| fixedWidth += visibleColumns[i].minWidth; |
| // trace(" minWidth " + visibleColumns[i].minWidth); |
| } |
| } |
| else |
| { |
| // trace(" not resizable"); |
| fixedWidth += visibleColumns[i].width; |
| } |
| |
| totalWidth += visibleColumns[i].width; |
| } |
| n = visibleLockedColumns.length; |
| for (i = 0; i < n; i++) |
| { |
| // trace("column " + i + " width = " + visibleLockedColumns[i].width); |
| if (visibleLockedColumns[i].resizable && !visibleLockedColumns[i].newlyVisible) |
| { |
| // trace(" resizable"); |
| if (!isNaN(visibleLockedColumns[i].explicitWidth)) |
| { |
| // trace(" explicit width " + visibleLockedColumns[i].width); |
| fixedWidth += visibleLockedColumns[i].width; |
| } |
| else |
| { |
| // trace(" implicitly resizable"); |
| numResizable++; |
| fixedWidth += visibleLockedColumns[i].minWidth; |
| // trace(" minWidth " + visibleLockedColumns[i].minWidth); |
| } |
| } |
| else |
| { |
| // trace(" not resizable"); |
| fixedWidth += visibleLockedColumns[i].width; |
| } |
| |
| totalWidth += visibleLockedColumns[i].width; |
| } |
| // trace("totalWidth = " + totalWidth); |
| // trace("displayWidth = " + displayWidth); |
| |
| var ratio:Number; |
| var newTotal:Number = displayWidth; |
| var minWidth:Number; |
| if (displayWidth > fixedWidth && numResizable) |
| { |
| // we have flexible columns and room to honor minwidths and non-resizable |
| // trace("have enough room"); |
| |
| // divide and distribute the excess among the resizable |
| n = visibleLockedColumns.length; |
| for (i = 0; i < n; i++) |
| { |
| if (visibleLockedColumns[i].resizable && !visibleLockedColumns[i].newlyVisible && isNaN(visibleLockedColumns[i].explicitWidth)) |
| { |
| lastColumn = visibleLockedColumns[i]; |
| if (totalWidth > displayWidth) |
| ratio = (lastColumn.width - lastColumn.minWidth)/ (totalWidth - fixedWidth); |
| else |
| ratio = lastColumn.width / totalWidth; |
| newSize = Math.floor(lastColumn.width - (totalWidth - displayWidth) * ratio); |
| minWidth = visibleLockedColumns[i].minWidth; |
| visibleLockedColumns[i].setWidth(newSize > minWidth ? newSize : minWidth); |
| // trace("column " + i + " set to " + visibleLockedColumns[i].width); |
| } |
| newTotal -= visibleLockedColumns[i].width; |
| visibleLockedColumns[i].newlyVisible = false; |
| } |
| n = visibleColumns.length; |
| for (i = 0; i < n; i++) |
| { |
| if (visibleColumns[i].resizable && !visibleColumns[i].newlyVisible && isNaN(visibleColumns[i].explicitWidth)) |
| { |
| lastColumn = visibleColumns[i]; |
| if (totalWidth > displayWidth) |
| ratio = (lastColumn.width - lastColumn.minWidth)/ (totalWidth - fixedWidth); |
| else |
| ratio = lastColumn.width / totalWidth; |
| newSize = Math.floor(lastColumn.width - (totalWidth - displayWidth) * ratio); |
| minWidth = visibleColumns[i].minWidth; |
| visibleColumns[i].setWidth(newSize > minWidth ? newSize : minWidth); |
| // trace("column " + i + " set to " + visibleColumns[i].width); |
| } |
| newTotal -= visibleColumns[i].width; |
| visibleColumns[i].newlyVisible = false; |
| } |
| if (newTotal && lastColumn) |
| { |
| // trace("excess = " + newTotal); |
| lastColumn.setWidth(lastColumn.width + newTotal); |
| } |
| } |
| else // can't honor minwidth and non-resizables so just scale everybody |
| { |
| // trace("too small or too big"); |
| n = visibleLockedColumns.length; |
| for (i = 0; i < n; i++) |
| { |
| lastColumn = visibleLockedColumns[i]; |
| ratio = lastColumn.width / totalWidth; |
| //totalWidth -= visibleLockedColumns[i].width; |
| newSize = Math.floor(displayWidth * ratio); |
| lastColumn.setWidth(newSize); |
| lastColumn.explicitWidth = NaN; |
| // trace("column " + i + " set to " + visibleLockedColumns[i].width); |
| newTotal -= newSize; |
| } |
| n = visibleColumns.length; |
| for (i = 0; i < n; i++) |
| { |
| lastColumn = visibleColumns[i]; |
| ratio = lastColumn.width / totalWidth; |
| //totalWidth -= visibleColumns[i].width; |
| newSize = Math.floor(displayWidth * ratio); |
| lastColumn.setWidth(newSize); |
| lastColumn.explicitWidth = NaN; |
| // trace("column " + i + " set to " + visibleColumns[i].width); |
| newTotal -= newSize; |
| } |
| if (newTotal && lastColumn) |
| { |
| // trace("excess = " + newTotal); |
| lastColumn.setWidth(lastColumn.width + newTotal); |
| } |
| } |
| } |
| else // we have or can have an horizontalScrollBar |
| { |
| totalWidth = 0; |
| // drop any that completely overflow |
| n = visibleColumns.length; |
| for (i = 0; i < n; i++) |
| { |
| if (totalWidth > displayWidth - lockedColumnWidth) |
| { |
| visibleColumns.splice(i); |
| break; |
| } |
| totalWidth += isNaN(visibleColumns[i].explicitWidth) ? visibleColumns[i].preferredWidth : visibleColumns[i].explicitWidth; |
| } |
| |
| if (visibleColumns.length == 0) |
| return; |
| |
| i = visibleColumns[visibleColumns.length - 1].colNum + 1; |
| // add more if we have room |
| if (totalWidth < displayWidth - lockedColumnWidth && i < displayableColumns.length) |
| { |
| n = displayableColumns.length; |
| for (; i < n && totalWidth < displayWidth - lockedColumnWidth; i++) |
| { |
| col = displayableColumns[i]; |
| |
| visibleColumns.push(col); |
| totalWidth += isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth; |
| } |
| } |
| else if (totalWidth < displayWidth - lockedColumnWidth && horizontalScrollPosition > 0) |
| { |
| while (totalWidth < displayWidth - lockedColumnWidth && horizontalScrollPosition > 0) |
| { |
| col = displayableColumns[lockedColumnCount + horizontalScrollPosition - 1]; |
| cw = isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth; |
| if (cw < displayWidth - lockedColumnWidth - totalWidth) |
| { |
| visibleColumns.splice(0, 0, col); |
| super.horizontalScrollPosition--; |
| totalWidth += cw; |
| } |
| else |
| break; |
| } |
| } |
| |
| lastColumn = visibleColumns[visibleColumns.length - 1]; |
| cw = isNaN(lastColumn.explicitWidth) ? lastColumn.preferredWidth : lastColumn.explicitWidth; |
| newSize = cw + displayWidth - lockedColumnWidth - totalWidth; |
| if (lastColumn == displayableColumns[displayableColumns.length - 1] |
| && lastColumn.resizable |
| && newSize >= lastColumn.minWidth |
| && newSize > cw) |
| { |
| lastColumn.setWidth(newSize); |
| maxHorizontalScrollPosition = |
| displayableColumns.length - visibleColumns.length; |
| } |
| else if (visibleColumns.length == 1 && |
| lastColumn == displayableColumns[displayableColumns.length - 1]) |
| { |
| maxHorizontalScrollPosition = |
| displayableColumns.length - visibleColumns.length; |
| } |
| else |
| { |
| maxHorizontalScrollPosition = |
| displayableColumns.length - visibleColumns.length + 1; |
| } |
| } |
| lockedColumnWidth = 0; |
| if (visibleLockedColumns.length) |
| { |
| n = visibleLockedColumns.length; |
| for (i = 0; i < n; i++) |
| { |
| col = visibleLockedColumns[i]; |
| lockedColumnWidth += col.width; |
| } |
| } |
| } |
| |
| /** |
| * @private |
| * If there is no horizontal scroll bar, changes the display width of other columns when |
| * one column's width is changed. |
| * @param col column whose width is changed |
| * @param w width of column |
| */ |
| override mx_internal function resizeColumn(col:int, w:Number):void |
| { |
| // there's a window of time before we calccolumnsizes |
| // that someone can set width in AS |
| if ((!visibleColumns || visibleColumns.length == 0) && (!visibleLockedColumns || visibleLockedColumns.length == 0)) |
| { |
| _columns[col].setWidth(w); |
| _columns[col].preferredWidth = w; |
| return; |
| } |
| |
| if (w < _columns[col].minWidth) |
| w = _columns[col].minWidth; |
| |
| // hScrollBar is present |
| if (_horizontalScrollPolicy == ScrollPolicy.ON || |
| _horizontalScrollPolicy == ScrollPolicy.AUTO) |
| { |
| // adjust the column's width |
| _columns[col].setWidth(w); |
| _columns[col].explicitWidth = w; |
| _columns[col].preferredWidth = w; |
| columnsInvalid = true; |
| } |
| else |
| { |
| // find the columns in the set of visible columns; |
| var n:int = _columns.length; |
| var i:int; |
| for (i = 0; i < n; i++) |
| { |
| if (col == _columns[i].colNum) |
| break; |
| } |
| if (i >= _columns.length - 1) // no resize of right most column |
| return; |
| col = i; |
| |
| // we want all cols's new widths to the right of this to be in proportion |
| // to what they were before the stretch. |
| |
| // get the original space to the right not taken up by the column |
| var totalSpace:Number = 0; |
| var lastColumn:DataGridColumn; |
| var newWidth:Number; |
| //non-resizable columns don't count though |
| for (i = col + 1; i < n; i++) |
| { |
| if (_columns[i].visible) |
| if (_columns[i].resizable) |
| totalSpace += _columns[i].width; |
| } |
| |
| var newTotalSpace:Number = _columns[col].width -
|