| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // Licensed to the Apache Software Foundation (ASF) under one or more |
| // contributor license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright ownership. |
| // The ASF licenses this file to You under the Apache License, Version 2.0 |
| // (the "License"); you may not use this file except in compliance with |
| // the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| package mx.controls |
| { |
| |
| import flash.display.DisplayObject; |
| import flash.display.DisplayObjectContainer; |
| import flash.display.GradientType; |
| import flash.display.Graphics; |
| import flash.display.Shape; |
| import flash.display.Sprite; |
| import flash.events.Event; |
| import flash.events.FocusEvent; |
| import flash.events.KeyboardEvent; |
| import flash.events.MouseEvent; |
| import flash.geom.Matrix; |
| import flash.geom.Point; |
| import flash.geom.Rectangle; |
| import flash.ui.Keyboard; |
| import flash.utils.Dictionary; |
| import flash.utils.describeType; |
| import flash.utils.getTimer; |
| |
| import mx.collections.CursorBookmark; |
| import mx.collections.ICollectionView; |
| import mx.collections.ISort; |
| import mx.collections.ISortField; |
| import mx.collections.IViewCursor; |
| import mx.collections.ItemResponder; |
| import mx.collections.Sort; |
| import mx.collections.SortField; |
| import mx.collections.errors.ItemPendingError; |
| import mx.controls.advancedDataGridClasses.AdvancedDataGridBase; |
| import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn; |
| import mx.controls.advancedDataGridClasses.AdvancedDataGridDragProxy; |
| import mx.controls.advancedDataGridClasses.AdvancedDataGridHeaderInfo; |
| import mx.controls.advancedDataGridClasses.AdvancedDataGridHeaderRenderer; |
| import mx.controls.advancedDataGridClasses.AdvancedDataGridItemRenderer; |
| import mx.controls.advancedDataGridClasses.AdvancedDataGridListData; |
| import mx.controls.advancedDataGridClasses.AdvancedDataGridSortItemRenderer; |
| import mx.controls.advancedDataGridClasses.SortInfo; |
| import mx.controls.listClasses.IDropInListItemRenderer; |
| import mx.controls.listClasses.IListItemRenderer; |
| import mx.controls.listClasses.ListBaseSeekPending; |
| import mx.controls.listClasses.ListBaseSelectionData; |
| import mx.controls.listClasses.ListRowInfo; |
| import mx.controls.scrollClasses.ScrollBar; |
| import mx.core.ClassFactory; |
| import mx.core.ContextualClassFactory; |
| import mx.core.EdgeMetrics; |
| import mx.core.EventPriority; |
| import mx.core.FlexShape; |
| import mx.core.FlexSprite; |
| import mx.core.IBorder; |
| import mx.core.IFactory; |
| import mx.core.IFlexDisplayObject; |
| import mx.core.IFlexModuleFactory; |
| import mx.core.IIMESupport; |
| import mx.core.IInvalidating; |
| import mx.core.IPropertyChangeNotifier; |
| import mx.core.IUIComponent; |
| import mx.core.LayoutDirection; |
| import mx.core.ScrollPolicy; |
| import mx.core.UIComponent; |
| import mx.core.UIComponentGlobals; |
| import mx.core.mx_internal; |
| import mx.events.AdvancedDataGridEvent; |
| import mx.events.AdvancedDataGridEventReason; |
| import mx.events.CollectionEvent; |
| import mx.events.CollectionEventKind; |
| import mx.events.DragEvent; |
| import mx.events.IndexChangedEvent; |
| import mx.events.ListEvent; |
| import mx.events.SandboxMouseEvent; |
| import mx.events.ScrollEvent; |
| import mx.events.ScrollEventDetail; |
| import mx.events.ScrollEventDirection; |
| import mx.managers.CursorManager; |
| import mx.managers.CursorManagerPriority; |
| import mx.managers.IFocusManager; |
| import mx.managers.IFocusManagerComponent; |
| import mx.skins.halo.DataGridColumnDropIndicator; |
| import mx.styles.ISimpleStyleClient; |
| import mx.utils.ObjectUtil; |
| import mx.utils.StringUtil; |
| |
| use namespace mx_internal; |
| |
| //-------------------------------------- |
| // Events |
| //-------------------------------------- |
| |
| /** |
| * Dispatched when the user releases the mouse button while over an item |
| * renderer, tabs to the AdvancedDataGrid control or within the AdvancedDataGrid control, |
| * or in any other way attempts to edit an item. |
| * |
| * @eventType mx.events.AdvancedDataGridEvent.ITEM_EDIT_BEGINNING |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="itemEditBeginning", type="mx.events.AdvancedDataGridEvent")] |
| |
| /** |
| * Dispatched when the <code>editedItemPosition</code> property has been set |
| * and the item can be edited. |
| * |
| * @eventType mx.events.AdvancedDataGridEvent.ITEM_EDIT_BEGIN |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="itemEditBegin", type="mx.events.AdvancedDataGridEvent")] |
| |
| /** |
| * Dispatched when an item editing session ends for any reason. |
| * |
| * @eventType mx.events.AdvancedDataGridEvent.ITEM_EDIT_END |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="itemEditEnd", type="mx.events.AdvancedDataGridEvent")] |
| |
| /** |
| * Dispatched when an item renderer gets focus, which can occur if the user |
| * clicks on an item in the AdvancedDataGrid control or navigates to the item using |
| * a keyboard. Only dispatched if the item is editable. |
| * |
| * @eventType mx.events.AdvancedDataGridEvent.ITEM_FOCUS_IN |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="itemFocusIn", type="mx.events.AdvancedDataGridEvent")] |
| |
| /** |
| * Dispatched when an item renderer loses focus, which can occur if the user |
| * clicks another item in the AdvancedDataGrid control or clicks outside the control, |
| * or uses the keyboard to navigate to another item in the AdvancedDataGrid control |
| * or outside the control. |
| * Only dispatched if the item is editable. |
| * |
| * @eventType mx.events.AdvancedDataGridEvent.ITEM_FOCUS_OUT |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="itemFocusOut", type="mx.events.AdvancedDataGridEvent")] |
| |
| /** |
| * Dispatched when a user changes the width of a column, indicating that the |
| * amount of data displayed in that column may have changed. |
| * If <code>horizontalScrollPolicy</code> is <code>"none"</code>, other |
| * columns shrink or expand to compensate for the columns' resizing, |
| * and they also dispatch this event. |
| * |
| * @eventType mx.events.AdvancedDataGridEvent.COLUMN_STRETCH |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="columnStretch", type="mx.events.AdvancedDataGridEvent")] |
| |
| /** |
| * Dispatched when the user releases the mouse button on a column header |
| * to request the control to sort |
| * the grid contents based on the contents of the column. |
| * Only dispatched if the column is sortable and the data provider supports |
| * sorting. The AdvancedDataGrid control has a default handler for this event that implements |
| * a single-column sort. Multiple-column sort can be implemented by calling the |
| * <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 <code>headerRelease</code> event. If you call the <code>preventDefault()</code> method |
| * in your event handler, the arrows are not drawn. |
| * </p> |
| * |
| * @eventType mx.events.AdvancedDataGridEvent.HEADER_RELEASE |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="headerRelease", type="mx.events.AdvancedDataGridEvent")] |
| |
| /** |
| * Dispatched when sorting is to be performed on the AdvancedDataGrid control. |
| * |
| * @eventType mx.events.AdvancedDataGridEvent.SORT |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="sort", type="mx.events.AdvancedDataGridEvent")] |
| |
| /** |
| * Dispatched when the user releases the mouse button on a column header after |
| * having dragged the column to a new location resulting in shifting the column |
| * to a new index |
| * |
| * @eventType mx.events.IndexChangedEvent.HEADER_SHIFT |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Event(name="headerShift", type="mx.events.IndexChangedEvent")] |
| |
| //-------------------------------------- |
| // Styles |
| //-------------------------------------- |
| |
| include "../styles/metadata/IconColorStyles.as" |
| |
| /** |
| * Name of the class of the itemEditor to be used if one is not |
| * specified for a column. This is a way to set |
| * an item editor for a group of AdvancedDataGrids instead of having to |
| * set each one individually. If you set the AdvancedDataGridColumn's itemEditor |
| * property, it supercedes this value. |
| * @default null |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| [Style(name="defaultDataGridItemEditor", type="Class", inherit="no")] |
| |
| /** |
| * Name of the class of the itemRenderer to be used if one is not |
| * specified for a column. This is a way to set |
| * an itemRenderer for a group of AdvancedDataGrids instead of having to |
| * set each one individually. If you set the AdvancedDataGrid's itemRenderer |
| * property, it supercedes this value. |
| * @default null |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| [Style(name="defaultDataGridItemRenderer", type="Class", inherit="no")] |
| |
| /** |
| * A flag that indicates whether to show vertical grid lines between |
| * the columns. |
| * If <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. |
| * @default 0x666666 |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="verticalGridLineColor", type="uint", format="Color", inherit="yes")] |
| |
| /** |
| * The color of the horizontal grid lines. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="horizontalGridLineColor", type="uint", format="Color", inherit="yes")] |
| |
| /** |
| * An array of two colors used to draw the header background gradient. |
| * The first color is the top color. |
| * The second color is the bottom color. |
| * @default [0xFFFFFF, 0xE6E6E6] |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="headerColors", type="Array", arrayType="uint", format="Color", inherit="yes")] |
| |
| /** |
| * The color of the row background when the user rolls over the row. |
| * @default 0xE3FFD6 |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="rollOverColor", type="uint", format="Color", inherit="yes")] |
| |
| /** |
| * The color of the background for the row when the user selects |
| * an item renderer in the row. |
| * @default 0xCDFFC1 |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="selectionColor", type="uint", format="Color", inherit="yes")] |
| |
| /** |
| * The name of a CSS style declaration for controlling other aspects of |
| * the appearance of the column headers. |
| * @default "dataGridStyles" |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="headerStyleName", type="String", inherit="no")] |
| |
| /** |
| * The class to use as the skin for a column that is being resized. |
| * |
| * @default mx.skins.halo.DataGridColumnResizeSkin (for both Halo and Spark themes) |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="columnResizeSkin", type="Class", inherit="no")] |
| |
| |
| /** |
| * The class to use as the skin that defines the appearance of the |
| * background of the column headers in a AdvancedDataGrid control. |
| * |
| * <p>The default skin class is based on the theme. For example, with the Halo theme, |
| * the default skin class is <code>mx.skins.halo.DataGridHeaderBackgroundSkin</code>. For the Spark theme, the default skin |
| * class is <code>mx.skins.spark.DataGridHeaderBackgroundSkin</code>.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="headerBackgroundSkin", type="Class", inherit="no")] |
| |
| /** |
| * The class to use as the skin that defines the appearance of the |
| * separator between column headers in a AdvancedDataGrid control. |
| * |
| * <p>The default skin class is based on the theme. For example, with the Halo theme, |
| * the default skin class is <code>mx.skins.halo.DataGridHeaderSeparator</code>. For the Spark theme, the default skin |
| * class is <code>mx.skins.spark.DataGridHeaderSeparatorSkin</code>.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="headerSeparatorSkin", type="Class", inherit="no")] |
| |
| /** |
| * The class to use as the skin that defines the appearance of the |
| * separator between a column group and its children columns/column group headers |
| * in an AdvancedDataGrid control. |
| * @default mx.skins.halo.AdvancedDataGridHeaderHorizontalSeparator |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="headerHorizontalSeparatorSkin", type="Class", inherit="no")] |
| |
| /** |
| * The class to use as the skin that defines the appearance of the |
| * separator between rows in a AdvancedDataGrid control. |
| * By default, the AdvancedDataGrid control uses the |
| * <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 AdvancedDataGrid control. |
| * By default, the AdvancedDataGrid 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 AdvancedDataGrid control. |
| * By default, the AdvancedDataGrid 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 AdvancedDataGrid control. |
| * By default, the AdvancedDataGrid 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 cursor that indicates that a column |
| * can be resized. |
| * @default mx.skins.halo.DataGridStretchCursor |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="stretchCursor", type="Class", inherit="no")] |
| |
| /** |
| * The class to use as the skin that indicates that |
| * a column can be dropped in the current location. |
| * |
| * @default mx.skins.halo.DataGridColumnDropIndicator (for both Halo and Spark themes) |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="columnDropIndicatorSkin", type="Class", inherit="no")] |
| |
| /** |
| * The name of a CSS style declaration for controlling aspects of the |
| * appearance of column when the user is dragging it to another location. |
| * |
| * @default "headerDragProxyStyle" |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Style(name="headerDragProxyStyleName", type="String", inherit="no")] |
| |
| //-------------------------------------- |
| // Excluded APIs |
| //-------------------------------------- |
| |
| [Exclude(name="columnCount", kind="property")] |
| [Exclude(name="labelField", kind="property")] |
| [Exclude(name="offscreenExtraRowsOrColumns", kind="property")] |
| [Exclude(name="offscreenExtraRows", kind="property")] |
| [Exclude(name="offscreenExtraRowsTop", kind="property")] |
| [Exclude(name="offscreenExtraRowsBottom", kind="property")] |
| [Exclude(name="offscreenExtraColumns", kind="property")] |
| [Exclude(name="offscreenExtraColumnsLeft", kind="property")] |
| [Exclude(name="offscreenExtraColumnsRight", kind="property")] |
| [Exclude(name="offscreenExtraRowsOrColumnsChanged", kind="property")] |
| [Exclude(name="maxHorizontalScrollPosition", kind="property")] |
| [Exclude(name="maxVerticalScrollPosition", kind="property")] |
| [Exclude(name="showDataTips", kind="property")] |
| [Exclude(name="cornerRadius", kind="style")] |
| |
| //-------------------------------------- |
| // Other metadata |
| //-------------------------------------- |
| |
| [DataBindingInfo("acceptedTypes", "{ dataProvider: "String" }")] |
| |
| [DefaultBindingProperty(source="selectedItem", destination="dataProvider")] |
| |
| [DefaultProperty("dataProvider")] |
| |
| [DefaultTriggerEvent("change")] |
| |
| [IconFile("AdvancedDataGrid.png")] |
| |
| [RequiresDataBinding(true)] |
| |
| /** |
| * The AdvancedDataGridBaseEx class is a base class of the AdvancedDataGrid control. |
| * This class contains code that provides functionality similar to the DataGrid control. |
| * |
| * @mxml |
| * <p> |
| * The <code><mx:AdvancedDataGridBaseEx></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:AdvancedDataGridBaseEx |
| * <b>Properties</b> |
| * columns="<i>From dataProvider</i>" |
| * draggableColumns="true|false" |
| * editable="item group summary" |
| * editedItemPosition="<code>null</code>" |
| * horizontalScrollPosition="null" |
| * imeMode="null" |
| * itemEditorInstance="null" |
| * lookAheadDuratio="400" |
| * minColumnWidth="<code>NaN</code>" |
| * resizableColumns="true|false" |
| * sortableColumns="true|false" |
| * sortExpertMode="false|true" |
| * |
| * <b>Styles</b> |
| * columnDropIndicatorSkin="DataGridColumnDropIndicator" |
| * columnResizeSkin="DataGridColumnResizeSkin" |
| * disabledIconColor="0x999999" |
| * headerBackgroundSkin="DataGridHeaderSeparator" |
| * headerColors="[#FFFFFF, #E6E6E6]" |
| * headerDragProxyStyleName="headerDragProxyStyle" |
| * headerHorizontalSeparatorSkin="AdvancedDataGridHeaderHorizontalSeparator" |
| * headerSeparatorSkin="DataGridHeaderSeparator" |
| * headerStyleName="<i>No default</i>" |
| * horizontalGridLineColor="<i>No default</i>" |
| * horizontalGridLines="false|true" |
| * horizontalLockedSeparatorSkin="undefined" |
| * horizontalSeparatorSkin="undefined" |
| * iconColor="0x111111" |
| * rollOverColor="#E3FFD6" |
| * selectionColor="#CDFFC1" |
| * stretchCursor="DataGridStretchCursor" |
| * verticalGridLineColor="#666666" |
| * 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>" |
| * /> |
| * |
| * <i>The following AdvancedDataGrid code sample specifies the column order:</i> |
| * <mx:AdvancedDataGrid> |
| * <mx:dataProvider> |
| * <mx:Object Artist="Pavement" Price="11.99" |
| * Album="Slanted and Enchanted"/> |
| * <mx:Object Artist="Pavement" |
| * Album="Brighten the Corners" Price="11.99"/> |
| * </mx:dataProvider> |
| * <mx:columns> |
| * <mx:AdvancedDataGridColumn dataField="Album"/> |
| * <mx:AdvancedDataGridColumn dataField="Price"/> |
| * </mx:columns> |
| * </mx:AdvancedDataGrid> |
| * </pre> |
| * </p> |
| * |
| * @see mx.controls.AdvancedDataGrid |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public class AdvancedDataGridBaseEx extends AdvancedDataGridBase implements IIMESupport |
| { |
| include "../core/Version.as"; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Class constants |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * TODO!!! Replace with global versioning infrastructure |
| */ |
| public static var useOldDGHeaderBGLogic:Boolean = false; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Constructor. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function AdvancedDataGridBaseEx() |
| { |
| super(); |
| |
| _columns = []; |
| |
| headerRenderer = new ClassFactory(AdvancedDataGridHeaderRenderer); |
| sortItemRenderer = new ClassFactory(AdvancedDataGridSortItemRenderer); |
| |
| // pick a default row height |
| setRowHeight(20); |
| |
| // Register default handlers for item editing and sorting events. |
| |
| addEventListener(AdvancedDataGridEvent.ITEM_EDIT_BEGINNING, |
| itemEditorItemEditBeginningHandler, |
| false, EventPriority.DEFAULT_HANDLER); |
| |
| addEventListener(AdvancedDataGridEvent.ITEM_EDIT_BEGIN, |
| itemEditorItemEditBeginHandler, |
| false, EventPriority.DEFAULT_HANDLER); |
| |
| addEventListener(AdvancedDataGridEvent.ITEM_EDIT_END, |
| itemEditorItemEditEndHandler, |
| false, EventPriority.DEFAULT_HANDLER); |
| |
| addEventListener(AdvancedDataGridEvent.HEADER_RELEASE, |
| headerReleaseHandler, |
| false, EventPriority.DEFAULT_HANDLER); |
| |
| addEventListener(AdvancedDataGridEvent.SORT, |
| sortHandler, |
| false, EventPriority.DEFAULT_HANDLER); |
| |
| addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * true if we want to block editing on mouseUp |
| */ |
| private var dontEdit:Boolean = false; |
| |
| /** |
| * @private |
| * true if we want to block editing on mouseUp |
| */ |
| private var losingFocus:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var _focusPane:Sprite; |
| |
| /** |
| * @private |
| * true if we're in the endEdit call. Used to handle |
| * some timing issues with collection updates |
| */ |
| private var inEndEdit:Boolean = false; |
| |
| /** |
| * @private |
| * true if we've disabled updates in the collection |
| */ |
| private var collectionUpdatesDisabled:Boolean = false; |
| |
| /** |
| * Specifies a graphic that shows the proposed column width as the user stretches it. |
| * |
| * @private |
| */ |
| private var resizeGraphic:IFlexDisplayObject; |
| |
| /** |
| * @private |
| * A tmp var to store the stretching col's X coord. |
| */ |
| private var startX:Number; |
| |
| /** |
| * @private |
| * A tmp var to store the stretching col's min X coord for column's minWidth. |
| */ |
| private var minX:Number; |
| |
| /** |
| * @private |
| * A tmp var to store the last point (in dataGrid coords) received while dragging. |
| */ |
| private var lastPt:Point; |
| |
| /** |
| * @private |
| * List of header separators for column resizing. |
| */ |
| private var separators:Array; |
| |
| /** |
| * @private |
| * List of header separators for column resizing in the locked column area. |
| */ |
| protected var lockedSeparators:Array; |
| |
| /** |
| * @private |
| * The column that is being resized. |
| */ |
| private var resizingColumn:AdvancedDataGridColumn; |
| |
| /** |
| * @private |
| * The index of the column being sorted. |
| */ |
| private var sortIndex:int = -1; |
| |
| /** |
| * @private |
| * The column being sorted. |
| */ |
| private var sortColumn:AdvancedDataGridColumn; |
| |
| /** |
| * @private |
| * The direction of the sort |
| */ |
| private var sortDirection:String; |
| |
| /** |
| * @private |
| * The index of the last column being sorted on. |
| */ |
| private var lastSortIndex:int = -1; |
| |
| /** |
| * @private |
| */ |
| private var lastItemDown:IListItemRenderer; |
| |
| /** |
| * @private |
| * The column that is being moved. |
| */ |
| protected var movingColumn:AdvancedDataGridColumn; |
| |
| /** |
| * @private |
| * Index of column before which to drop |
| */ |
| protected var dropColumnIndex:int = -1; |
| |
| /** |
| * @private |
| */ |
| mx_internal var columnDropIndicator:IFlexDisplayObject; |
| |
| /** |
| * @private |
| */ |
| private var displayWidth:Number; |
| |
| /** |
| * @private |
| * Additional affordance given to header separators. |
| */ |
| private var separatorAffordance:Number = 3; |
| |
| |
| /** |
| * @private |
| * Columns with visible="true" |
| */ |
| protected var displayableColumns:Array; |
| /** |
| * @private |
| * Whether we have auto-generated the set of columns |
| * Defaults to true so we'll run the auto-generation at init time if needed |
| */ |
| protected var generatedColumns:Boolean = true; |
| |
| /** |
| * @private |
| * A hash table of objects used to calculate sizes |
| */ |
| protected var measuringObjects:Dictionary; |
| |
| /** |
| * @private |
| */ |
| private var resizeCursorID:int = CursorManager.NO_CURSOR; |
| |
| // last known position of item editor instance |
| private var actualRowIndex:int; |
| private var actualColIndex:int; |
| |
| /** |
| * @private |
| * Flag to indicate whether sorting is manual or programmatic. If it's |
| * not manual, we try to draw the sort arrow on the right column header. |
| */ |
| private var manualSort:Boolean; |
| |
| /** |
| * An ordered list of AdvancedDataGridHeaderInfo instances that |
| * correspond to the visible column headers. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected var orderedHeadersList:Array = []; |
| |
| /** |
| * Contains <code>true</code> if the <code>headerInfos</code> property |
| * has been initialized with AdvancedDataGridHeaderInfo instances. |
| * |
| * @see mx.controls.advancedDataGridClasses.AdvancedDataGridHeaderInfo |
| * |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected var headerInfoInitialized:Boolean = false; |
| |
| /** |
| * Contains <code>true</code> if a key press is in progress. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected var isKeyPressed:Boolean = false; |
| |
| /** |
| * @private |
| * Stores the last typed character(s) |
| * for multiple characters type ahead lookup. |
| */ |
| private var lookAheadCache:String = ""; |
| |
| /** |
| * @private |
| * Stores the time of the last typed character |
| * for multiple characters type ahead lookup. |
| */ |
| private var previousTime:uint; |
| |
| private var headerBGSkinChanged:Boolean = false; |
| |
| private var headerSepSkinChanged:Boolean = false; |
| |
| private var columnsChanged:Boolean = false; |
| |
| /** |
| * @private |
| * Set to true when the view is scrolled and |
| * optimumColumns != visibleColumns |
| */ |
| private var subContentScrolled:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var minColumnWidthInvalid:Boolean = false; |
| |
| /** |
| * @private |
| */ |
| private var bEditedItemPositionChanged:Boolean = false; |
| |
| /** |
| * @private |
| * undefined means we've processed it |
| * null means don't put up an editor |
| * {} is the coordinates for the editor |
| */ |
| private var _proposedEditedItemPosition:*; |
| |
| /** |
| * @private |
| * the last editedItemPosition. We restore editing |
| * to this point if we get focus from the TAB key |
| */ |
| private var lastEditedItemPosition:*; |
| |
| private var _headerWordWrapPresent:Boolean = false; |
| private var _originalExplicitHeaderHeight:Boolean = false; |
| private var _originalHeaderHeight:Number = 0; |
| |
| /** |
| * @private |
| * true if based on mouse position, a dropIndex has been found |
| */ |
| private var dropIndexFound:Boolean = false; |
| |
| /** |
| * @private |
| * true if header getting dragged is outside the permissible area |
| */ |
| private var isHeaderDragOutside:Boolean = false; |
| |
| /** |
| * The AdvancedDataGridHeaderInfo instances that |
| * correspond to the currently selected column header. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| mx_internal var selectedHeaderInfo:AdvancedDataGridHeaderInfo; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // itemRenderer |
| //---------------------------------- |
| |
| /** |
| * @private |
| * |
| * Defer creation of the class factory to give a chance for the moduleFactory to be set. |
| */ |
| override public function get itemRenderer():IFactory |
| { |
| if (super.itemRenderer == null) |
| { |
| var fontName:String = StringUtil.trimArrayElements(getStyle("fontFamily"),","); |
| var fontWeight:String = getStyle("fontWeight"); |
| var fontStyle:String = getStyle("fontStyle"); |
| var bold:Boolean = (fontWeight == "bold"); |
| var italic:Boolean = (fontStyle == "italic"); |
| var flexModuleFactory:IFlexModuleFactory = getFontContext(fontName, bold, italic); |
| |
| var c:Class = getStyle("defaultDataGridItemRenderer"); |
| if (!c) |
| c = AdvancedDataGridItemRenderer; |
| |
| super.itemRenderer = new ContextualClassFactory(c, flexModuleFactory); |
| |
| } |
| |
| return super.itemRenderer; |
| } |
| |
| //---------------------------------- |
| // baselinePosition |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| override public function get baselinePosition():Number |
| { |
| var top:Number = 0; |
| |
| if (border && border is IBorder) |
| top = IBorder(border).borderMetrics.top; |
| |
| return top + measureText(" ").ascent; |
| } |
| |
| /** |
| * @private |
| * Number of columns that can be displayed. |
| * Some may be offscreen depending on horizontalScrollPolicy |
| * and the width of the AdvancedDataGrid. |
| */ |
| override public function get columnCount():int |
| { |
| if (_columns) |
| return _columns.length; |
| else |
| return 0; |
| } |
| |
| //---------------------------------- |
| // enabled |
| //---------------------------------- |
| |
| [Inspectable(category="General", enumeration="true,false", defaultValue="true")] |
| |
| /** |
| * @private |
| */ |
| override public function set enabled(value:Boolean):void |
| { |
| super.enabled = value; |
| |
| if (itemEditorInstance) |
| endEdit(AdvancedDataGridEventReason.OTHER); |
| |
| invalidateDisplayList(); |
| } |
| |
| //---------------------------------- |
| // horizontalScrollPosition |
| //---------------------------------- |
| |
| /** |
| * The offset into the content from the left edge. |
| * This can be a pixel offset in some subclasses or some other metric |
| * like the number of columns in an AdvancedDataGrid control. |
| * |
| * The AdvancedDataGrid scrolls by columns so the value of the |
| * <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, which scrolls by pixels. |
| * The AdvancedDataGrid control always aligns the left edge |
| * of a column with the left edge of the AdvancedDataGrid control. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| override public function set horizontalScrollPosition(value:Number):void |
| { |
| // if not init or no data; |
| if (!initialized || listItems.length == 0) |
| { |
| super.horizontalScrollPosition = value; |
| return; |
| } |
| |
| var oldValue:int = super.horizontalScrollPosition; |
| super.horizontalScrollPosition = value; |
| |
| columnsInvalid = true; |
| calculateColumnSizes(); |
| |
| // we are going to get a full repaint so don't repaint now |
| if (itemsSizeChanged) |
| return; |
| |
| if (oldValue != value) |
| { |
| removeClipMask(); |
| |
| if (getOptimumColumns() == visibleColumns) |
| { |
| //clearIndicators(); |
| visibleData = {}; |
| |
| // columns have variable width so we need to recalc scroll parms |
| scrollAreaChanged = true; |
| |
| var bookmark:CursorBookmark; |
| |
| if (iterator) |
| bookmark = iterator.bookmark; |
| |
| //if we scrolled more than the number of scrollable columns |
| makeRowsAndColumns(0, 0, listContent.width, listContent.height, 0, 0); |
| |
| if (iterator && bookmark) |
| iterator.seek(bookmark, 0); |
| |
| } |
| else |
| { |
| // In case of column grouping and |
| // column span we just move the scroll rect |
| subContentScrolled = true; |
| } |
| |
| updateSubContent(); |
| updateHeaderSearchList(); |
| |
| addClipMask(false); |
| |
| //an invalidation is needed, to redraw the vertical lines and separators |
| invalidateDisplayList(); |
| } |
| } |
| |
| //---------------------------------- |
| // verticalScrollPosition |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Sets verticalScrollPosition and draw horizontal lines again |
| * variableRowHeight is true. |
| */ |
| override public function set verticalScrollPosition(value:Number):void |
| { |
| super.verticalScrollPosition = value; |
| |
| // draw the horizontal lines afresh if variableRowHeight is true |
| // i.e., row height may differ for each row |
| if (variableRowHeight) |
| drawHorizontalSeparators(); |
| } |
| |
| //---------------------------------- |
| // focusPane |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| override public function set focusPane(value:Sprite):void |
| { |
| super.focusPane = value; |
| |
| if (value) |
| value.scrollRect = listSubContent ? listSubContent.scrollRect : null; |
| |
| if (!value && _focusPane) |
| _focusPane.mask = null; |
| _focusPane = value; |
| } |
| |
| //---------------------------------- |
| // horizontalScrollPolicy |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Accomodates ScrollPolicy.AUTO. |
| * Makes sure column widths stay in synch. |
| * |
| * @param policy on, off, or auto |
| */ |
| override public function set horizontalScrollPolicy(value:String):void |
| { |
| super.horizontalScrollPolicy = value; |
| columnsInvalid = true; |
| itemsSizeChanged = true; |
| invalidateDisplayList(); |
| } |
| |
| //---------------------------------- |
| // lockedColumnCount |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| override public function set lockedColumnCount(value:int):void |
| { |
| var i:int = 0; |
| var j:int = 0; |
| var m:int = 0; |
| // remove the items from columnMap, so that they can be created again |
| if (value > super.lockedColumnCount) |
| { |
| for (i = super.lockedColumnCount; i < value ;i++) |
| { |
| m = listItems.length; |
| for(j = 0; j < m; j++) |
| { |
| if (listItems[j] && listItems[j][i]) |
| delete columnMap[listItems[j][i].name]; |
| } |
| } |
| } |
| else if (value < super.lockedColumnCount) |
| { |
| for (i = value; i < super.lockedColumnCount ;i++) |
| { |
| m = listItems.length; |
| for(j = 0; j < m; j++) |
| { |
| if (listItems[j] && listItems[j][i]) |
| delete columnMap[listItems[j][i].name]; |
| } |
| } |
| } |
| super.lockedColumnCount = value; |
| |
| //listSubContent scrollRectneed to be changed in case lockedColumnCount has changed |
| // otherwise items in the scrollrect overlap with the items which have come |
| // because of change in lockedColumnCount |
| updateSubContent(); |
| |
| itemsSizeChanged = true; |
| |
| columnsInvalid = true; |
| |
| // set the horizontalScrollPosition so that all the changes are reflected correctly |
| horizontalScrollPosition = super.horizontalScrollPosition; |
| } |
| |
| //---------------------------------- |
| // dragImage |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| override protected function get dragImage():IUIComponent |
| { |
| var image:AdvancedDataGridDragProxy = new AdvancedDataGridDragProxy(); |
| image.owner = this; |
| image.moduleFactory = moduleFactory; |
| return image; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // columns |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| // Added to AdvancedDataGridBase |
| //private var _columns:Array; // the array of our AdvancedDataGridColumns |
| |
| [Bindable("columnsChanged")] |
| [Inspectable(category="General", arrayType="mx.controls.advancedDataGridClasses.AdvancedDataGridColumn")] |
| |
| /** |
| * An array of AdvancedDataGridColumn objects, one for each column that |
| * can be displayed. If not explicitly set, the AdvancedDataGrid control |
| * attempts to examine the first data provider item to determine the |
| * set of properties and display those properties in alphabetic |
| * order. |
| * |
| * <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 <code>columns</code> property. This is because |
| * the AdvancedDataGrid control returns a copy of the Array of columns, |
| * not a reference, and therefore cannot detect changes to the copy.</p> |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get columns():Array |
| { |
| return _columns.slice(0); |
| } |
| |
| /** |
| * @private |
| */ |
| public function set columns(value:Array):void |
| { |
| var n:int; |
| var i:int; |
| |
| // remove the header items |
| purgeHeaderRenderers(); |
| |
| n = _columns.length; |
| for (i = 0; i < n; i++) |
| { |
| columnRendererChanged(_columns[i]); |
| } |
| |
| freeItemRenderersTable = new Dictionary(false); |
| itemRendererToFactoryMap = new Dictionary(true); |
| columnMap = {}; |
| |
| _columns = value.slice(0); |
| columnsInvalid = true; |
| generatedColumns = false; |
| |
| n = value.length; |
| for (i = 0; i < n; i++) |
| { |
| var column:AdvancedDataGridColumn = _columns[i]; |
| column.owner = this; |
| column.colNum = i; |
| } |
| |
| updateSortIndexAndDirection(); |
| itemsSizeChanged = true; |
| columnsChanged = true; |
| invalidateDisplayList(); |
| dispatchEvent(new Event("columnsChanged")); |
| } |
| |
| /** |
| * An array of AdvancedDataGridColumn 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")] |
| |
| /** |
| * Indicates whether you are allowed to reorder columns. |
| * If <code>true</code>, you can reorder the columns |
| * of the AdvancedDataGrid control by dragging the header cells. |
| * |
| * @default true |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get draggableColumns():Boolean |
| { |
| return _draggableColumns; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set draggableColumns(value:Boolean):void |
| { |
| _draggableColumns = value; |
| } |
| |
| //---------------------------------- |
| // enableIME |
| //---------------------------------- |
| |
| /** |
| * A flag that indicates whether the IME should |
| * be enabled when the component receives focus. |
| * |
| * If the editor is up, it will set enableIME |
| * accordingly. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @productversion Flex 4 |
| */ |
| public function get enableIME():Boolean |
| { |
| return false; |
| } |
| |
| //---------------------------------- |
| // imeMode |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _imeMode:String = null; |
| |
| [Inspectable(environment="none")] |
| |
| /** |
| * Specifies the IME (input method editor) mode. |
| * The IME mode enables users to enter text in Chinese, Japanese, and Korean. |
| * Flex sets the specified IME mode when the control gets the focus, |
| * and sets it back to the previous value when the control loses the focus. |
| * |
| * <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; |
| } |
| |
| //---------------------------------- |
| // minColumnWidth |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _minColumnWidth:Number; |
| |
| [Inspectable(defaultValue="NaN")] |
| |
| /** |
| * The minimum width of the columns, in pixels. If not NaN, |
| * the AdvancedDataGrid control applies this value as the minimum width for |
| * all columns. Otherwise, individual columns can have |
| * their own minimum widths. |
| * |
| * @default NaN |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get minColumnWidth():Number |
| { |
| return _minColumnWidth; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set minColumnWidth(value:Number):void |
| { |
| _minColumnWidth = value; |
| minColumnWidthInvalid = true; |
| itemsSizeChanged = true; |
| columnsInvalid = true; |
| invalidateDisplayList(); |
| } |
| |
| //---------------------------------- |
| // itemEditorInstance |
| //---------------------------------- |
| |
| [Inspectable(environment="none")] |
| |
| /** |
| * A reference to the currently active instance of the item editor, |
| * if it exists. |
| * |
| * <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>AdvancedDataGridColumn.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; |
| |
| //---------------------------------- |
| // editedItemRenderer |
| //---------------------------------- |
| |
| /** |
| * A reference to the item renderer |
| * in the AdvancedDataGrid 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 listItems[actualRowIndex][actualColIndex]; |
| } |
| |
| //---------------------------------- |
| // headerIndex |
| //---------------------------------- |
| |
| /** |
| * @private |
| * Storage for headerIndex |
| */ |
| private var _headerIndex:int = -1; |
| |
| /** |
| * If a header is selected via keyboard. |
| * |
| * headerIndex is the absolute column number i.e. index of 'columns'. |
| * |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| mx_internal function set headerIndex(value:int):void |
| { |
| _headerIndex = value; |
| |
| dispatchEvent(new ListEvent(ListEvent.CHANGE)); |
| } |
| |
| mx_internal function get headerIndex():int |
| { |
| return _headerIndex; |
| |
| } |
| |
| //---------------------------------- |
| // editable |
| //---------------------------------- |
| |
| private var _editable:String = ""; |
| |
| [Inspectable(category="General")] |
| /** |
| * Indicates whether or not the user can edit items in the data provider. |
| * |
| * <p>If <code>"item"</code>, the item renderers in the control are editable. |
| * The user can click on an item renderer to open an editor.</p> |
| * |
| * <p>If <code>"item group"</code>, the item renderers and grouping headers can be edited.</p> |
| * |
| * <p>If <code>"item summary"</code>, the item renderers and summary cells can be edited.</p> |
| * |
| * <p>You can combine these values. For example, <code>editable = "item group summary"</code>. |
| * Note that item editing has to be enabled if enabling group or summary editing.</p> |
| * |
| * <p>If you specify an empty String, no editing is allowed.</p> |
| * |
| * <p>The values <code>"true"</code> and <code>"false"</code> correspond |
| * to item editing and no editing.</p> |
| * |
| * <p>A value of <code>"all"</code> means everything is editable.</p> |
| * |
| * <p>You can turn off editing for individual columns of the |
| * AdvancedDataGrid control using the <code>AdvancedDataGridColumn.editable</code> property, |
| * or by handling the <code>itemEditBeginning</code> and |
| * <code>itemEditBegin</code> events.</p> |
| * |
| * @default "" |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function get editable():String |
| { |
| return _editable; |
| } |
| |
| public function set editable(value:String):void |
| { |
| _editable = ""; |
| |
| if (!value) |
| return; |
| |
| var editableFlags:Array = value.split(" "); // space delimited |
| var n:int = editableFlags.length; |
| var keepProcessingFlags:Boolean = true; |
| |
| for (var i:int = 0; i < n && keepProcessingFlags; i++) |
| { |
| switch (editableFlags[i]) |
| { |
| case "item": |
| case "group": |
| case "summary": |
| { |
| _editable += editableFlags[i] + " "; |
| break; |
| } |
| |
| case "true": |
| { |
| _editable = "item" + " "; |
| keepProcessingFlags = false; |
| break; |
| } |
| |
| case "false": |
| { |
| _editable = "" + " "; |
| keepProcessingFlags = false; |
| break; |
| } |
| |
| case "all": |
| { |
| _editable = "item group summary" + " "; |
| keepProcessingFlags = false; |
| break; |
| } |
| } |
| } |
| _editable = _editable.slice(0, -1); // remove trailing space |
| } |
| |
| //---------------------------------- |
| // editedItemPosition |
| //---------------------------------- |
| |
| /** |
| * @private |
| */ |
| private var _editedItemPosition:Object; |
| |
| [Bindable("itemFocusIn")] |
| |
| /** |
| * The column and row index of the item renderer for the |
| * data provider item being edited, if any. |
| * |
| * <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); |
| } |
| |
| //---------------------------------- |
| // lookAheadDuration |
| //---------------------------------- |
| |
| [Inspectable(defaultValue="400")] |
| /** |
| * The type look-ahead duration, in milliseconds, for multi-character look ahead. |
| * Setting it to 0 will turn off multiple character type ahead lookup. |
| * |
| * @default 400 |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public var lookAheadDuration:Number = 400; |
| |
| //---------------------------------- |
| // resizableColumns |
| //---------------------------------- |
| |
| [Inspectable(category="General")] |
| |
| /** |
| * A flag that indicates whether the user can change the size of the |
| * columns. |
| * If <code>true</code>, the user can stretch or shrink the columns of |
| * the AdvancedDataGrid control by dragging the grid lines between the header cells. |
| * If <code>true</code>, individual columns must also have their |
| * <code>resizeable</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>AdvancedDataGridColumn.dataField</code> property of the column |
| * or the <code>AdvancedDataGridColumn.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 AdvancedDataGrid |
| * 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 |
| * AdvancedDataGrid sorts using that column's <code>AdvancedDataGridColumn.dataField</code> or |
| * <code>AdvancedDataGridColumn.sortCompareFunction</code> properties.</p> |
| * |
| * @default true |
| * |
| * @see mx.controls.advancedDataGridClasses.AdvancedDataGridColumn#dataField |
| * @see mx.controls.advancedDataGridClasses.AdvancedDataGridColumn#sortCompareFunction |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public var sortableColumns:Boolean = true; |
| |
| //---------------------------------- |
| // sortExpertMode |
| //---------------------------------- |
| |
| // Type of sorting UI displayed |
| private var _sortExpertMode:Boolean = false; |
| |
| /** |
| * By default, the <code>sortExpertMode</code> property is set to <code>false</code>, |
| * which means you click in the header area of a column to sort the rows of |
| * the AdvancedDataGrid control by that column. |
| * You then click in the multiple-column sort area of the header to sort by additional columns. |
| * If you set the <code>sortExpertMode</code> property to <code>true</code>, |
| * you use the Control key to select every column after the first column to perform sort. |
| * |
| * @default false |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| [Inspectable(enumeration="true,false", defaultValue="false")] |
| public function get sortExpertMode():Boolean |
| { |
| return _sortExpertMode; |
| } |
| |
| /** |
| * @private |
| */ |
| public function set sortExpertMode(value:Boolean):void |
| { |
| _sortExpertMode = value; |
| |
| invalidateHeaders(); |
| invalidateProperties(); |
| invalidateDisplayList(); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| [Inspectable(category="Data", defaultValue="undefined")] |
| |
| /** |
| * @private |
| */ |
| override public function set dataProvider(value:Object):void |
| { |
| if (itemEditorInstance) |
| endEdit(AdvancedDataGridEventReason.OTHER); |
| |
| lastEditedItemPosition = null; |
| |
| super.dataProvider = value; |
| |
| invalidateProperties(); |
| } |
| |
| /** |
| * @private |
| * Adds support for multiple characters type ahead lookup. |
| */ |
| override protected function findKey(eventCode:int):Boolean |
| { |
| var tmpCode:int = eventCode; |
| |
| // get the timer value now |
| var now:uint = getTimer(); |
| var str:String = String.fromCharCode(tmpCode); |
| |
| if (!(tmpCode >= 33 && tmpCode <= 126)) |
| return false; |
| // store the value of the _selectedIndex |
| var selIndex:Number = _selectedIndex; |
| |
| // compare the timer value with the previously stored |
| // timer value and set up multiple character type ahead |
| // lookup. |
| if ((now - previousTime) < lookAheadDuration) |
| { |
| str = lookAheadCache + str; |
| lookAheadCache = str; |
| previousTime = now; |
| // decrement the _selecteIndex |
| // we want the lookup to start from the previous item |
| if (_selectedIndex > 0) |
| { |
| selIndex = _selectedIndex; |
| _selectedIndex--; |
| } |
| } |
| else |
| { |
| previousTime = now; |
| lookAheadCache = str; |
| } |
| |
| var selectionChanged:Boolean = findString(str); |
| |
| // set the _selectedIndex back if we cant find the item |
| if (!selectionChanged && _selectedIndex != selIndex) |
| _selectedIndex = selIndex; |
| |
| return selectionChanged; |
| } |
| |
| /** |
| * @private |
| * Measures the AdvancedDataGrid based on its contents, |
| * summing the total of the visible column widths. |
| */ |
| override protected function measure():void |
| { |
| super.measure(); |
| |
| var o:EdgeMetrics = viewMetrics; |
| |
| var n:int = _columns.length; |
| if (n == 0) |
| { |
| measuredWidth = DEFAULT_MEASURED_WIDTH; |
| measuredMinWidth = DEFAULT_MEASURED_MIN_WIDTH; |
| return; |
| } |
| |
| var columnWidths:Number = 0; |
| var columnMinWidths:Number = 0; |
| for (var i:int = 0; i < n; i++) |
| { |
| if (_columns[i].visible) |
| { |
| columnWidths += _columns[i].preferredWidth; |
| if (isNaN(_minColumnWidth)) |
| columnMinWidths += _columns[i].minWidth; |
| } |
| } |
| |
| if (!isNaN(_minColumnWidth)) |
| columnMinWidths = n * _minColumnWidth; |
| |
| measuredWidth = columnWidths + o.left + o.right; |
| measuredMinWidth = columnMinWidths + o.left + o.right; |
| |
| // factor out scrollbars if policy == AUTO. See Container.viewMetrics |
| if (verticalScrollPolicy == ScrollPolicy.AUTO && |
| verticalScrollBar && verticalScrollBar.visible) |
| { |
| measuredWidth -= verticalScrollBar.minWidth; |
| measuredMinWidth -= verticalScrollBar.minWidth; |
| } |
| if (horizontalScrollPolicy == ScrollPolicy.AUTO && |
| horizontalScrollBar && horizontalScrollBar.visible) |
| { |
| measuredHeight -= horizontalScrollBar.minHeight; |
| measuredMinHeight -= horizontalScrollBar.minHeight; |
| } |
| |
| } |
| /** |
| * @private |
| * Sizes and positions the column headers, columns, and items based on the |
| * size of the AdvancedDataGrid. |
| */ |
| override protected function updateDisplayList(unscaledWidth:Number, |
| unscaledHeight:Number):void |
| { |
| // Note: We can't immediately call super.updateDisplayList() |
| // because the visibleColumns array must be populated first. |
| var updateContent:Boolean = false; |
| if (displayWidth != unscaledWidth - viewMetrics.right - viewMetrics.left) |
| { |
| displayWidth = unscaledWidth - viewMetrics.right - viewMetrics.left; |
| columnsInvalid = true; |
| updateContent = true; |
| } |
| |
| |
| calculateColumnSizes(); |
| |
| if (updateContent) |
| updateSubContent(); |
| |
| if (rendererChanged) |
| purgeItemRenderers(); |
| |
| super.updateDisplayList(unscaledWidth, unscaledHeight); |
| |
| // We need to explicitly call configureScrollBars |
| // when horizontal scrolling is optimized. In this case |
| // because scrollAreaChanged is false, super doesn't |
| // configure scrollbars. |
| if (horizontalScrollPolicy != ScrollPolicy.OFF |
| && getOptimumColumns() != visibleColumns |
| && !itemsSizeChanged && !bSelectionChanged |
| && !scrollAreaChanged |
| && subContentScrolled) |
| { |
| configureScrollBars(); |
| subContentScrolled = false; |
| } |
| |
| if (collection && collection.length) |
| { |
| setRowCount(listItems.length); |
| |
| if (headerInfos && headerInfos.length) |
| setColumnCount(headerInfos.length); |
| else |
| setColumnCount(0); |
| } |
| |
| if (_horizontalScrollPolicy == ScrollPolicy.OFF) |
| { |
| // If we have a vScroll only and if we have room to fit the scrollbar below the header, |
| // we want the scrollbar to be below |
| |
| var bm:EdgeMetrics = borderMetrics; |
| var hh:Number = headerRowInfo.length ? headerRowInfo[0].height : headerHeight; |
| |
| if (verticalScrollBar != null && verticalScrollBar.visible && headerVisible |
| && roomForScrollBar(verticalScrollBar, |
| unscaledWidth-bm.left-bm.right, |
| unscaledHeight-hh-bm.top-bm.bottom)) |
| { |
| verticalScrollBar.move(verticalScrollBar.x, viewMetrics.top + hh); |
| verticalScrollBar.setActualSize( |
| verticalScrollBar.width, |
| unscaledHeight - viewMetrics.top - viewMetrics.bottom - hh); |
| verticalScrollBar.visible = (verticalScrollBar.height >= verticalScrollBar.minHeight); |
| } |
| } |
| if (bEditedItemPositionChanged) |
| { |
| bEditedItemPositionChanged = false; |
| // don't do this if mouse is down on an item |
| // on mouse up, we'll let the edit session logic |
| // request a new position |
| if (!lastItemDown) |
| commitEditedItemPosition(_proposedEditedItemPosition); |
| _proposedEditedItemPosition = undefined; |
| itemsSizeChanged = false; |
| } |
| |
| var headerBG:UIComponent = |
| UIComponent(listContent.getChildByName("headerBG")); |
| |
| if (headerBGSkinChanged) |
| { |
| headerBGSkinChanged = false; |
| if (headerBG) |
| listContent.removeChild(headerBG); |
| headerBG = null; |
| } |
| |
| if (!headerBG) |
| { |
| headerBG = new UIComponent(); |
| headerBG.name = "headerBG"; |
| listContent.addChildAt(DisplayObject(headerBG), listContent.getChildIndex(selectionLayer)); |
| |
| var headerBGSkinClass:Class = getStyle("headerBackgroundSkin"); |
| |
| if (headerBGSkinClass != null) |
| { |
| var headerBGSkin:IFlexDisplayObject = new headerBGSkinClass(); |
| |
| if (headerBGSkin is ISimpleStyleClient) |
| ISimpleStyleClient(headerBGSkin).styleName = this; |
| headerBG.addChild(DisplayObject(headerBGSkin)); |
| } |
| } |
| |
| if (headerVisible) |
| { |
| headerBG.visible = true; |
| if (useOldDGHeaderBGLogic) |
| { |
| drawHeaderBackground(headerBG); |
| } |
| else |
| { |
| if (headerBG.numChildren > 0) |
| drawHeaderBackgroundSkin(IFlexDisplayObject(headerBG.getChildAt(0))); |
| } |
| } |
| else |
| { |
| headerBG.visible = false; |
| } |
| |
| drawRowBackgrounds(); |
| |
| if (headerVisible) |
| drawSeparators(); |
| else |
| clearSeparators(); |
| |
| drawLinesAndColumnBackgrounds(); |
| |
| // trace("<<updateDisplayList"); |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function adjustListContent(unscaledWidth:Number = -1, |
| unscaledHeight:Number = -1):void |
| { |
| super.adjustListContent(unscaledWidth, unscaledHeight); |
| |
| if(getOptimumColumns() != visibleColumns) |
| { |
| var n:int = displayableColumns.length; |
| var listSubContentWidth:int = 0; |
| for(var i:int = lockedColumnCount; i < n; i++) |
| { |
| listSubContentWidth += displayableColumns[i].width; |
| } |
| |
| listSubContentWidth += getLastColumnResidualWidth() + 50; //Adding default sort renderer's width |
| listSubContent.setActualSize(listSubContentWidth,unscaledHeight); |
| } |
| |
| // listSubContent scrollRect needs to be updated whenever |
| // listContent size is adjusted |
| updateSubContent(); |
| } |
| |
| /** |
| * @private |
| */ |
| private function getLastColumnResidualWidth():Number |
| { |
| var n:int = displayableColumns.length-1; |
| var displayWidth:int = unscaledWidth - viewMetrics.right - viewMetrics.left; |
| var totalWidth:Number = 0; |
| var i:int; |
| var numLockCols:int = Math.max(0, lockedColumnCount); |
| |
| //Find the scrollable width i.e displayWidth - { sum of width of locked columns} |
| if (numLockCols > 0 && numLockCols < visibleColumns.length) |
| { |
| for (i = 0; i < numLockCols; i++) |
| { |
| displayWidth -= displayableColumns[i].width; |
| } |
| } |
| |
| // Starting from the right most column in the displayableColumns array |
| // find out how many columns we will be able to |
| // accumulate and how much width they will take |
| // when horizontal scroll bar is at the rightmost end |
| if (n>=0) |
| { |
| totalWidth = (isNaN(displayableColumns[n].explicitWidth) ? displayableColumns[n].preferredWidth : displayableColumns[n].explicitWidth); |
| } |
| |
| for (i = n-1; i >= numLockCols; i--) |
| { |
| if (totalWidth + displayableColumns[i].width <= displayWidth) |
| totalWidth += displayableColumns[i].width; |
| else |
| break; |
| } |
| |
| // The residual width is the width which should be added to |
| // the last column apriori, so that in future when we have horizontally |
| // scrolled to the right most position we don't need to create larger |
| // items for that column |
| return displayWidth - totalWidth; |
| } |
| |
| // horizontal page up, page down |
| /** |
| * @private |
| */ |
| override protected function moveSelectionHorizontally(code:uint, |
| shiftKey:Boolean, |
| ctrlKey:Boolean):void |
| { |
| // The new calculated value of the horizontal scroll position |
| var newHorizontalScrollPosition:Number; |
| // Has the horizontal scroll position actually changed? |
| var bUpdateHorizontalScrollPosition:Boolean = false; |
| // Max horizontal position |
| var maxPosition:int; |
| |
| if (shiftKey && code == Keyboard.PAGE_UP) |
| { |
| newHorizontalScrollPosition = Math.max( |
| horizontalScrollPosition - (visibleColumns.length - lockedColumnCount) |
| , 0); |
| |
| if (newHorizontalScrollPosition != horizontalScrollPosition) |
| bUpdateHorizontalScrollPosition = true; |
| } |
| else if (shiftKey && code == Keyboard.PAGE_DOWN) |
| { |
| // We don't want to exceed the max scroll value or the last column's index |
| maxPosition = Math.min(maxHorizontalScrollPosition, _columns.length-1); |
| newHorizontalScrollPosition = Math.min( |
| horizontalScrollPosition + (visibleColumns.length - lockedColumnCount) |
| , maxPosition); |
| |
| if (newHorizontalScrollPosition != horizontalScrollPosition) |
| bUpdateHorizontalScrollPosition = true; |
| } |
| else |
| { |
| super.moveSelectionHorizontally(code, shiftKey, ctrlKey); |
| } |
| |
| // Mark the event of the horizontal scroll position changing |
| if (bUpdateHorizontalScrollPosition) |
| { |
| var scrollEvent:ScrollEvent = new ScrollEvent(ScrollEvent.SCROLL); |
| scrollEvent.detail = ScrollEventDetail.THUMB_POSITION; |
| scrollEvent.direction = ScrollEventDirection.HORIZONTAL; |
| scrollEvent.delta = newHorizontalScrollPosition - horizontalScrollPosition; |
| scrollEvent.position = newHorizontalScrollPosition; |
| horizontalScrollPosition = newHorizontalScrollPosition; |
| dispatchEvent(scrollEvent); |
| |
| if (headerIndex != -1) |
| unselectColumnHeader(headerIndex); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function makeRowsAndColumns(left:Number, top:Number, |
| right:Number, bottom:Number, |
| firstCol:int, firstRow:int, |
| byCount:Boolean = false, rowsNeeded:uint = 0):Point |
| { |
| listContent.allowItemSizeChangeNotification = false; |
| listSubContent.allowItemSizeChangeNotification = false; |
| |
| if (headerVisible && itemsSizeChanged) |
| calculateHeaderHeight(); |
| |
| var pt:Point = super.makeRowsAndColumns(left, top, right, bottom, |
| firstCol, firstRow, byCount, rowsNeeded); |
| var optimumColumns:Array = getOptimumColumns(); |
| if (itemEditorInstance) |
| { |
| itemEditorInstance.parent.setChildIndex(DisplayObject(itemEditorInstance), |
| itemEditorInstance.parent.numChildren - 1); |
| var col:AdvancedDataGridColumn = optimumColumns[actualColIndex]; |
| var item:IListItemRenderer = listItems[actualRowIndex][actualColIndex]; |
| var rowData:ListRowInfo = rowInfo[actualRowIndex]; |
| if (item && !col.rendererIsEditor) |
| { |
| var dx:Number = col.editorXOffset; |
| var dy:Number = col.editorYOffset; |
| var dw:Number = col.editorWidthOffset; |
| var dh:Number = col.editorHeightOffset; |
| itemEditorInstance.move(item.x + dx, rowData.y + dy); |
| itemEditorInstance.setActualSize(Math.min(col.width + dw, listContent.width - listContent.x - itemEditorInstance.x), |
| Math.min(rowData.height + dh, listContent.height - listContent.y - itemEditorInstance.y)); |
| // Commenting to show the item (with disclosure icon) behind the item editor |
| //item.visible = false; |
| |
| } |
| } |
| |
| var lines:Sprite = Sprite(listSubContent.getChildByName("lines")); |
| if (lines) |
| listSubContent.setChildIndex(lines, listSubContent.numChildren - 1); |
| |
| |
| listContent.allowItemSizeChangeNotification = variableRowHeight; |
| listSubContent.allowItemSizeChangeNotification = variableRowHeight; |
| return pt; |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function commitProperties():void |
| { |
| if(columnsInvalid) |
| { |
| // initializeHeaderInfo need to be called only if columns array have been changed |
| // no need to call it everytime columnsInvalid becomes true |
| if(columnsChanged && !headerInfoInitialized) |
| { |
| headerInfos = initializeHeaderInfo(columns); |
| headerInfoInitialized = true; |
| } |
| |
| columnsChanged = false; |
| |
| visibleHeaderInfos = updateVisibleHeaders(); |
| updateHeaderSearchList(); |
| |
| createDisplayableColumns(); |
| |
| // It is possible that columns became invisible and the hsp is no longer valid. |
| // Force visibleColumns to be recomputed now so if there are lockedColumns and |
| // updateSubContent() is called before updateDisplayList() is called, |
| // visibleColumns will be correct. |
| if (visibleHeaderInfos) |
| { |
| if (horizontalScrollPosition > visibleHeaderInfos.length) |
| horizontalScrollPosition = visibleHeaderInfos.length - 1; |
| } |
| else |
| { |
| horizontalScrollPosition = 0; |
| } |
| } |
| |
| super.commitProperties(); |
| |
| measureItems(); |
| } |
| |
| /** |
| * @private |
| * Instead of measuring the items, we measure the visible columns instead. |
| */ |
| override public function measureWidthOfItems(index:int = -1, count:int = 0):Number |
| { |
| var w:Number = 0; |
| |
| var n:int = _columns ? _columns.length : 0; |
| for (var i:int = 0; i < n; i++) |
| { |
| if (_columns[i].visible) |
| w += _columns[i].width; |
| } |
| |
| return w; |
| } |
| |
| /** |
| * @private |
| */ |
| override public function measureHeightOfItems(index:int = -1, count:int = 0):Number |
| { |
| return measureHeightOfItemsUptoMaxHeight(index, count); |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function calculateRowHeight(data:Object, hh:Number, skipVisible:Boolean = false):Number |
| { |
| var item:IListItemRenderer; |
| var c:AdvancedDataGridColumn; |
| |
| var n:int = _columns.length; |
| var i:int; |
| var j:int = 0; |
| |
| if (skipVisible && visibleColumns.length == _columns.length) |
| return hh; |
| |
| var paddingTop:Number = getStyle("paddingTop"); |
| var paddingBottom:Number = getStyle("paddingBottom"); |
| |
| if (!measuringObjects) |
| measuringObjects = new Dictionary(false); |
| |
| for (i = 0; i < n; i++) |
| { |
| // skip any columns that are visible |
| if (skipVisible && j < visibleColumns.length && visibleColumns[j].colNum == _columns[i].colNum) |
| { |
| j++; |
| continue; |
| } |
| c = _columns[i]; |
| |
| if (!c.visible) |
| continue; |
| |
| item = getMeasuringRenderer(c, false,data); |
| setupRendererFromData(c, item, data); |
| hh = Math.max(hh, item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop); |
| } |
| return hh; |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function scrollHandler(event:Event):void |
| { |
| if (event.target == verticalScrollBar || |
| event.target == horizontalScrollBar) |
| { |
| // TextField.scroll bubbles so you might see it here |
| if (event is ScrollEvent) |
| { |
| if (!liveScrolling && |
| ScrollEvent(event).detail == ScrollEventDetail.THUMB_TRACK) |
| { |
| return; |
| } |
| |
| if (itemEditorInstance) |
| endEdit(AdvancedDataGridEventReason.OTHER); |
| |
| var scrollBar:ScrollBar = ScrollBar(event.target); |
| var pos:Number = scrollBar.scrollPosition; |
| |
| if (scrollBar == verticalScrollBar) |
| verticalScrollPosition = pos; |
| else if (scrollBar == horizontalScrollBar) |
| horizontalScrollPosition = pos; |
| |
| super.scrollHandler(event); |
| } |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function configureScrollBars():void |
| { |
| var oldHorizontalScrollBar:Object = horizontalScrollBar; |
| var oldVerticalScrollBar:Object = verticalScrollBar; |
| |
| var rowCount:int = listItems.length; |
| // check whether the header items are present |
| if (rowCount + getHeaderItemsLength() == 0) |
| { |
| // Get rid of any existing scrollbars. |
| if (oldHorizontalScrollBar || oldVerticalScrollBar) |
| setScrollBarProperties(0, 0, 0, 0); |
| |
| return; |
| } |
| |
| var vScrollProperties:Array; |
| var hScrollProperties:Array; |
| |
| // partial last rows don't count |
| if (rowCount > 1 && rowInfo[rowCount - 1].y + rowInfo[rowCount - 1].height > listContent.height) |
| rowCount--; |
| |
| // offset, when added to rowCount, is the index of the dataProvider |
| // item for that row. IOW, row 10 in listItems is showing dataProvider |
| // item 10 + verticalScrollPosition - lockedRowCount; |
| var offset:int = verticalScrollPosition - lockedRowCount; |
| // don't count filler rows at the bottom either. |
| var fillerRows:int = 0; |
| while (rowCount && listItems[rowCount - 1].length == 0) |
| { |
| // as long as we're past the end of the collection, add up |
| // fillerRows |
| if (collection && rowCount + offset >= collection.length) |
| { |
| rowCount--; |
| ++fillerRows; |
| } |
| else |
| { |
| break; |
| } |
| } |
| |
| // we have to scroll up. We can't have filler rows unless the scrollPosition is 0 |
| if (verticalScrollPosition > 0 && fillerRows > 0) |
| { |
| if (adjustVerticalScrollPositionDownward(Math.max(rowCount, 1))) |
| return; |
| } |
| |
| vScrollProperties = [collection ? collection.length - lockedRowCount : 0, |
| Math.max(rowCount - lockedRowCount, 1)]; |
| |
| var colCount:int = visibleColumns.length; |
| var lastHeaderInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(visibleColumns[visibleColumns.length - 1]); |
| var headerPosX:int = lastHeaderInfo.headerItem.x; |
| if(visibleColumns.length - 1 > lockedColumnCount) |
| headerPosX = getAdjustedXPos(headerPosX); |
| |
| // if the last column is visible and partially offscreen (but it isn't the only |
| // column) then adjust the column count so we can scroll to see it |
| if (colCount > 1 && visibleColumns[colCount - 1] == displayableColumns[displayableColumns.length - 1] |
| && headerPosX + visibleColumns[colCount - 1].width > displayWidth) |
| { |
| colCount--; |
| } |
| |
| hScrollProperties = [displayableColumns.length - lockedColumnCount, |
| Math.max(colCount - lockedColumnCount, 1)]; |
| |
| |
| //Finally set both the scroll bar properties |
| setScrollBarProperties(hScrollProperties[0], hScrollProperties[1], |
| vScrollProperties[0], vScrollProperties[1]); |
| |
| if ((!verticalScrollBar || !verticalScrollBar.visible) && collection && |
| collection.length - lockedRowCount > rowCount - lockedRowCount) |
| maxVerticalScrollPosition = collection.length - lockedRowCount - (rowCount - lockedRowCount); |
| |
| if ((!horizontalScrollBar || !horizontalScrollBar.visible) && |
| displayableColumns.length - lockedColumnCount > colCount - lockedColumnCount) |
| maxHorizontalScrollPosition = displayableColumns.length - lockedColumnCount - (colCount - lockedColumnCount); |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function scrollVertically(pos:int, deltaPos:int, scrollUp:Boolean):void |
| { |
| // temporarily shift the cursor index to first movable row. |
| iterator.seek(CursorBookmark.CURRENT, lockedRowCount); |
| |
| super.scrollVertically(pos, deltaPos, scrollUp); |
| |
| // move the cursor back to actual first row. |
| iterator.seek(CursorBookmark.CURRENT, - lockedRowCount); |
| } |
| |
| /** |
| * @private |
| */ |
| override public function calculateDropIndex(event:DragEvent = null):int |
| { |
| if (event) |
| { |
| var item:IListItemRenderer; |
| var pt:Point = new Point(event.localX, event.localY); |
| pt = DisplayObject(event.target).localToGlobal(pt); |
| pt = listContent.globalToLocal(pt); |
| |
| var n:int = listItems.length; |
| for (var i:int = 0; i < n; i++) |
| { |
| if (rowInfo[i].y <= pt.y && pt.y <= rowInfo[i].y + rowInfo[i].height) |
| { |
| item = listItems[i][0]; |
| break; |
| } |
| } |
| |
| if (item) |
| lastDropIndex = itemRendererToIndex(item); |
| else |
| lastDropIndex = collection ? collection.length : 0; |
| } |
| |
| return lastDropIndex; |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function calculateDropIndicatorY(rowCount:Number, rowNum:int):Number |
| { |
| var i:int; |
| // we need to take care of headerHeight |
| var yy:Number = headerVisible ? headerHeight : 0; |
| |
| if (rowCount && listItems[rowNum].length && listItems[rowNum][0]) |
| { |
| return listItems[rowNum][0].y - 1 |
| } |
| |
| for (i = 0; i < rowCount; i++) |
| { |
| if (listItems[i].length) |
| yy += rowInfo[i].height; |
| else |
| break; |
| } |
| return yy; |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function drawRowBackgrounds():void |
| { |
| var rowBGs:Sprite = Sprite(listContent.getChildByName("rowBGs")); |
| if (!rowBGs) |
| { |
| rowBGs = new FlexSprite(); |
| rowBGs.mouseEnabled = false; |
| rowBGs.name = "rowBGs"; |
| listContent.addChildAt(rowBGs, 0); |
| } |
| |
| var colors:Array; |
| var colorsStyle:Object = getStyle("alternatingItemColors"); |
| |
| if (colorsStyle) |
| colors = (colorsStyle is Array) ? (colorsStyle as Array) : [colorsStyle]; |
| |
| if (!colors || colors.length == 0) |
| return; |
| |
| styleManager.getColorNames(colors); |
| |
| var curRow:int = 0; |
| |
| var i:int = 0; |
| var actualRow:int = verticalScrollPosition; |
| var actualLockedRow:int = 0; |
| var n:int = listItems.length; |
| |
| // for Locked rows |
| while (curRow < lockedRowCount && curRow < n) |
| { |
| drawRowBackground(rowBGs, i++, rowInfo[curRow].y, rowInfo[curRow].height, colors[actualLockedRow % colors.length], actualLockedRow); |
| curRow++; |
| actualLockedRow++; |
| actualRow++; |
| } |
| |
| // for unlocked rows |
| while (curRow < n) |
| { |
| drawRowBackground(rowBGs, i++, rowInfo[curRow].y, rowInfo[curRow].height, colors[actualRow % colors.length], actualRow); |
| curRow++; |
| actualRow++; |
| } |
| |
| while (rowBGs.numChildren > i) |
| { |
| rowBGs.removeChildAt(rowBGs.numChildren - 1); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function mouseEventToItemRenderer(event:MouseEvent):IListItemRenderer |
| { |
| var r:IListItemRenderer; |
| |
| if (event.target == highlightIndicator || event.target == listContent) |
| { |
| var pt:Point = new Point(event.stageX, event.stageY); |
| pt = listContent.globalToLocal(pt); |
| |
| var ww:Number = 0; |
| |
| // For headerItems |
| // headerItems are created even if showHeader is false |
| // dont look for header renderers if headerVisible is false |
| if (headerVisible) |
| r = findHeaderRenderer(pt); |
| |
| // For listItems |
| // if ADG is empty then length of rowInfo is 0 |
| if (!r && rowInfo.length !=0) |
| r = findRenderer(pt,listItems,rowInfo,rowInfo[0].y); |
| } |
| |
| if (!r) |
| r = super.mouseEventToItemRenderer(event); |
| |
| return r == itemEditorInstance ? null : r; |
| } |
| |
| /** |
| * @private |
| */ |
| override public function styleChanged(styleProp:String):void |
| { |
| super.styleChanged(styleProp); |
| |
| var changed:Boolean = false; |
| |
| if (styleProp == "headerBackgroundSkin") |
| { |
| changed = true; |
| headerBGSkinChanged = true; |
| } |
| else if (styleProp == "headerSortSeparatorSkin") |
| { |
| changed = true; |
| } |
| else if (styleProp == "headerSeparatorSkin") |
| { |
| headerSepSkinChanged = true; |
| changed = true; |
| } |
| |
| if (changed) |
| { |
| itemsSizeChanged = true; |
| } |
| } |
| |
| /** |
| * @private |
| * handle header selection |
| */ |
| override protected function selectItem(item:IListItemRenderer, |
| shiftKey:Boolean, ctrlKey:Boolean, |
| transition:Boolean = true):Boolean |
| { |
| var val:Boolean = super.selectItem(item, shiftKey, ctrlKey, transition); |
| |
| // if item.data is AdvancedDataGridColumn, it means that a header is selected |
| // selectedItem should be null |
| if (item.data is AdvancedDataGridColumn) |
| _selectedItem = null; |
| |
| return val; |
| } |
| |
| /** |
| * @private |
| * handle header selection |
| */ |
| override mx_internal function addSelectionData(uid:String, selectionData:ListBaseSelectionData):void |
| { |
| // if data is AdvancedDataGridColumn, it means that a header is selected |
| // it should not be added into the list |
| if (selectionData.data is AdvancedDataGridColumn) |
| return ; |
| super.addSelectionData(uid, selectionData); |
| } |
| |
| /** |
| * @private |
| * used by ListBase.findString. Shouldn't be used elsewhere |
| * because column's itemToLabel is preferred |
| */ |
| override public function itemToLabel(data:Object):String |
| { |
| var column:AdvancedDataGridColumn = displayableColumns[sortIndex == -1 ? 0 : sortIndex]; |
| return column ? column.itemToLabel(data) : ""; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * When column width changes or horizontal scrolling happens |
| * we need to adjust the sub content area. |
| */ |
| private function updateSubContent():void |
| { |
| if(!visibleColumns || getOptimumColumns() == visibleColumns) |
| { |
| listSubContent.scrollRect = null; |
| listSubContent.x = 0; |
| return ; |
| } |
| |
| var lockedWidth:Number = 0; |
| for(var i:int = 0; i < lockedColumnCount; ++i) |
| { |
| lockedWidth += displayableColumns[i].width; |
| } |
| |
| var scrollWidth:Number = 0; |
| if(visibleColumns.length > lockedColumnCount) |
| { |
| for(i = lockedColumnCount; i < lockedColumnCount+horizontalScrollPosition; ++i) |
| { |
| scrollWidth += displayableColumns[i].width; |
| } |
| } |
| if (horizontalScrollPosition == 0) |
| { |
| //tmpMask.x = 0; |
| listSubContent.scrollRect = null; |
| listSubContent.x = 0; |
| } |
| else |
| { |
| if (lockedColumnCount > 0) |
| listSubContent.x = lockedWidth; |
| else |
| listSubContent.x = 0; |
| //tmpMask.x = lockedWidth; |
| if (lockedWidth > 0) |
| listSubContent.scrollRect = new Rectangle(lockedWidth+scrollWidth, 0, |
| listContent.width - lockedWidth, listContent.height); |
| else |
| listSubContent.scrollRect = new Rectangle(scrollWidth, 0, |
| listContent.width, listContent.height); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| protected function updateVisibleHeaders():Array |
| { |
| var visibleHeaderInfos:Array = []; |
| |
| var n:int = headerInfos ? headerInfos.length : 0; |
| var i:int; |
| var k:int= 0; |
| |
| for ( i = 0; i < n; i++) |
| { |
| headerInfos[i].visible = headerInfos[i].column.visible; |
| if(headerInfos[i].visible) |
| { |
| visibleHeaderInfos.push(headerInfos[i]); |
| headerInfos[i].actualColNum = k++; |
| headerInfos[i].columnSpan = 1; |
| } |
| else |
| { |
| headerInfos[i].actualColNum = NaN; |
| } |
| } |
| return visibleHeaderInfos; |
| } |
| |
| /** |
| * @private |
| */ |
| protected function updateHeaderSearchList():void |
| { |
| var n:int = visibleHeaderInfos? visibleHeaderInfos.length : 0; |
| |
| orderedHeadersList = []; |
| for (var i:int = 0; i < n; i++) |
| { |
| orderedHeadersList.push(visibleHeaderInfos[i]); |
| } |
| } |
| |
| /** |
| * @private |
| * |
| * Note columns may not have been committed at this point. |
| */ |
| protected function initializeHeaderInfo(columns:Array):Array |
| { |
| var result:Array = []; |
| var n:int = columns.length; |
| for(var i:int = 0; i < n; i++) |
| { |
| result.push(new AdvancedDataGridHeaderInfo(columns[i], null, i, 0)); |
| } |
| return result; |
| } |
| |
| /** |
| * Get the length of the header items |
| * |
| * @private |
| */ |
| protected function getHeaderItemsLength():int |
| { |
| return headerItems.length; |
| } |
| |
| /** |
| * @private |
| */ |
| mx_internal function getMeasuringRenderer(c:AdvancedDataGridColumn, forHeader:Boolean, data:Object):IListItemRenderer |
| { |
| var factory:IFactory = columnItemRendererFactory(c,forHeader,data); |
| |
| if (!measuringObjects) |
| measuringObjects = new Dictionary(false); |
| |
| var item:IListItemRenderer = measuringObjects[factory]; |
| if (!item) |
| { |
| item = columnItemRenderer(c, forHeader, data); |
| item.visible = false; |
| item.styleName = c; |
| listContent.addChild(DisplayObject(item)); |
| measuringObjects[factory] = item; |
| } |
| |
| return item; |
| } |
| |
| mx_internal function setupRendererFromData(c:AdvancedDataGridColumn, item:IListItemRenderer, data:Object):void |
| { |
| var rowData:AdvancedDataGridListData = |
| AdvancedDataGridListData(makeListData(data, itemToUID(data), 0, c.colNum, c)); |
| |
| if (item is IDropInListItemRenderer) |
| { |
| if (data != null) |
| IDropInListItemRenderer(item).listData = makeListData(data, itemToUID(data), 0 /* rowNum */, c.colNum, c); |
| else |
| IDropInListItemRenderer(item).listData = null; |
| } |
| |
| item.data = data; |
| |
| if (item is IInvalidating) |
| IInvalidating(item).invalidateSize(); |
| |
| item.explicitWidth = getWidthOfItem(item, c, currentColNum); |
| |
| UIComponentGlobals.layoutManager.validateClient(item, true); |
| } |
| |
| /** |
| * @private |
| */ |
| mx_internal function measureHeightOfItemsUptoMaxHeight(index:int = -1, count:int = 0, maxHeight:Number = -1):Number |
| { |
| if (!_columns.length) |
| return rowHeight * count; |
| |
| var h:Number = 0; |
| |
| var item:IListItemRenderer; |
| var c:AdvancedDataGridColumn; |
| var ch:Number = 0; |
| var n:int; |
| var j:int; |
| |
| var paddingTop:Number = getStyle("paddingTop"); |
| var paddingBottom:Number = getStyle("paddingBottom"); |
| |
| if (!measuringObjects) |
| measuringObjects = new Dictionary(false); |
| |
| var lockedCount:int = lockedRowCount; |
| |
| if (headerVisible && count > 0 && index == -1) |
| { |
| h = calculateHeaderHeight(); |
| |
| if (maxHeight != -1 && h > maxHeight) |
| { |
| setRowCount(0); |
| return 0; |
| } |
| |
| // trace(this + " header preferredHeight = " + h); |
| } |
| |
| var bookmark:CursorBookmark = (iterator) ? iterator.bookmark : null; |
| |
| var bMore:Boolean = iterator != null; |
| if (index != -1 && iterator) |
| { |
| try |
| { |
| iterator.seek(CursorBookmark.FIRST, index); |
| } |
| catch (e:ItemPendingError) |
| { |
| bMore = false; |
| } |
| } |
| |
| if (lockedCount > 0 && collectionIterator) |
| { |
| try |
| { |
| collectionIterator.seek(CursorBookmark.FIRST,0); |
| } |
| catch (e:ItemPendingError) |
| { |
| bMore = false; |
| } |
| } |
| |
| for (var i:int = 0; i < count; i++) |
| { |
| var data:Object; |
| if (bMore) |
| { |
| data = (lockedCount > 0) ? collectionIterator.current : iterator.current; |
| ch = 0; |
| n = _columns.length; |
| for (j = 0; j < n; j++) |
| { |
| c = _columns[j]; |
| |
| if (!c.visible) |
| continue; |
| |
| item = getMeasuringRenderer(c, false,data); |
| setupRendererFromData(c, item, data); |
| ch = Math.max(ch, variableRowHeight ? item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop : rowHeight); |
| } |
| } |
| |
| if (maxHeight != -1 && (h + ch > maxHeight || !bMore)) |
| { |
| try |
| { |
| if (iterator) |
| iterator.seek(bookmark, 0); |
| } |
| catch (e:ItemPendingError) |
| { |
| // we don't recover here since we'd only get here if the first seek failed. |
| } |
| count = i; |
| setRowCount(count); |
| return h; |
| } |
| |
| h += ch; |
| if (iterator) |
| { |
| try |
| { |
| bMore = iterator.moveNext(); |
| if (lockedCount > 0) |
| { |
| collectionIterator.moveNext(); |
| lockedCount--; |
| } |
| } |
| catch (e:ItemPendingError) |
| { |
| // if we run out of data, assume all remaining rows are the size of the previous row |
| bMore = false; |
| } |
| } |
| } |
| |
| if (iterator) |
| { |
| try |
| { |
| iterator.seek(bookmark, 0); |
| } |
| catch (e:ItemPendingError) |
| { |
| // we don't recover here since we'd only get here if the first seek failed. |
| } |
| } |
| |
| // trace("calcheight = " + h); |
| return h; |
| } |
| |
| /** |
| * @private |
| */ |
| protected function calculateHeaderHeight():Number |
| { |
| if (!_columns.length) |
| return rowHeight; |
| |
| var item:IListItemRenderer; |
| var c:AdvancedDataGridColumn; |
| var rowData:AdvancedDataGridListData; |
| var ch:Number = 0; |
| var n:int; |
| var j:int; |
| |
| var paddingTop:Number = getStyle("paddingTop"); |
| var paddingBottom:Number = getStyle("paddingBottom"); |
| |
| if (!measuringObjects) |
| measuringObjects = new Dictionary(false); |
| |
| if (headerVisible) |
| { |
| ch = 0; |
| n = _columns.length; |
| |
| if (_headerWordWrapPresent) |
| { |
| _headerHeight = _originalHeaderHeight; |
| _explicitHeaderHeight = _originalExplicitHeaderHeight; |
| } |
| |
| for (j = 0; j < n; j++) |
| { |
| c = _columns[j]; |
| |
| if (!c.visible) |
| continue; |
| |
| // passing data as null, as it is used for header renderer |
| item = getMeasuringRenderer(c, true, null); |
| rowData = AdvancedDataGridListData(makeListData(c, uid, 0, c.colNum, c)); |
| rowMap[item.name] = rowData; |
| if (item is IDropInListItemRenderer) |
| IDropInListItemRenderer(item).listData = rowData; |
| item.data = c; |
| item.explicitWidth = c.width; |
| UIComponentGlobals.layoutManager.validateClient(item, true); |
| ch = Math.max(ch, _explicitHeaderHeight ? headerHeight : item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop); |
| |
| if (columnHeaderWordWrap(c)) |
| _headerWordWrapPresent = true; |
| } |
| |
| if (_headerWordWrapPresent) |
| { |
| // take backups |
| _originalHeaderHeight = _headerHeight; |
| _originalExplicitHeaderHeight = _explicitHeaderHeight; |
| |
| headerHeight = ch; |
| } |
| } |
| return ch; |
| } |
| |
| /** |
| * @private |
| */ |
| protected function getAdjustedXPos(posx:int):int |
| { |
| if (listSubContent.scrollRect) |
| { |
| if (listSubContent.x == 0) |
| posx -= listSubContent.scrollRect.x; |
| else |
| posx -= (listSubContent.scrollRect.x - listSubContent.x); |
| } |
| return posx; |
| } |
| |
| /** |
| * @private |
| */ |
| mx_internal function getHeaderInfo(col:AdvancedDataGridColumn):AdvancedDataGridHeaderInfo |
| { |
| return headerInfos[col.colNum]; |
| } |
| |
| /** |
| * @private |
| */ |
| mx_internal function getHeaderInfoAt(colIndex:int):AdvancedDataGridHeaderInfo |
| { |
| if(headerInfos) |
| return headerInfos[colIndex]; |
| return null; |
| } |
| |
| /** |
| * @private |
| */ |
| protected function getNumColumns():int |
| { |
| if(headerItems && headerItems[0]) |
| return headerItems[0].length; |
| return -1; |
| } |
| |
| /** |
| * @private |
| * Makes verticalScrollPosition smaller until it is 0 or there |
| * are no empty rows. This is needed if we're scrolled to the |
| * bottom and something is deleted or the rows resize so more |
| * rows can be shown. |
| */ |
| private function adjustVerticalScrollPositionDownward(rowCount:int):Boolean |
| { |
| var bookmark:CursorBookmark = iterator.bookmark; |
| |
| // add up how much space we're currently taking with valid items |
| var h:Number = 0; |
| |
| var item:IListItemRenderer; |
| var c:AdvancedDataGridColumn; |
| var ch:Number = 0; |
| var n:int; |
| var j:int; |
| |
| var paddingTop:Number = getStyle("paddingTop"); |
| var paddingBottom:Number = getStyle("paddingBottom"); |
| |
| h = rowInfo[rowCount - 1].y + rowInfo[rowCount - 1].height; |
| h = listContent.height - h; |
| |
| // back up one |
| var numRows:int = 0; |
| try |
| { |
| if (iterator.afterLast) |
| iterator.seek(CursorBookmark.LAST, 0) |
| else |
| var bMore:Boolean = iterator.movePrevious(); |
| } |
| catch (e:ItemPendingError) |
| { |
| bMore = false; |
| } |
| if (!bMore) |
| { |
| // reset to 0; |
| super.verticalScrollPosition = 0; |
| try |
| { |
| iterator.seek(CursorBookmark.FIRST, 0); |
| // sometimes, if the iterator is invalid we'll get lucky and succeed |
| // here, then we have to make the iterator valid again |
| if (!iteratorValid) |
| { |
| iteratorValid = true; |
| lastSeekPending = null; |
| } |
| } |
| catch (e:ItemPendingError) |
| { |
| lastSeekPending = new ListBaseSeekPending(CursorBookmark.FIRST, 0); |
| e.addResponder(new ItemResponder(seekPendingResultHandler, seekPendingFailureHandler, |
| lastSeekPending)); |
| iteratorValid = false; |
| invalidateList(); |
| return true; |
| } |
| updateList(); |
| return true; |
| } |
| |
| // now work backwards to see how many more rows we need to create |
| while (h > 0 && bMore) |
| { |
| var data:Object; |
| if (bMore) |
| { |
| data = iterator.current; |
| ch = 0; |
| n = _columns.length; |
| for (j = 0; j < n; j++) |
| { |
| c = _columns[j]; |
| |
| if (!c.visible) |
| continue; |
| |
| if (variableRowHeight) |
| { |
| item = getMeasuringRenderer(c, false,data); |
| setupRendererFromData(c, item, data); |
| } |
| ch = Math.max(ch, variableRowHeight ? item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop : rowHeight); |
| } |
| } |
| h -= ch; |
| try |
| { |
| bMore = iterator.movePrevious(); |
| numRows++; |
| } |
| catch (e:ItemPendingError) |
| { |
| // if we run out of data, assume all remaining rows are the size of the previous row |
| bMore = false; |
| } |
| } |
| // if we overrun, go back one. |
| if (h < 0) |
| { |
| numRows--; |
| } |
| |
| iterator.seek(bookmark, 0); |
| verticalScrollPosition = Math.max(0, verticalScrollPosition - numRows); |
| |
| // make sure we get through configureScrollBars w/o coming in here. |
| if (numRows > 0 && !variableRowHeight) |
| configureScrollBars(); |
| |
| return (numRows > 0); |
| } |
| |
| /** |
| * @private |
| * Move a column to a new position in the columns array, shifting all |
| * other columns left or right and updating the sortIndex and |
| * lastSortIndex variables accordingly. |
| */ |
| mx_internal function shiftColumns(oldIndex:int, newIndex:int, |
| trigger:Event = null):void |
| { |
| var groupInfos:Array = headerInfos; //getPossibleDropPositions(movingColumn); |
| if (newIndex >= 0 && oldIndex != newIndex) |
| { |
| var incr:int = oldIndex < newIndex ? 1 : -1; |
| for (var i:int = oldIndex; i != newIndex; i += incr) |
| { |
| var j:int = i + incr; |
| var c:AdvancedDataGridColumn = _columns[i]; |
| _columns[i] = _columns[j]; |
| _columns[j] = c; |
| _columns[i].colNum = i; |
| _columns[j].colNum = j; |
| |
| var cInfo:AdvancedDataGridHeaderInfo = groupInfos[i]; |
| groupInfos[i] = groupInfos[j]; |
| groupInfos[j] = cInfo; |
| groupInfos[i].index -=incr; |
| groupInfos[j].index += incr; |
| } |
| |
| if (sortIndex == oldIndex) |
| sortIndex += newIndex - oldIndex; |
| else if ((oldIndex < sortIndex && sortIndex <= newIndex) |
| || (newIndex <= sortIndex && sortIndex < oldIndex)) |
| sortIndex -= incr; |
| |
| if (lastSortIndex == oldIndex) |
| lastSortIndex += newIndex - oldIndex; |
| else if ((oldIndex < lastSortIndex |
| && lastSortIndex <= newIndex) |
| || (newIndex <= lastSortIndex |
| && lastSortIndex < oldIndex)) |
| lastSortIndex -= incr; |
| |
| columnsInvalid = true; |
| itemsSizeChanged = true; |
| |
| visibleHeaderInfos = updateVisibleHeaders(); |
| updateHeaderSearchList(); |
| createDisplayableColumns(); |
| |
| invalidateDisplayList(); |
| var icEvent:IndexChangedEvent = |
| new IndexChangedEvent(IndexChangedEvent.HEADER_SHIFT); |
| icEvent.oldIndex = oldIndex; |
| icEvent.newIndex = newIndex; |
| icEvent.triggerEvent = trigger; |
| dispatchEvent(icEvent); |
| } |
| } |
| |
| /** |
| * @private |
| * Searches the iterator to determine columns. |
| */ |
| private function generateCols():void |
| { |
| if (collection.length > 0) |
| { |
| var col:AdvancedDataGridColumn; |
| var newCols:Array = []; |
| var cols:Array; |
| if (dataProvider) |
| { |
| try |
| { |
| iterator.seek(CursorBookmark.FIRST); |
| } |
| catch (e:ItemPendingError) |
| { |
| lastSeekPending = new ListBaseSeekPending(CursorBookmark.FIRST, 0); |
| e.addResponder(new ItemResponder(generateColumnsPendingResultHandler, seekPendingFailureHandler, |
| lastSeekPending)); |
| iteratorValid = false; |
| return; |
| } |
| var info:Object = |
| ObjectUtil.getClassInfo(iterator.current, |
| ["uid", "mx_internal_uid"]); |
| |
| if(info) |
| cols = info.properties; |
| } |
| |
| if (!cols) |
| { |
| // introspect the first item and use its fields |
| var itmObj:Object = iterator.current; |
| for (var p:String in itmObj) |
| { |
| if (p != "uid") |
| { |
| col = new AdvancedDataGridColumn(); |
| col.dataField = p; |
| newCols.push(col); |
| } |
| } |
| } |
| else |
| { |
| // this is an old recordset - use its columns |
| var n:int = cols.length; |
| var colName:Object; |
| for (var i:int = 0; i < n; i++) |
| { |
| colName = cols[i]; |
| if (colName is QName) |
| colName = QName(colName).localName; |
| col = new AdvancedDataGridColumn(); |
| col.dataField = String(colName); |
| newCols.push(col); |
| } |
| } |
| columns = newCols; |
| generatedColumns = true; |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| private function generateColumnsPendingResultHandler(data:Object, info:ListBaseSeekPending):void |
| { |
| // generate cols if we haven't successfully generated them |
| if (_columns.length == 0) |
| generateCols(); |
| seekPendingResultHandler(data, info); |
| } |
| |
| /** |
| * @private |
| */ |
| protected function createDisplayableColumns():void |
| { |
| var i:int; |
| var n:int; |
| |
| displayableColumns = null; |
| n = _columns.length; |
| for (i = 0; i < n; i++) |
| { |
| if (displayableColumns && _columns[i].visible) |
| { |
| displayableColumns.push(_columns[i]); |
| } |
| else if (!displayableColumns && !_columns[i].visible) |
| { |
| displayableColumns = new Array(i); |
| for (var j:int = 0; j < i; j++) |
| { |
| displayableColumns[j] = _columns[j]; |
| } |
| } |
| } |
| // If there are no hidden columns, displayableColumns points to |
| // _columns (we don't need a duplicate copy of _columns). |
| if (!displayableColumns) |
| displayableColumns = _columns; |
| } |
| |
| /** |
| * @private |
| */ |
| private function calculateColumnSizes():void |
| { |
| var delta:Number; |
| var n:int; |
| var i:int; |
| var totalWidth:Number = 0; |
| var col:AdvancedDataGridColumn; |
| var cw:Number; |
| |
| if (_columns.length == 0) |
| { |
| visibleColumns = []; |
| return; |
| } |
| |
| // no columns are visible so figure out which ones |
| // to make visible |
| if (columnsInvalid) |
| { |
| columnsInvalid = false; |
| visibleColumns = []; |
| |
| if (minColumnWidthInvalid) |
| { |
| n = _columns.length; |
| for (i = 0; i < n; i++) |
| { |
| _columns[i].minWidth = minColumnWidth; |
| } |
| minColumnWidthInvalid = false; |
| } |
| |
| // if no hscroll, then pack columns in available space |
| if (horizontalScrollPolicy == ScrollPolicy.OFF) |
| { |
| n = displayableColumns.length; |
| for (i = 0; i < n; i++) |
| { |
| visibleColumns.push(displayableColumns[i]); |
| } |
| } |
| else |
| { |
| n = displayableColumns.length; |
| for (i = 0; i < n; i++) |
| { |
| if (i >= lockedColumnCount && |
| i < lockedColumnCount + horizontalScrollPosition) |
| { |
| continue; |
| } |
| |
| col = displayableColumns[i]; |
| if (col.preferredWidth < col.minWidth) |
| col.preferredWidth = col.minWidth; |
| |
| if (totalWidth < displayWidth) |
| { |
| visibleColumns.push(col); |
| totalWidth += isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth; |
| if (col.width != col.preferredWidth) |
| col.setWidth(col.preferredWidth); |
| } |
| else |
| { |
| if (visibleColumns.length == 0) |
| visibleColumns.push(displayableColumns[0]); |
| break; |
| } |
| } |
| } |
| } |
| |
| var lastColumn:AdvancedDataGridColumn; |
| var newSize:Number; |
| |
| // if no hscroll, then pack columns in available space |
| if (horizontalScrollPolicy == ScrollPolicy.OFF) |
| { |
| var numResizable:int = 0; |
| var fixedWidth:Number = 0; |
| |
| // trace("resizing columns"); |
| |
| // count how many resizable columns and how wide they are |
| n = visibleColumns.length; |
| for (i = 0; i < n; i++) |
| { |
| // trace("column " + i + " width = " + visibleColumns[i].width); |
| if (visibleColumns[i].resizable) |
| { |
| // trace(" resizable"); |
| if (!isNaN(visibleColumns[i].explicitWidth)) |
| { |
| // trace(" explicit width " + visibleColumns[i].width); |
| fixedWidth += visibleColumns[i].width; |
| } |
| else |
| { |
| // trace(" implicitly resizable"); |
| numResizable++; |
| fixedWidth += visibleColumns[i].minWidth; |
| // trace(" minWidth " + visibleColumns[i].minWidth); |
| } |
| } |
| else |
| { |
| // trace(" not resizable"); |
| fixedWidth += visibleColumns[i].width; |
| } |
| |
| totalWidth += visibleColumns[i].width; |
| } |
| // trace("totalWidth = " + totalWidth); |
| // trace("displayWidth = " + displayWidth); |
| |
| var ratio:Number; |
| var newTotal:Number = displayWidth; |
| var minWidth:Number; |
| if (displayWidth > fixedWidth && numResizable) |
| { |
| // we have flexible columns and room to honor minwidths and non-resizable |
| // trace("have enough room"); |
| |
| // divide and distribute the excess among the resizable |
| n = visibleColumns.length; |
| for (i = 0; i < n; i++) |
| { |
| if (visibleColumns[i].resizable && isNaN(visibleColumns[i].explicitWidth)) |
| { |
| lastColumn = visibleColumns[i]; |
| if (totalWidth > displayWidth) |
| ratio = (lastColumn.width - lastColumn.minWidth)/ (totalWidth - fixedWidth); |
| else |
| ratio = lastColumn.width / totalWidth; |
| newSize = lastColumn.width - (totalWidth - displayWidth) * ratio; |
| minWidth = visibleColumns[i].minWidth; |
| visibleColumns[i].setWidth(newSize > minWidth ? newSize : minWidth); |
| // trace("column " + i + " set to " + visibleColumns[i].width); |
| } |
| newTotal -= visibleColumns[i].width; |
| } |
| if (newTotal && lastColumn) |
| { |
| // trace("excess = " + newTotal); |
| lastColumn.setWidth(lastColumn.width + newTotal); |
| } |
| } |
| else // can't honor minwidth and non-resizables so just scale everybody |
| { |
| // trace("too small or too big"); |
| n = visibleColumns.length; |
| for (i = 0; i < n; i++) |
| { |
| lastColumn = visibleColumns[i]; |
| ratio = lastColumn.width / totalWidth; |
| //totalWidth -= visibleColumns[i].width; |
| newSize = displayWidth * ratio; |
| lastColumn.setWidth(newSize); |
| lastColumn.explicitWidth = NaN; |
| // trace("column " + i + " set to " + visibleColumns[i].width); |
| newTotal -= newSize; |
| } |
| if (newTotal && lastColumn) |
| { |
| // trace("excess = " + newTotal); |
| lastColumn.setWidth(lastColumn.width + newTotal); |
| } |
| } |
| } |
| else // we have or can have an horizontalScrollBar |
| { |
| totalWidth = 0; |
| // drop any that completely overflow |
| n = visibleColumns.length; |
| for (i = 0; i < n; i++) |
| { |
| if (totalWidth > displayWidth) |
| { |
| visibleColumns.splice(i); |
| break; |
| } |
| totalWidth += isNaN(visibleColumns[i].explicitWidth) ? visibleColumns[i].preferredWidth : visibleColumns[i].explicitWidth; |
| } |
| |
| if (visibleColumns.length == 0) |
| return; |
| |
| i = visibleColumns[visibleColumns.length - 1].colNum + 1; |
| // add more if we have room |
| if (totalWidth < displayWidth && i < displayableColumns.length) |
| { |
| n = displayableColumns.length; |
| for (; i < n && totalWidth < displayWidth; i++) |
| { |
| col = displayableColumns[i]; |
| |
| visibleColumns.push(col); |
| totalWidth += isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth; |
| } |
| } |
| else if (totalWidth < displayWidth && horizontalScrollPosition > 0) |
| { |
| while (totalWidth < displayWidth && horizontalScrollPosition > 0) |
| { |
| col = displayableColumns[lockedColumnCount + horizontalScrollPosition - 1]; |
| cw = isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth; |
| if (cw < displayWidth - totalWidth) |
| { |
| visibleColumns.splice(lockedColumnCount, 0, col); |
| super.horizontalScrollPosition--; |
| totalWidth += cw; |
| } |
| else |
| { |
| break; |
| } |
| } |
| } |
| |
| lastColumn = visibleColumns[visibleColumns.length - 1]; |
| cw = isNaN(lastColumn.explicitWidth) ? lastColumn.preferredWidth : lastColumn.explicitWidth; |
| newSize = cw + displayWidth - totalWidth; |
| |
| if (lastColumn == displayableColumns[displayableColumns.length - 1] |
| && lastColumn.resizable |
| && newSize >= lastColumn.minWidth |
| && newSize > cw) |
| { |
| lastColumn.setWidth(newSize); |
| maxHorizontalScrollPosition = |
| displayableColumns.length - visibleColumns.length; |
| } |
| else |
| { |
| if (visibleColumns.length == displayableColumns.length) |
| { |
| // set scrollPosition to zero |
| maxHorizontalScrollPosition = 0; |
| super.horizontalScrollPosition = 0; |
| } |
| else if(lockedColumnCount < visibleColumns.length) |
| { |
| maxHorizontalScrollPosition = |
| displayableColumns.length - visibleColumns.length + 1; |
| } |
| else |
| { |
| maxHorizontalScrollPosition = |
| Math.max(0, displayableColumns.length - lockedColumnCount + 1); |
| super.horizontalScrollPosition = Math.min(horizontalScrollPosition, maxHorizontalScrollPosition); |
| } |
| } |
| } |
| } |
| |
| /** |
| * @private |
| * If there is no horizontal scroll bar, changes the display width of other columns when |
| * one column's width is changed. |
| * @param col column whose width is changed |
| * @param w width of column |
| */ |
| mx_internal function resizeColumn(col:int, w:Number):void |
| { |
| // there's a window of time before we calccolumnsizes |
| // that someone can set width in AS |
| if (!visibleColumns || visibleColumns.length == 0) |
| { |
| _columns[col].setWidth(w); |
| _columns[col].preferredWidth = w; |
| return; |
| } |
| |
| if (w < _columns[col].minWidth) |
| w = _columns[col].minWidth; |
| |
| // hScrollBar is present |
| if (_horizontalScrollPolicy == ScrollPolicy.ON || |
| _horizontalScrollPolicy == ScrollPolicy.AUTO) |
| { |
| // adjust the column's width |
| _columns[col].setWidth(w); |
| _columns[col].explicitWidth = w; |
| _columns[col].preferredWidth = w; |
| columnsInvalid = true; |
| } |
| else |
| { |
| // find the columns in the set of visible columns; |
| var n:int = visibleColumns.length; |
| var i:int; |
| for (i = 0; i < n; i++) |
| { |
| if (col == visibleColumns[i].colNum) |
| break; |
| } |
| if (i >= visibleColumns.length) |
| return; |
| col = i; |
| |
| // we want all cols's new widths to the right of this to be in proportion |
| // to what they were before the stretch. |
| |
| // get the original space to the right not taken up by the column |
| var totalSpace:Number = 0; |
| var lastColumn:AdvancedDataGridColumn; |
| var newWidth:Number; |
| //non-resizable columns don't count though |
| var optimumColumns:Array = getOptimumColumns(); |
| for (i = col + 1; i < n; i++) |
| { |
| if (optimumColumns[i].resizable) |
| totalSpace += visibleColumns[i].width; |
| } |
| |
| var newTotalSpace:Number = optimumColumns[col].width - w + totalSpace; |
| if (totalSpace) |
| { |
| optimumColumns[col].setWidth(w); |
| optimumColumns[col].explicitWidth = w; |
| } |
| |
| var totX:Number = 0; |
| // resize the columns to the right proportionally to what they were |
| for (i = col + 1; i < n; i++) |
| { |
| if (optimumColumns[i].resizable) |
| { |
| newWidth = Math.floor(visibleColumns[i].width |
| * newTotalSpace / totalSpace); |
| if (newWidth < visibleColumns[i].minWidth) |
| newWidth = visibleColumns[i].minWidth; |
| optimumColumns[i].setWidth(newWidth); |
| totX += optimumColumns[i].width; |
| lastColumn = optimumColumns[i]; |
| } |
| } |
| |
| if (totX > newTotalSpace) |
| { |
| // if excess then should be taken out only from changing column |
| // cause others would have already gone to their minimum |
| newWidth = optimumColumns[col].width - totX + newTotalSpace; |
| if (newWidth < optimumColumns[col].minWidth) |
| newWidth = optimumColumns[col].minWidth; |
| optimumColumns[col].setWidth(newWidth); |
| } |
| else if (lastColumn) |
| { |
| // if less then should be added in last column |
| // dont need to check for minWidth as we are adding |
| lastColumn.setWidth(lastColumn.width - totX + newTotalSpace); |
| } |
| } |
| itemsSizeChanged = true; |
| |
| updateSubContent(); |
| |
| invalidateDisplayList(); |
| } |
| |
| /** |
| * Draws the background of the headers into the given |
| * UIComponent. The graphics drawn can be scaled horizontally |
| * if the component's width changes, or this method will be |
| * called again to redraw at a different width and/or height |
| * |
| * @param headerBG A UIComponent that will contain the header |
| * background graphics. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function drawHeaderBackground(headerBG:UIComponent):void |
| { |
| var tot:Number = displayWidth; |
| |
| // If we have vScroll only, extend the header over the scrollbar |
| if (verticalScrollBar != null && |
| _horizontalScrollPolicy == ScrollPolicy.OFF && |
| headerVisible) |
| { |
| var bm:EdgeMetrics = borderMetrics; |
| var adjustedWidth:Number = unscaledWidth - (bm.left + bm.right); |
| tot = adjustedWidth; |
| // Need to extend mask too. |
| maskShape.width = adjustedWidth; |
| } |
| |
| var hh:Number = headerRowInfo.length ? headerRowInfo[0].height : headerHeight; |
| |
| var g:Graphics = headerBG.graphics; |
| g.clear(); |
| var colors:Array = getStyle("headerColors"); |
| styleManager.getColorNames(colors); |
| |
| var matrix:Matrix = new Matrix(); |
| matrix.createGradientBox(tot, hh + 1, Math.PI/2, 0, 0); |
| |
| colors = [ colors[0], colors[0], colors[1] ]; |
| var ratios:Array = [ 0, 60, 255 ]; |
| var alphas:Array = [ 1.0, 1.0, 1.0 ]; |
| |
| g.beginGradientFill(GradientType.LINEAR, colors, alphas, ratios, matrix); |
| g.lineStyle(0, 0x000000, 0); |
| g.moveTo(0, 0); |
| g.lineTo(tot, 0); |
| g.lineTo(tot, hh - 0.5); |
| g.lineStyle(0, getStyle("borderColor"), 100); |
| g.lineTo(0, hh - 0.5); |
| g.lineStyle(0, 0x000000, 0); |
| g.endFill(); |
| } |
| |
| private function drawHeaderBackgroundSkin(headerBGSkin:IFlexDisplayObject):void |
| { |
| var tot:Number = displayWidth; |
| |
| // If we have vScroll only, extend the header over the scrollbar |
| if (verticalScrollBar != null && |
| _horizontalScrollPolicy == ScrollPolicy.OFF && |
| headerVisible) |
| { |
| var bm:EdgeMetrics = borderMetrics; |
| var adjustedWidth:Number = unscaledWidth - (bm.left + bm.right); |
| tot = adjustedWidth; |
| // Need to extend mask too. |
| maskShape.width = adjustedWidth; |
| } |
| |
| var hh:Number = headerRowInfo.length ? headerRowInfo[0].height : headerHeight; |
| headerBGSkin.setActualSize(tot,hh); |
| } |
| |
| /** |
| * Draws a row background |
| * at the position and height specified using the |
| * color specified. This implementation creates a Shape as a |
| * child of the input Sprite and fills it with the appropriate color. |
| * This method also uses the <code>backgroundAlpha</code> style property |
| * setting to determine the transparency of the background color. |
| * |
| * @param s A Sprite that will contain a display object |
| * that contains the graphics for that row. |
| * |
| * @param rowIndex The row's index in the set of displayed rows. The |
| * header does not count, the top most visible row has a row index of 0. |
| * This is used to keep track of the objects used for drawing |
| * backgrounds so a particular row can re-use the same display object |
| * even though the index of the item that row is rendering has changed. |
| * |
| * @param y The suggested y position for the background. |
| * |
| * @param height The suggested height for the indicator. |
| * |
| * @param color The suggested color for the indicator. |
| * |
| * @param dataIndex The index of the item for that row in the |
| * data provider. This can be used to color the tenth item differently, |
| * for example. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function drawRowBackground(s:Sprite, rowIndex:int, |
| y:Number, height:Number, color:uint, dataIndex:int):void |
| { |
| var background:Shape; |
| if (rowIndex < s.numChildren) |
| { |
| background = Shape(s.getChildAt(rowIndex)); |
| } |
| else |
| { |
| background = new FlexShape(); |
| background.name = "background"; |
| s.addChild(background); |
| } |
| |
| background.y = y; |
| |
| // Height is usually as tall is the items in the row, but not if |
| // it would extend below the bottom of listContent |
| var minHeight:Number = Math.min(height, |
| listContent.height - |
| y); |
| |
| var g:Graphics = background.graphics; |
| g.clear(); |
| g.beginFill(color, getStyle("backgroundAlpha")); |
| g.drawRect(0, 0, displayWidth, minHeight); |
| g.endFill(); |
| } |
| |
| /** |
| * Draws a column background for a column with the suggested color. |
| * This implementation creates a Shape as a |
| * child of the input Sprite and fills it with the appropriate color. |
| * |
| * @param s A Sprite that will contain a display object |
| * that contains the graphics for that column. |
| * |
| * @param columnIndex The column's index in the set of displayed columns. |
| * The left-most visible column has a column index of 0. |
| * This is used to keep track of the objects used for drawing |
| * backgrounds, so a particular column can re-use the same display object |
| * even though the index of the AdvancedDataGridColumn for that column has changed. |
| * |
| * @param color The suggested color for the indicator. |
| * |
| * @param column The column of the AdvancedDataGrid control that you are drawing the background for. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function drawColumnBackground(s:Sprite, columnIndex:int, |
| color:uint, column:AdvancedDataGridColumn):void |
| { |
| var background:Shape; |
| background = Shape(s.getChildByName(columnIndex.toString())); |
| if (!background) |
| { |
| background = new FlexShape(); |
| s.addChild(background); |
| background.name = columnIndex.toString(); |
| } |
| |
| var g:Graphics = background.graphics; |
| g.clear(); |
| |
| if(columnIndex >= lockedColumnCount && |
| columnIndex < lockedColumnCount + horizontalScrollPosition) |
| return; |
| |
| g.beginFill(color); |
| |
| var lastRow:Object = rowInfo[listItems.length - 1]; |
| var headerInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(getOptimumColumns()[columnIndex]); |
| var xx:Number = headerInfo.headerItem.x; |
| if(columnIndex >= lockedColumnCount) |
| xx = getAdjustedXPos(xx); |
| var yy:Number = headerRowInfo[0].y; |
| |
| if (headerVisible) |
| yy += headerRowInfo[0].height; |
| |
| // Height is usually as tall is the items in the row, but not if |
| // it would extend below the bottom of listContent |
| var height:Number = Math.min(lastRow.y + lastRow.height, |
| listContent.height - yy); |
| |
| g.drawRect(xx, yy, headerInfo.headerItem.width, height); |
| g.endFill(); |
| } |
| |
| /** |
| * Creates and sizes the horizontalSeparator skins. If none have been specified, then draws the lines using |
| * drawHorizontalLine(). |
| * |
| * @private |
| */ |
| private function drawHorizontalSeparator(s:Sprite, rowIndex:int, color:uint, y:Number):void |
| { |
| var useLockedSeparator:Boolean = false; |
| |
| if (lockedRowCount > 0 && rowIndex == lockedRowCount - 1) |
| { |
| useLockedSeparator = true; |
| } |
| |
| var hSepSkinName:String = "hSeparator" + rowIndex; |
| var hLockedSepSkinName:String = "hLockedSeparator" + rowIndex; |
| var createThisSkinName:String = useLockedSeparator ? hLockedSepSkinName : hSepSkinName; |
| var createThisStyleName:String = useLockedSeparator ? "horizontalLockedSeparatorSkin" : "horizontalSeparatorSkin"; |
| |
| var sepSkin:IFlexDisplayObject; |
| var lockedSepSkin:IFlexDisplayObject; |
| var deleteThisSkin:IFlexDisplayObject; |
| var createThisSkin:IFlexDisplayObject; |
| |
| // Look for separator by name |
| sepSkin = IFlexDisplayObject(s.getChildByName(hSepSkinName)); |
| lockedSepSkin = IFlexDisplayObject(s.getChildByName(hLockedSepSkinName)); |
| |
| createThisSkin = useLockedSeparator ? lockedSepSkin : sepSkin; |
| deleteThisSkin = useLockedSeparator ? sepSkin : lockedSepSkin; |
| |
| if (deleteThisSkin) |
| { |
| s.removeChild(DisplayObject(deleteThisSkin)); |
| //delete deleteThisSkin; |
| } |
| |
| if (!createThisSkin) |
| { |
| var sepSkinClass:Class = Class(getStyle(createThisStyleName)); |
| |
| if (sepSkinClass) |
| { |
| createThisSkin = IFlexDisplayObject(new sepSkinClass()); |
| createThisSkin.name = createThisSkinName; |
| |
| var styleableSkin:ISimpleStyleClient = createThisSkin as ISimpleStyleClient; |
| if (styleableSkin) |
| styleableSkin.styleName = this; |
| |
| s.addChild(DisplayObject(createThisSkin)); |
| } |
| } |
| |
| if (createThisSkin) |
| { |
| var mHeight:Number = !isNaN(createThisSkin.measuredHeight) ? createThisSkin.measuredHeight : 1; |
| createThisSkin.setActualSize(displayWidth, mHeight); |
| createThisSkin.move(0, y); |
| } |
| else // If we still don't have a sepSkin, then we have no skin style defined. Use the default function instead |
| { |
| drawHorizontalLine(s, rowIndex, color, y); |
| } |
| |
| } |
| |
| /** |
| * Draws a line between rows. This implementation draws a line |
| * directly into the given Sprite. The Sprite has been cleared |
| * before lines are drawn into it. |
| * |
| * @param s A Sprite that will contain a display object |
| * that contains the graphics for that row. |
| * |
| * @param rowIndex The row's index in the set of displayed rows. The |
| * header does not count; the top-most visible row has a row index of 0. |
| * This is used to keep track of the objects used for drawing |
| * backgrounds so a particular row can re-use the same display object |
| * even though the index of the item that row is rendering has changed. |
| * |
| * @param color The suggested color for the indicator. |
| * |
| * @param y The suggested y position for the background. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function drawHorizontalLine(s:Sprite, rowIndex:int, color:uint, y:Number):void |
| { |
| var g:Graphics = s.graphics; |
| |
| if (lockedRowCount > 0 && rowIndex == lockedRowCount-1) |
| g.lineStyle(1, 0); |
| else |
| g.lineStyle(1, color); |
| |
| g.moveTo(0, y); |
| g.lineTo(displayWidth, y); |
| } |
| |
| /** |
| * Creates and sizes the verticalSeparator skins. If none have been specified, then draws the lines using |
| * drawVerticalLine(). |
| * |
| * @private |
| */ |
| private function drawVerticalSeparator(s:Sprite, colIndex:int, color:uint, x:Number, y:Number):void |
| { |
| var useLockedSeparator:Boolean = false; |
| |
| if (lockedColumnCount > 0 && colIndex == lockedColumnCount-1) |
| { |
| useLockedSeparator = true; |
| } |
| |
| var vSepSkinName:String = "vSeparator" + colIndex; |
| var vLockedSepSkinName:String = "vLockedSeparator" + colIndex; |
| var createThisSkinName:String = useLockedSeparator ? vLockedSepSkinName : vSepSkinName; |
| var createThisStyleName:String = useLockedSeparator ? "verticalLockedSeparatorSkin" : "verticalSeparatorSkin"; |
| |
| var sepSkin:IFlexDisplayObject; |
| var lockedSepSkin:IFlexDisplayObject; |
| var deleteThisSkin:IFlexDisplayObject; |
| var createThisSkin:IFlexDisplayObject; |
| |
| // Look for separator by name |
| sepSkin = IFlexDisplayObject(s.getChildByName(vSepSkinName)); |
| lockedSepSkin = IFlexDisplayObject(s.getChildByName(vLockedSepSkinName)); |
| |
| createThisSkin = useLockedSeparator ? lockedSepSkin : sepSkin; |
| deleteThisSkin = useLockedSeparator ? sepSkin : lockedSepSkin; |
| |
| if (deleteThisSkin) |
| { |
| s.removeChild(DisplayObject(deleteThisSkin)); |
| //delete deleteThisSkin; |
| } |
| |
| if (!createThisSkin) |
| { |
| var sepSkinClass:Class = Class(getStyle(createThisStyleName)); |
| |
| if (sepSkinClass) |
| { |
| createThisSkin = IFlexDisplayObject(new sepSkinClass()); |
| createThisSkin.name = createThisSkinName; |
| |
| var styleableSkin:ISimpleStyleClient = createThisSkin as ISimpleStyleClient; |
| if (styleableSkin) |
| styleableSkin.styleName = this; |
| |
| s.addChild(DisplayObject(createThisSkin)); |
| } |
| } |
| |
| if (createThisSkin) |
| { |
| var mWidth:Number = !isNaN(createThisSkin.measuredWidth) ? createThisSkin.measuredWidth : 1; |
| createThisSkin.setActualSize(mWidth, listContent.height); |
| createThisSkin.move(x, y); |
| } |
| else // If we still don't have a sepSkin, then we have no skin style defined. Use the default function instead |
| { |
| drawVerticalLine(s, colIndex, color, x); |
| } |
| |
| } |
| |
| /** |
| * Draws lines between columns. This implementation draws a line |
| * directly into the given Sprite. The Sprite has been cleared |
| * before lines are drawn into it. |
| * |
| * @param s A Sprite that will contain a display object |
| * that contains the graphics for that row. |
| * |
| * @param columnIndex The column's index in the set of displayed columns. |
| * The left most visible column has a column index of 0. |
| * |
| * @param color The suggested color for the indicator. |
| * |
| * @param x The suggested x position for the background. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function drawVerticalLine(s:Sprite, colIndex:int, color:uint, x:Number):void |
| { |
| //draw our vertical lines |
| var g:Graphics = s.graphics; |
| if (lockedColumnCount > 0 && colIndex == lockedColumnCount - 1) |
| g.lineStyle(1, 0, 100); |
| else |
| g.lineStyle(1, color, 100); |
| |
| var tempY:Number = 0; |
| if(headerVisible) |
| { |
| //In case of lockedColumn line, we start it from the top, so that it comes |
| //in the header area as well |
| if(lockedColumnCount > 0 && colIndex == lockedColumnCount - 1) |
| { |
| g.moveTo(x, 1); |
| g.lineTo(x, headerItems[0][colIndex].height); |
| } |
| else |
| tempY = headerItems[0][colIndex].height; |
| |
| } |
| |
| // draw line from tempY to listContent's height |
| g.moveTo(x, tempY); |
| g.lineTo(x, listContent.height); |
| } |
| |
| /** |
| * Draws lines between columns, and column backgrounds. |
| * This implementation calls the <code>drawHorizontalLine()</code>, |
| * <code>drawVerticalLine()</code>, |
| * and <code>drawColumnBackground()</code> methods as needed. |
| * It creates a |
| * Sprite that contains all of these graphics and adds it as a |
| * child of the <code>listContent</code> at the front of the z-order. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function drawLinesAndColumnBackgrounds():void |
| { |
| var lines:Sprite = getLines(); |
| lines.graphics.clear(); |
| |
| var len:uint = getNumColumns(); |
| len = (len != -1)? len : visibleColumns.length; |
| // defend against degenerate case when width == 0 |
| var optimumColumns:Array = getOptimumColumns(); |
| if (len > optimumColumns.length) |
| len = optimumColumns.length; |
| |
| // draw horizontal lines |
| drawHorizontalSeparators(); |
| |
| // draw vertical lines |
| drawVerticalSeparators(); |
| |
| // draw column backgrounds |
| if (headerInfos && hasHeaderItemsCreated(0) && hasHeaderItemsCreated(len-1)) |
| { |
| var colBGs:Sprite = Sprite(listContent.getChildByName("colBGs")); |
| // traverse the columns, set the sizes, draw the column backgrounds |
| var lastChild:int = -1; |
| for (var i:int = 0; i < len; i++) |
| { |
| var col:AdvancedDataGridColumn = optimumColumns[i]; |
| var bgCol:Object; |
| if (enabled) |
| bgCol = col.getStyle("backgroundColor"); |
| else |
| bgCol = col.getStyle("backgroundDisabledColor"); |
| |
| if (bgCol !== null && !isNaN(Number(bgCol))) |
| { |
| if (!colBGs) |
| { |
| colBGs = new FlexSprite(); |
| colBGs.mouseEnabled = false; |
| colBGs.name = "colBGs"; |
| listContent.addChildAt(colBGs, listContent.getChildIndex(listContent.getChildByName("rowBGs")) + 1); |
| } |
| drawColumnBackground(colBGs, i, Number(bgCol), col); |
| lastChild = i; |
| } |
| else if (colBGs) |
| { |
| var background:Shape = Shape(colBGs.getChildByName(i.toString())); |
| if (background) |
| { |
| var g:Graphics = background.graphics; |
| g.clear(); |
| colBGs.removeChild(background); |
| } |
| } |
| } |
| if (colBGs && colBGs.numChildren) |
| { |
| while (colBGs.numChildren) |
| { |
| var bg:DisplayObject = colBGs.getChildAt(colBGs.numChildren - 1); |
| if (parseInt(bg.name) > lastChild) |
| colBGs.removeChild(bg); |
| else |
| break; |
| } |
| } |
| } |
| } |
| |
| /** |
| * @private |
| * |
| * Call drawHorizontalSeparator() to draw horizontal lines |
| */ |
| protected function drawHorizontalSeparators():void |
| { |
| // draw horizontalGridlines if needed. |
| var lineCol:uint = getStyle("horizontalGridLineColor"); |
| |
| var lockedContent:Sprite = getLockedContent(); |
| |
| // draw vertical lines in the locked column area |
| var lockedLinesBody:Sprite = Sprite(lockedContent.getChildByName("lockedHorizontalLines")); |
| |
| if (lockedLinesBody) |
| { |
| lockedLinesBody.graphics.clear(); |
| |
| while (lockedLinesBody.numChildren) |
| { |
| lockedLinesBody.removeChildAt(0); |
| } |
| } |
| |
| //In case of horizontal lines we don't need to care |
| //about column locking a line from 0-displayWidth will do |
| if (getStyle("horizontalGridLines") |
| || lockedRowCount > 0 && lockedRowCount < listItems.length) |
| { |
| if (!lockedLinesBody) |
| { |
| lockedLinesBody = new UIComponent(); |
| lockedLinesBody.name = "lockedHorizontalLines"; |
| lockedContent.addChild(lockedLinesBody); |
| } |
| |
| if (getStyle("horizontalGridLines")) |
| { |
| var n:int = listItems.length; |
| for (var i:int = 0; i < n; i++) |
| { |
| drawHorizontalSeparator(lockedLinesBody, i, lineCol, rowInfo[i].y + rowInfo[i].height); |
| } |
| } |
| else |
| { |
| drawHorizontalSeparator(lockedLinesBody, lockedRowCount - 1 , lineCol, rowInfo[lockedRowCount - 1].y + rowInfo[lockedRowCount - 1].height); |
| } |
| } |
| } |
| |
| /** |
| * @private |
| * |
| * Call drawVerticalSeparator() to draw vertical lines |
| */ |
| protected function drawVerticalSeparators():void |
| { |
| var lines:Sprite = getLines(); |
| |
| var lockedContent:Sprite = getLockedContent(); |
| |
| // draw vertical lines in the locked column area |
| var lockedLinesBody:Sprite = Sprite(lockedContent.getChildByName("lockedVerticalLines")); |
| |
| if (!lockedLinesBody) |
| { |
| lockedLinesBody = new UIComponent(); |
| lockedLinesBody.name = "lockedVerticalLines"; |
| lockedContent.addChild(lockedLinesBody); |
| } |
| |
| // Make sure that the lockedLinesBody are on a higher index in the childList |
| // of lockedContent as compared to resizing separators |
| var child:UIComponent = UIComponent(lockedContent.getChildByName("lockedHeaderLines")); |
| if(child) |
| { |
| var childIndex:int = lockedContent.getChildIndex(DisplayObject(child)); |
| if(childIndex > lockedContent.getChildIndex(DisplayObject(lockedLinesBody))) |
| lockedContent.setChildIndex(lockedLinesBody, childIndex); |
| } |
| |
| lockedLinesBody.graphics.clear(); |
| |
| while (lockedLinesBody.numChildren) |
| { |
| lockedLinesBody.removeChildAt(0); |
| } |
| |
| var yVal:Number = (headerVisible && headerRowInfo && headerRowInfo[0]) ? headerRowInfo[0].height : 0; |
| var len:uint = Math.min((visibleColumns ? visibleColumns.length : 0), Math.max(0,lockedColumnCount)); |
| var vLines:Boolean = getStyle("verticalGridLines"); |
| var lineCol:uint = getStyle("verticalGridLineColor"); |
| if (vLines && len) |
| { |
| for (var i:int = 0; i < len; i++) |
| { |
| drawVerticalSeparator(lockedLinesBody, i, lineCol, |
| getHeaderInfo(visibleColumns[i]).headerItem.x + visibleColumns[i].width, yVal); |
| } |
| } |
| |
| // draw vertical lines in the scrollable area |
| var linesBody:Sprite = getLinesBody(lines, "verticalLines"); |
| |
| // clear the vertical lines and draw them again |
| linesBody.graphics.clear(); |
| |
| while (linesBody.numChildren) |
| { |
| linesBody.removeChildAt(0); |
| } |
| |
| len = visibleColumns.length; |
| |
| // defend against degenerate case when width == 0 |
| if (len > visibleColumns.length) |
| len = visibleColumns.length; |
| |
| vLines = getStyle("verticalGridLines"); |
| lineCol = getStyle("verticalGridLineColor"); |
| |
| if (vLines && headerInfos && hasHeaderItemsCreated(0) && hasHeaderItemsCreated(len - 1)) |
| { |
| //Check against the negative case |
| var lockedColCount:int = Math.max(0, lockedColumnCount); |
| |
| for (i = lockedColCount; i < len - 1; i++) |
| { |
| var headerInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(visibleColumns[i]); |
| |
| drawVerticalSeparator(linesBody, absoluteToVisibleColumnIndex(visibleColumns[i].colNum), lineCol, |
| headerInfo.headerItem.x + visibleColumns[i].width, yVal); |
| } |
| } |
| |
| // is this drawing the vertical locked column indicator? |
| if (!vLines && lockedColumnCount > 0 && lockedColumnCount < len) |
| drawVerticalSeparator(linesBody, lockedColumnCount - 1, lineCol, |
| getHeaderInfo(visibleColumns[lockedColumnCount-1]).headerItem.x |
| + visibleColumns[lockedColumnCount - 1].width, 0); |
| } |
| |
| /** |
| * @private |
| * |
| * Get the 'lockedContent' Sprite |
| */ |
| private function getLockedContent():Sprite |
| { |
| var locked:Sprite = Sprite(listContent.getChildByName("lockedContent")); |
| if (!locked) |
| { |
| locked = new UIComponent(); |
| locked.name = "lockedContent"; |
| locked.cacheAsBitmap = true; |
| locked.mouseEnabled = false; |
| listContent.addChild(locked); |
| } |
| listContent.setChildIndex(locked, listContent.numChildren - 1); |
| |
| return locked; |
| } |
| |
| /** |
| * @private |
| * |
| * Get the 'lines' Sprite |
| */ |
| private function getLines():Sprite |
| { |
| var lines:Sprite = Sprite(listSubContent.getChildByName("lines")); |
| if (!lines) |
| { |
| lines = new UIComponent(); |
| lines.name = "lines"; |
| lines.cacheAsBitmap = true; |
| lines.mouseEnabled = false; |
| listSubContent.addChild(lines); |
| } |
| listSubContent.setChildIndex(lines, listSubContent.numChildren - 1); |
| |
| return lines; |
| } |
| |
| /** |
| * @private |
| * |
| * Get the Sprite for horizontal/vertical lines |
| */ |
| private function getLinesBody(lines:Sprite, linesBodyName:String):Sprite |
| { |
| var linesBody:Sprite = Sprite(lines.getChildByName(linesBodyName)); |
| |
| if (!linesBody) |
| { |
| linesBody = new UIComponent(); |
| linesBody.name = linesBodyName; |
| lines.addChild(linesBody); |
| } |
| |
| return linesBody; |
| } |
| |
| /** |
| * Removes column header separators that you normally use |
| * to resize columns. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function clearSeparators():void |
| { |
| if (!separators) |
| return; |
| |
| var lines:Sprite = Sprite(listSubContent.getChildByName("lines")); |
| var headerLines:Sprite = Sprite(lines.getChildByName("header")); |
| if (headerLines) |
| { |
| while (headerLines.numChildren) |
| { |
| headerLines.removeChildAt(headerLines.numChildren - 1); |
| separators.pop(); |
| } |
| } |
| |
| var lockedContent:Sprite = getLockedContent(); |
| headerLines = Sprite(lockedContent.getChildByName("lockedHeaderLines")); |
| if (headerLines) |
| { |
| while (headerLines.numChildren) |
| { |
| headerLines.removeChildAt(headerLines.numChildren - 1); |
| lockedSeparators.pop(); |
| } |
| } |
| } |
| |
| /** |
| * Creates and displays the column header separators that the user |
| * normally uses to resize columns. This implementation uses |
| * the same Sprite as the lines and column backgrounds, adds |
| * instances of the <code>headerSeparatorSkin</code>, and attaches mouse |
| * listeners to them in order to know when the user wants |
| * to resize a column. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function drawSeparators():void |
| { |
| var lines:Sprite = getLines(); |
| lines.graphics.clear(); |
| |
| var optimumColumns:Array = getOptimumColumns(); |
| |
| if (headerSepSkinChanged) |
| { |
| headerSepSkinChanged = false; |
| clearSeparators(); |
| } |
| |
| if (!separators) |
| { |
| separators = []; |
| lockedSeparators = []; |
| } |
| |
| lines = Sprite(listSubContent.getChildByName("lines")); |
| |
| var actualLocked:int = 0; |
| var allColsLocked:Boolean = false; |
| var numUnLockedSeparators:int = Math.max(0, optimumColumns.length - 1); |
| |
| var lockedContent:Sprite = getLockedContent(); |
| var lockedHeaderLines:UIComponent = UIComponent(lockedContent.getChildByName("lockedHeaderLines")); |
| var headerLines:UIComponent = UIComponent(lines.getChildByName("header")); |
| |
| if(optimumColumns && optimumColumns.length > 0) |
| { |
| if (lockedColumnCount > 0) |
| { |
| actualLocked = Math.min(lockedColumnCount, optimumColumns.length); |
| allColsLocked = (actualLocked == optimumColumns.length); |
| |
| // -1 because we need one less separator, thus when all columns are |
| // locked, we dont draw separator after last column |
| if(allColsLocked) |
| actualLocked--; |
| |
| //Number of separators left to be drawn in the scrollable area have reduced by actualLocked |
| numUnLockedSeparators = numUnLockedSeparators - actualLocked; |
| |
| // Drawing a separator at lockedColumn boundary is required for resize cursor to appear |
| // when mouse is coming from right |
| if(!allColsLocked) |
| numUnLockedSeparators++; |
| |
| //Create only if needed i.e lockedColumnCount > 0 |
| if (!lockedHeaderLines) |
| { |
| lockedHeaderLines = new UIComponent(); |
| lockedHeaderLines.name = "lockedHeaderLines"; |
| lockedContent.addChild(lockedHeaderLines); |
| } |
| } |
| |
| if(lockedHeaderLines) |
| createHeaderSeparators(actualLocked, lockedSeparators, lockedHeaderLines); |
| |
| if (!headerLines) |
| { |
| headerLines = new UIComponent(); |
| headerLines.name = "header"; |
| lines.addChild(headerLines); |
| } |
| |
| // Create separators for columns which are not locked |
| // -1 because we need one less separator |
| createHeaderSeparators(numUnLockedSeparators, separators, headerLines); |
| } |
| |
| // remove extra locked separators |
| if(lockedHeaderLines) |
| removeExtraSeparators(actualLocked, lockedSeparators, lockedHeaderLines); |
| |
| // remove extra unlocked separators |
| if (headerLines) |
| removeExtraSeparators(numUnLockedSeparators, separators, headerLines); |
| } |
| |
| /** |
| * Returns the header separators between column headers, |
| * and populates the <code>separators</code> Array with the separators returned. |
| * |
| * @param i The number of separators to return. |
| * |
| * @param seperators Array to be populated with the header objects. |
| * |
| * @param headerLines The parent component of the header separators. |
| * Flex calls the <code>headerLines.getChild()</code> method internally to return the separators. |
| * |
| * @return The header separators between column headers. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function getSeparator(i:int, seperators:Array, headerLines:UIComponent):UIComponent |
| { |
| var sep:UIComponent; |
| var sepSkin:IFlexDisplayObject; |
| |
| if (i < headerLines.numChildren) |
| { |
| sep = UIComponent(headerLines.getChildAt(i)); |
| sepSkin = IFlexDisplayObject(sep.getChildAt(0)); |
| } |
| else |
| { |
| var headerSeparatorClass:Class = |
| getStyle("headerSeparatorSkin"); |
| sepSkin = new headerSeparatorClass(); |
| if (sepSkin is ISimpleStyleClient) |
| ISimpleStyleClient(sepSkin).styleName = this; |
| sep = new UIComponent(); |
| sep.addChild(DisplayObject(sepSkin)); |
| headerLines.addChild(sep); |
| DisplayObject(sep).addEventListener( |
| MouseEvent.MOUSE_OVER, columnResizeMouseOverHandler); |
| DisplayObject(sep).addEventListener( |
| MouseEvent.MOUSE_OUT, columnResizeMouseOutHandler); |
| DisplayObject(sep).addEventListener( |
| MouseEvent.MOUSE_DOWN, columnResizeMouseDownHandler); |
| seperators.push(sep); |
| } |
| return sep; |
| } |
| |
| |
| /** |
| * Creates the header separators between column headers, |
| * and populates the <code>separators</code> Array with the separators created. |
| * |
| * @param n The number of separators to create. |
| * |
| * @param seperators Array to be populated with the header objects. |
| * |
| * @param headerLines The parent component of the header separators to which the separators are added. |
| * That is, Flex calls the <code>headerLines.addChild()</code> method internally to add the separators to the display. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function createHeaderSeparators(n:int, seperators:Array, headerLines:UIComponent):void |
| { |
| var optimumColumns:Array = getOptimumColumns(); |
| |
| for (var i:int = 0; i < n; i++) |
| { |
| var headerItemIndex:int = (lockedColumnCount > 0 && seperators != lockedSeparators) ? i+lockedColumnCount - 1 : i; |
| |
| var sep:UIComponent = getSeparator(i, seperators, headerLines); |
| var sepSkin:IFlexDisplayObject = IFlexDisplayObject(sep.getChildAt(0)); |
| if (!headerItems || !headerItems[0] || !headerItems[0][headerItemIndex]) |
| { |
| sep.visible = false; |
| continue; |
| } |
| |
| sep.visible = true; |
| sep.x = headerItems[0][headerItemIndex].x + |
| optimumColumns[headerItemIndex].width - Math.round(sep.measuredWidth / 2 + 0.5); |
| |
| if (i > 0) |
| { |
| sep.x = Math.max(sep.x, |
| seperators[i - 1].x + Math.round(sep.measuredWidth / 2 + 0.5)); |
| } |
| sep.y = 0; |
| |
| sepSkin.setActualSize(sepSkin.measuredWidth, |
| headerRowInfo.length ? |
| headerRowInfo[0].height : |
| headerHeight); |
| |
| // Draw invisible background for separator affordance |
| sep.graphics.clear(); |
| sep.graphics.beginFill(0xFFFFFF, 0); |
| sep.graphics.drawRect(-separatorAffordance, 0, sepSkin.measuredWidth + separatorAffordance , headerHeight); |
| sep.graphics.endFill(); |
| } |
| } |
| |
| /** |
| * @private |
| * removes the extra separators |
| */ |
| private function removeExtraSeparators(n:int, seperators:Array, headerLines:UIComponent):void |
| { |
| while (headerLines.numChildren > n) |
| { |
| headerLines.removeChildAt(headerLines.numChildren - 1); |
| seperators.pop(); |
| } |
| } |
| |
| /** |
| * @private |
| * Update sortIndex and sortDirection based on sort info availabled in |
| * underlying data provider. |
| */ |
| private function updateSortIndexAndDirection():void |
| { |
| // Don't show sort indicator if sortableColumns is false or if the |
| // column sorted on has sortable="false" |
| |
| if (!sortableColumns) |
| { |
| lastSortIndex = sortIndex; |
| sortIndex = -1; |
| |
| if (lastSortIndex != sortIndex) |
| invalidateDisplayList(); |
| |
| return; |
| } |
| |
| if (!dataProvider) |
| return; |
| |
| var view:ICollectionView = ICollectionView(dataProvider); |
| var sort:ISort = view.sort; |
| if (!sort) |
| { |
| sortIndex = lastSortIndex = -1; |
| return; |
| } |
| |
| var fields:Array = sort.fields; |
| if (!fields) |
| return; |
| |
| if (fields.length != 1) |
| { |
| lastSortIndex = sortIndex; |
| sortIndex = -1; |
| |
| if (lastSortIndex != sortIndex) |
| invalidateDisplayList(); |
| |
| return; |
| } |
| |
| // fields.length == 1, so the collection is sorted on a single field. |
| var sortField:ISortField = fields[0]; |
| var n:int = _columns.length; |
| for (var i:int = 0; i < n; i++) |
| { |
| if (_columns[i].dataField == sortField.name) |
| { |
| sortIndex = _columns[i].sortable ? i : -1; |
| sortDirection = sortField.descending ? "DESC" : "ASC"; |
| return; |
| } |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| private function setEditedItemPosition(coord:Object):void |
| { |
| bEditedItemPositionChanged = true; |
| _proposedEditedItemPosition = coord; |
| invalidateDisplayList(); |
| } |
| |
| /** |
| * @private |
| * focus an item renderer in the grid - harder than it looks |
| */ |
| private function commitEditedItemPosition(coord:Object):void |
| { |
| if (!enabled || !editable.length) |
| return; |
| |
| // just give focus back to the itemEditorInstance |
| if (itemEditorInstance && coord && |
| itemEditorInstance is IFocusManagerComponent && |
| _editedItemPosition.rowIndex == coord.rowIndex && |
| _editedItemPosition.columnIndex == coord.columnIndex) |
| { |
| IFocusManagerComponent(itemEditorInstance).setFocus(); |
| return; |
| } |
| |
| // dispose of any existing editor, saving away its data first |
| if (itemEditorInstance) |
| { |
| var reason:String; |
| if (!coord) |
| { |
| reason = AdvancedDataGridEventReason.OTHER; |
| } |
| else |
| { |
| reason = (!editedItemPosition || coord.rowIndex == editedItemPosition.rowIndex) ? |
| AdvancedDataGridEventReason.NEW_COLUMN : |
| AdvancedDataGridEventReason.NEW_ROW; |
| } |
| if (!endEdit(reason) && reason != AdvancedDataGridEventReason.OTHER) |
| return; |
| } |
| |
| // store the value |
| _editedItemPosition = coord; |
| |
| // allow setting of undefined to dispose item editor instance |
| if (!coord) |
| return; |
| |
| if (dontEdit) |
| { |
| return; |
| } |
| |
| var rowIndex:int = coord.rowIndex; |
| var colIndex:int = coord.columnIndex; |
| if (displayableColumns.length != _columns.length) |
| { |
| var n:int = displayableColumns.length; |
| for (var i:int = 0; i < n; i++) |
| { |
| if (displayableColumns[i].colNum >= colIndex) |
| { |
| colIndex = i; |
| break; |
| } |
| } |
| if (i == displayableColumns.length) |
| colIndex = 0; |
| } |
| |
| // trace("commitEditedItemPosition ", coord.rowIndex, selectedIndex); |
| |
| var needChangeEvent:Boolean = false; |
| if (selectedIndex != coord.rowIndex) |
| { |
| commitSelectedIndex(coord.rowIndex); |
| needChangeEvent = true; |
| } |
| |
| var actualLockedRows:int = lockedRowCount; |
| var lastRowIndex:int = verticalScrollPosition + listItems.length - 1; |
| var partialRow:int = (rowInfo[listItems.length - 1].y + rowInfo[listItems.length - 1].height > listContent.height) ? 1 : 0; |
| |
| // actual row/column is the offset into listItems |
| if (rowIndex > actualLockedRows) |
| { |
| // not a locked editable row make sure it is on screen |
| if (rowIndex < verticalScrollPosition + actualLockedRows) |
| verticalScrollPosition = rowIndex - actualLockedRows; |
| else |
| { |
| // variable row heights means that we can't know how far to scroll sometimes so we loop |
| // until we get it right |
| while (rowIndex > lastRowIndex || |
| // we're the last row, and we're partially visible, but we're not |
| // the top scrollable row already |
| (rowIndex == lastRowIndex && rowIndex > verticalScrollPosition + actualLockedRows && |
| partialRow)) |
| { |
| if (verticalScrollPosition == maxVerticalScrollPosition) |
| break; |
| verticalScrollPosition = Math.min(verticalScrollPosition + (rowIndex > lastRowIndex ? rowIndex - lastRowIndex : partialRow), maxVerticalScrollPosition); |
| lastRowIndex = verticalScrollPosition + listItems.length - 1; |
| partialRow = (rowInfo[listItems.length - 1].y + rowInfo[listItems.length - 1].height > listContent.height) ? 1 : 0; |
| } |
| } |
| actualRowIndex = rowIndex - verticalScrollPosition; |
| } |
| else |
| { |
| if (rowIndex == actualLockedRows) |
| verticalScrollPosition = 0; |
| |
| actualRowIndex = rowIndex; |
| } |
| |
| var len:uint = /*(headerItems && headerItems[0]) ? headerItems[0].length :*/ visibleColumns.length; |
| var lastColIndex:int = horizontalScrollPosition + len - 1; |
| |
| // TODO with locked columns this won't give correct results? |
| var headerInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(visibleColumns[visibleColumns.length-1]); |
| var partialCol:int = (headerInfo.headerItem.x + headerInfo.column.width |
| > listContent.width) ? 1 : 0; |
| |
| if(colIndex > lockedColumnCount) |
| { |
| if (colIndex < horizontalScrollPosition + lockedColumnCount) |
| { |
| horizontalScrollPosition = colIndex - lockedColumnCount; |
| } |
| else |
| { |
| while (colIndex > lastColIndex || |
| (colIndex == lastColIndex && colIndex > horizontalScrollPosition + lockedColumnCount && |
| partialCol)) |
| { |
| if (horizontalScrollPosition == maxHorizontalScrollPosition) |
| break; |
| horizontalScrollPosition = Math.min(horizontalScrollPosition + (colIndex > lastColIndex ? colIndex - lastColIndex : partialCol), maxHorizontalScrollPosition); |
| |
| lastColIndex = horizontalScrollPosition + visibleColumns.length - 1; |
| headerInfo = getHeaderInfo(visibleColumns[visibleColumns.length - 1]); |
| partialCol = (headerInfo.headerItem && headerInfo.headerItem.x + headerInfo.headerItem.width > listContent.width) ? 1 : 0; |
| } |
| } |
| // Need to get the index in visibleColumns |
| actualColIndex = absoluteToVisibleColumnIndex(displayToAbsoluteColumnIndex(colIndex)); |
| } |
| else |
| { |
| if (colIndex == lockedColumnCount) |
| horizontalScrollPosition = 0; |
| |
| actualColIndex = colIndex; |
| } |
| |
| // get the actual references for the column, row, and item |
| var item:IListItemRenderer; |
| if (listItems[actualRowIndex] && listItems[actualRowIndex][actualColIndex]) |
| item = listItems[actualRowIndex][actualColIndex]; |
| if (!item) |
| { |
| // assume that editing was cancelled |
| commitEditedItemPosition(null); |
| return; |
| } |
| |
| if (needChangeEvent) |
| { |
| var evt:ListEvent = new ListEvent(ListEvent.CHANGE); |
| evt.columnIndex = coord.columnIndex; |
| evt.rowIndex = coord.rowIndex; |
| evt.itemRenderer = item; |
| dispatchEvent(evt); |
| } |
| |
| var event:AdvancedDataGridEvent = |
| new AdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_EDIT_BEGIN, false, true); |
| // ITEM_EDIT events are cancelable |
| event.columnIndex = displayableColumns[colIndex].colNum; |
| event.rowIndex = _editedItemPosition.rowIndex; |
| event.itemRenderer = item; |
| dispatchEvent(event); |
| |
| lastEditedItemPosition = _editedItemPosition; |
| |
| // user may be trying to change the focused item renderer |
| if (bEditedItemPositionChanged) |
| { |
| bEditedItemPositionChanged = false; |
| commitEditedItemPosition(_proposedEditedItemPosition); |
| _proposedEditedItemPosition = undefined; |
| |
| } |
| |
| if (!itemEditorInstance) |
| { |
| // assume that editing was cancelled |
| commitEditedItemPosition(null); |
| } |
| } |
| |
| /** |
| * Creates the item editor for the item renderer at the |
| * <code>editedItemPosition</code> using the editor |
| * specified by the <code>itemEditor</code> property. |
| * |
| * <p>This method sets the editor instance as the |
| * <code>itemEditorInstance</code> property.</p> |
| * |
| * <p>You may only call this method from within the event listener |
| * for the <code>itemEditBegin</code> event. |
| * To create an editor at other times, set the |
| * <code>editedItemPosition</code> property to generate |
| * the <code>itemEditBegin</code> event.</p> |
| * |
| * @param colIndex The column index in the data provider of the item to be edited. |
| * |
| * @param rowIndex The row index in the data provider of the item to be edited. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function createItemEditor(colIndex:int, rowIndex:int):void |
| { |
| if (displayableColumns.length != _columns.length) |
| { |
| var n:int = displayableColumns.length; |
| for (var i:int = 0; i < n; i++) |
| { |
| if (displayableColumns[i].colNum >= colIndex) |
| { |
| colIndex = i; |
| break; |
| } |
| } |
| if (i == displayableColumns.length) |
| colIndex = 0; |
| } |
| |
| var col:AdvancedDataGridColumn = displayableColumns[colIndex]; |
| if (rowIndex > lockedRowCount) |
| rowIndex -= verticalScrollPosition; |
| |
| if (colIndex > lockedColumnCount) |
| colIndex -= horizontalScrollPosition; |
| |
| var item:IListItemRenderer; |
| item = listItems[actualRowIndex][actualColIndex]; |
| |
| var rowData:ListRowInfo = rowInfo[actualRowIndex]; |
| |
| // Before the editor opens up, change the label to the original data |
| // and not the label which is (possibly) formatted data. |
| // See AdvancedDataGridColumn.itemToLabel() regarding formatter. |
| if (item is IDropInListItemRenderer) |
| IDropInListItemRenderer(item).listData.label = col.itemToLabel(item.data, false); |
| |
| if (!col.rendererIsEditor) |
| { |
| var dx:Number = 0; |
| var dy:Number = -2; |
| var dw:Number = 0; |
| var dh:Number = 4; |
| // if this isn't implemented, use an input control as editor |
| if (!itemEditorInstance) |
| { |
| var itemEditor:IFactory = col.itemEditor; |
| if (itemEditor == AdvancedDataGridColumn.defaultItemEditorFactory) |
| { |
| // if it is the default factory, see if someone |
| // overrode it with this style |
| var c:Class = getStyle("defaultDataGridItemEditor"); |
| if (c) |
| { |
| var fontName:String = |
| StringUtil.trimArrayElements(col.getStyle("fontFamily"), ","); |
| var fontWeight:String = col.getStyle("fontWeight"); |
| var fontStyle:String = col.getStyle("fontStyle"); |
| var bold:Boolean = (fontWeight == "bold"); |
| var italic:Boolean = (fontStyle == "italic"); |
| |
| var flexModuleFactory:IFlexModuleFactory = |
| getFontContext(fontName, bold, italic); |
| |
| itemEditor = col.itemEditor = new ContextualClassFactory( |
| c, flexModuleFactory); |
| } |
| } |
| |
| dx = col.editorXOffset; |
| dy = col.editorYOffset; |
| dw = col.editorWidthOffset; |
| dh = col.editorHeightOffset; |
| itemEditorInstance = itemEditor.newInstance(); |
| itemEditorInstance.owner = this; |
| itemEditorInstance.styleName = col; |
| addRendererToContentArea(itemEditorInstance, col); |
| } |
| itemEditorInstance.parent.setChildIndex(DisplayObject(itemEditorInstance), |
| itemEditorInstance.parent.numChildren - 1); |
| // give it the right size, look and placement |
| itemEditorInstance.visible = true; |
| |
| var itemXPos:Number = item.x + dx; |
| |
| itemEditorInstance.move(itemXPos, rowData.y + dy); |
| |
| // Original code: |
| /* |
| itemEditorInstance.setActualSize(Math.min(col.width + dw, |
| listContent.width - listContent.x - itemXPos), |
| Math.min(rowData.height + dh, listContent.height - listContent.y - itemEditorInstance.y)); |
| DisplayObject(itemEditorInstance).addEventListener(FocusEvent.FOCUS_OUT, itemEditorFocusOutHandler); |
| listContent.width - listContent.x - itemXPos), |
| Math.min(rowData.height + dh, listContent.height - listContent.y - itemEditorInstance.y)); |
| */ |
| |
| // To support column spanning: |
| itemEditorInstance.setActualSize(editedItemRenderer.width + dw, |
| Math.min(rowData.height + dh, |
| listContent.height - listContent.y - itemEditorInstance.y)); |
| |
| DisplayObject(itemEditorInstance).addEventListener(FocusEvent.FOCUS_OUT, itemEditorFocusOutHandler); |
| // Commenting to show the item (with disclosure icon) behind the item editor |
| //item.visible = false; |
| |
| layoutItemEditor(); |
| } |
| else |
| { |
| // if the item renderer is also the editor, we'll use it |
| itemEditorInstance = item; |
| } |
| |
| // listen for keyStrokes on the itemEditorInstance (which lets the grid supervise for ESC/ENTER) |
| DisplayObject(itemEditorInstance).addEventListener(KeyboardEvent.KEY_DOWN, editorKeyDownHandler); |
| // we disappear on any mouse down outside the editor |
| systemManager.getSandboxRoot(). |
| addEventListener(MouseEvent.MOUSE_DOWN, editorMouseDownHandler, true, 0, true); |
| systemManager.getSandboxRoot(). |
| addEventListener(SandboxMouseEvent.MOUSE_DOWN_SOMEWHERE, editorMouseDownHandler, false, 0, true); |
| // we disappear if stage is resized |
| systemManager.addEventListener(Event.RESIZE, editorStageResizeHandler, true, 0, true); |
| } |
| |
| /** |
| * @private |
| * Determines the next item renderer to navigate to using the Tab key. |
| * If the item renderer to be focused falls out of range (the end or beginning |
| * of the grid) then move focus outside the grid. |
| */ |
| private function findNextItemRenderer(shiftKey:Boolean):Boolean |
| { |
| if (!lastEditedItemPosition) |
| return false; |
| |
| if (!editable.length) |
| { |
| loseFocus(); |
| return false; |
| } |
| |
| // some other thing like a collection change has changed the |
| // position, so bail and wait for commit to reset the editor. |
| if (_proposedEditedItemPosition !== undefined) |
| return true; |
| |
| _editedItemPosition = lastEditedItemPosition; |
| |
| var index:int = _editedItemPosition.rowIndex; |
| var colIndex:int = _editedItemPosition.columnIndex; |
| |
| var found:Boolean = false; |
| var incr:int = shiftKey ? -1 : 1; |
| var maxIndex:int = collection.length - 1; |
| var itemRenderer:IListItemRenderer; |
| |
| // cycle till we find something worth focusing, or the end of the grid |
| while (!found) |
| { |
| // go to next column |
| colIndex += incr; |
| if (colIndex >= _columns.length || colIndex < 0) |
| { |
| // if we fall off the end of the columns, wrap around |
| colIndex = (colIndex < 0) ? _columns.length - 1 : 0; |
| // and increment/decrement the row index |
| index += incr; |
| if (index > maxIndex || index < 0) |
| { |
| if (endEdit(AdvancedDataGridEventReason.NEW_ROW)) |
| { |
| // if we've fallen off the rows, we need to leave the grid. get rid of the editor |
| setEditedItemPosition(null); |
| |
| // set focus back to the grid so default handler will move it to the next component |
| deferFocus(); |
| return false; |
| } |
| return true; |
| } |
| } |
| |
| // We have to skip cells where the item renderer is invisible so |
| // that we handle column spanning i.e. we should not open editors |
| // where column spanning is applied and the column should not be |
| // considered. |
| // TODO here we are checking for |
| // existence even before scrolling. |
| var visibleCoords:Object = absoluteToVisibleIndices(index, colIndex); |
| var visibleRowIndex:int = visibleCoords.rowIndex; |
| var visibleColIndex:int = visibleCoords.columnIndex; |
| |
| if (visibleColIndex > -1) // column.visible=false |
| { |
| // Assumption that item renderer is never invisible in the |
| // first column! i.e. When in last row, skip to the new last row's |
| // first column directly without checking the item renderer's |
| // visibility. |
| if (visibleRowIndex == listItems.length) // last row last column -> tab |
| visibleRowIndex -= 1; |
| else if (visibleRowIndex == -1) // first row first column -> shift-tab |
| visibleRowIndex = 0; |
| itemRenderer = null; |
| if (listItems[visibleRowIndex] && listItems[visibleRowIndex][visibleColIndex]) |
| itemRenderer = listItems[visibleRowIndex][visibleColIndex]; |
| if (itemRenderer && !itemRenderer.visible) // handle column-spanning |
| continue; |
| } |
| |
| var newData:Object = rowNumberToData(index); |
| if (newData == null) |
| return true; |
| if (!isDataEditable(newData)) |
| continue; |
| |
| // if we find a visible and editable column, move to it |
| // if the item is visible, then only create item editor for it |
| if (_columns[colIndex].editable && _columns[colIndex].visible) |
| { |
| found = true; |
| // kill the old edit session |
| var reason:String; |
| reason = index == _editedItemPosition.rowIndex ? |
| AdvancedDataGridEventReason.NEW_COLUMN : |
| AdvancedDataGridEventReason.NEW_ROW; |
| if (!itemEditorInstance || endEdit(reason)) |
| { |
| // send event to create the new one |
| var advancedDataGridEvent:AdvancedDataGridEvent = |
| new AdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_EDIT_BEGINNING, false, true); |
| // ITEM_EDIT events are cancelable |
| advancedDataGridEvent.columnIndex = colIndex; |
| advancedDataGridEvent.dataField = _columns[colIndex].dataField; |
| advancedDataGridEvent.rowIndex = index; |
| dispatchEvent(advancedDataGridEvent); |
| } |
| } |
| } |
| return found; |
| } |
| |
| private function loseFocus():void |
| { |
| // if we've fallen off the rows, we need to leave the grid. get rid of the editor |
| setEditedItemPosition(null); |
| // set focus back to the grid so default handler will move it to the next component |
| losingFocus = true; |
| setFocus(); |
| } |
| |
| /** |
| * This method closes an item editor currently open on an item renderer. |
| * You typically call this method only from within the event listener |
| * for the <code>itemEditEnd</code> event, after |
| * you have already called the <code>preventDefault()</code> method to |
| * prevent the default event listener from executing. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function destroyItemEditor():void |
| { |
| // trace("destroyItemEditor"); |
| if (itemEditorInstance) |
| { |
| DisplayObject(itemEditorInstance).removeEventListener(KeyboardEvent.KEY_DOWN, editorKeyDownHandler); |
| systemManager.getSandboxRoot(). |
| removeEventListener(MouseEvent.MOUSE_DOWN, editorMouseDownHandler, true); |
| systemManager.getSandboxRoot(). |
| removeEventListener(SandboxMouseEvent.MOUSE_DOWN_SOMEWHERE, editorMouseDownHandler); |
| systemManager.removeEventListener(Event.RESIZE, editorStageResizeHandler, true); |
| |
| var event:AdvancedDataGridEvent = |
| new AdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_FOCUS_OUT); |
| event.columnIndex = _editedItemPosition.columnIndex; |
| event.rowIndex = _editedItemPosition.rowIndex; |
| event.itemRenderer = itemEditorInstance; |
| dispatchEvent(event); |
| |
| if (! _columns[_editedItemPosition.columnIndex].rendererIsEditor) |
| { |
| // FocusManager.removeHandler() does not find |
| // itemEditors in focusableObjects[] array |
| // and hence does not remove the focusRectangle |
| if (itemEditorInstance && itemEditorInstance is UIComponent) |
| UIComponent(itemEditorInstance).drawFocus(false); |
| |
| // setfocus back to us so something on stage has focus |
| deferFocus(); |
| |
| // must call removeChild() so FocusManager.lastFocus becomes null |
| if (itemEditorInstance) |
| itemEditorInstance.parent.removeChild(DisplayObject(itemEditorInstance)); |
| |
| // we are not setting the item renderer's visibility to false while creating an editor, |
| // then why set its visibility to true |
| // setting it visible will display the invisible item renderer in case of Custom Rows |
| //editedItemRenderer.visible = true; |
| } |
| itemEditorInstance = null; |
| _editedItemPosition = null; |
| } |
| } |
| |
| /** |
| * @private |
| * When the user finished editing an item, this method is called. |
| * It dispatches the AdvancedDataGridEvent.ITEM_EDIT_END event to start the process |
| * of copying the edited data from |
| * the itemEditorInstance to the data provider and hiding the itemEditorInstance. |
| * returns true if nobody called preventDefault. |
| */ |
| protected function endEdit(reason:String):Boolean |
| { |
| // this happens if the renderer is removed asynchronously ususally with FDS |
| if (!editedItemRenderer) |
| return true; |
| |
| inEndEdit = true; |
| |
| var advancedDataGridEvent:AdvancedDataGridEvent = |
| new AdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_EDIT_END, false, true); |
| // ITEM_EDIT events are cancelable |
| advancedDataGridEvent.columnIndex = editedItemPosition.columnIndex; |
| advancedDataGridEvent.dataField = _columns[editedItemPosition.columnIndex].dataField; |
| advancedDataGridEvent.rowIndex = editedItemPosition.rowIndex; |
| advancedDataGridEvent.itemRenderer = editedItemRenderer; |
| advancedDataGridEvent.reason = reason; |
| dispatchEvent(advancedDataGridEvent); |
| // set a flag to not open another edit session if the item editor is still up |
| // this means somebody wants the old edit session to stay. |
| dontEdit = itemEditorInstance != null; |
| // trace("dontEdit", dontEdit); |
| |
| if (!dontEdit && reason == AdvancedDataGridEventReason.CANCELLED) |
| { |
| losingFocus = true; |
| setFocus(); |
| } |
| |
| inEndEdit = false; |
| |
| return !(advancedDataGridEvent.isDefaultPrevented()) |
| } |
| |
| /** |
| * @private |
| * Sets focus back to the grid so default handler will move it to the |
| * next component. |
| */ |
| private function deferFocus():void |
| { |
| losingFocus = true; |
| setFocus(); |
| losingFocus = false; |
| } |
| |
| /** |
| * @private |
| */ |
| mx_internal function columnRendererChanged(c:AdvancedDataGridColumn):void |
| { |
| var item:IListItemRenderer; |
| |
| var factory:IFactory = columnItemRendererFactory(c,true,null); |
| if (measuringObjects) |
| { |
| item = measuringObjects[factory]; |
| if (item) |
| { |
| item.parent.removeChild(DisplayObject(item)); |
| measuringObjects[factory] = null; |
| } |
| // TODO - set valid item to be passed |
| factory = columnItemRendererFactory(c,false,null); |
| item = measuringObjects[factory]; |
| if (item) |
| { |
| item.parent.removeChild(DisplayObject(item)); |
| measuringObjects[factory] = null; |
| } |
| } |
| if(freeItemRenderersTable[c]) |
| { |
| // remove item renderers |
| var freeRenderers:Array = freeItemRenderersTable[c][c.itemRenderer] as Array; |
| if (freeRenderers) |
| { |
| while (freeRenderers.length) |
| { |
| item = freeRenderers.pop(); |
| item.parent.removeChild(DisplayObject(item)); |
| } |
| } |
| // remove header renderers |
| freeRenderers = freeItemRenderersTable[c][c.headerRenderer ? c.headerRenderer : headerRenderer] as Array; |
| if (freeRenderers) |
| { |
| while (freeRenderers.length) |
| { |
| item = freeRenderers.pop(); |
| item.parent.removeChild(DisplayObject(item)); |
| } |
| } |
| } |
| rendererChanged = true; |
| invalidateDisplayList(); |
| } |
| |
| /** |
| * @private |
| */ |
| protected function getPossibleDropPositions(val:AdvancedDataGridColumn):Array |
| { |
| var n:int = visibleColumns ? visibleColumns.length : 0; |
| |
| var dropPositions:Array = []; |
| for ( var i:int = 0; i < n; i++) |
| { |
| dropPositions.push(getHeaderInfo(visibleColumns[i])); |
| } |
| return dropPositions; |
| } |
| |
| /** |
| * @private |
| */ |
| protected function hasHeaderItemsCreated(index:int=-1):Boolean |
| { |
| if(index == -1) |
| return (headerItems && headerItems[0] && headerItems[0][0]); |
| return (headerItems && headerItems[0] && headerItems[0][index]); |
| } |
| |
| /** |
| * @private |
| */ |
| protected function columnDraggingMouseMoveHandler(event:MouseEvent):void |
| { |
| if (!event.buttonDown) |
| { |
| columnDraggingMouseUpHandler(event); |
| return; |
| } |
| var item:IListItemRenderer; |
| var c:AdvancedDataGridColumn = movingColumn; |
| var s:Sprite; |
| var i:int = 0; |
| var n:int; |
| if (isNaN(startX)) |
| { |
| // If startX is not a number, dragging has just started. |
| // Initialise and return without actually moving anything. |
| |
| startX = event.stageX; |
| |
| // Set this to null so sort doesn't happen. |
| lastItemDown = null; |
| |
| // Create and position proxy. |
| // passing data as null, as it is used for header renderer |
| var proxy:IListItemRenderer = columnItemRenderer(c, true, null); |
| proxy.name = "headerDragProxy"; |
| |
| var rowData:AdvancedDataGridListData = AdvancedDataGridListData(makeListData(c, null, 0, c.colNum, c)); |
| if (proxy is IDropInListItemRenderer) |
| IDropInListItemRenderer(proxy).listData = rowData; |
| |
| listContent.addChild(DisplayObject(proxy)); |
| |
| n = orderedHeadersList.length; |
| for (i = 0; i < n; i++) |
| { |
| item = orderedHeadersList[i].headerItem; |
| if (item && item.data == movingColumn) |
| break; |
| } |
| |
| var h:Number = item.height + cachedPaddingBottom + cachedPaddingTop; |
| var w:Number = item.getExplicitOrMeasuredWidth(); |
| var x:Number = item.x; |
| |
| //In case we have scrolled the "selection" shown need to be shifted |
| if(orderedHeadersList[i].actualColNum >= lockedColumnCount) |
| { |
| x = getAdjustedXPos(item.x); |
| // In case of column grouping, it may be partially visible, so need to get the visible width as well as the |
| //x pos from which it is visible |
| if(horizontalScrollPosition > 0 && orderedHeadersList[i].actualColNum - horizontalScrollPosition < lockedColumnCount) |
| { |
| var lockedWidth:Number = 0; |
| if(lockedColumnCount > 0) |
| { |
| var lastLockedInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(_columns[lockedColumnCount-1]); |
| lockedWidth = lastLockedInfo.headerItem.x + _columns[lockedColumnCount - 1].width; |
| } |
| else |
| lockedWidth = 0; |
| |
| w -= (lockedWidth - x); |
| x = lockedWidth; |
| } |
| } |
| |
| proxy.data = c; |
| proxy.styleName = getStyle("headerDragProxyStyleName"); |
| UIComponentGlobals.layoutManager.validateClient(proxy, true); |
| proxy.setActualSize(w, _explicitHeaderHeight ? |
| headerHeight : proxy.getExplicitOrMeasuredHeight()); |
| |
| proxy.move(x, item.y); |
| |
| // Create, position and draw column overlay. |
| s = new FlexSprite(); |
| s.name = "columnDragOverlay"; |
| s.alpha = 0.6; |
| listContent.addChildAt(s, listContent.getChildIndex(selectionLayer)); |
| |
| var vm:EdgeMetrics = viewMetrics; |
| |
| s.x = x; |
| s.y = item.y - cachedPaddingTop;; |
| |
| if (w > 0) |
| { |
| var g:Graphics = s.graphics; |
| g.beginFill(getStyle("disabledColor")); |
| g.drawRect(0, 0, w, |
| unscaledHeight - vm.bottom - s.y); |
| g.endFill(); |
| } |
| |
| s = Sprite(selectionLayer.getChildByName("headerSelection")); |
| if (s) |
| s.width = w;//movingColumn.width; |
| |
| if (!listContent.mask) |
| { |
| // Clip the contents so the header drag proxy doesn't show |
| // outside the list. |
| var bm:EdgeMetrics = borderMetrics; |
| listContent.scrollRect = new Rectangle(0, 0, |
| unscaledWidth - bm.left - bm.right, |
| unscaledHeight - bm.top - bm.bottom); |
| } |
| |
| return; |
| } |
| |
| // Global coordinates. |
| var deltaX:Number = event.stageX - startX; |
| |
| // If the mouse pointer over the right (layoutDirection=”ltr”) or |
| // left (layoutDirection=”rtl”) half of the column, the drop indicator |
| // should be shown before the next column. |
| var deltaXInLocalCoordinates:Number = |
| (layoutDirection == LayoutDirection.LTR ? +deltaX : -deltaX); |
| |
| // Move header selection. |
| s = Sprite(selectionLayer.getChildByName("headerSelection")); |
| if (s) |
| s.x += deltaXInLocalCoordinates; |
| |
| // Move header proxy. |
| item = IListItemRenderer(listContent.getChildByName("headerDragProxy")); |
| if (item) |
| item.move(item.x + deltaXInLocalCoordinates, item.y); |
| |
| startX += deltaX; |
| |
| var pt:Point = new Point(event.stageX, event.stageY); |
| pt = listContent.globalToLocal(pt); |
| lastPt = pt; |
| |
| var headerSearchArray:Array = getPossibleDropPositions(movingColumn); |
| n = headerSearchArray.length; |
| var headerInfo:AdvancedDataGridHeaderInfo; |
| var columnXPos:Number = headerSearchArray[0].headerItem.x; |
| |
| var ww:Number = columnXPos; |
| var notLocked:Boolean = false; |
| |
| dropIndexFound = false; |
| |
| for (var k:int = 0; k < n; ++k) |
| { |
| headerInfo = headerSearchArray[k]; |
| |
| //Is the column getting checked is locked or not? |
| if(headerInfo.actualColNum >= lockedColumnCount) |
| notLocked = true; |
| |
| ww += headerInfo.column.width; |
| |
| //We are not interested in columns hidden in the left because of scrolling |
| // interested in visibleColumns only |
| if(notLocked && headerInfo.actualColNum + headerInfo.columnSpan - horizontalScrollPosition <= lockedColumnCount) |
| { |
| columnXPos = ww; |
| continue; |
| } |
| |
| if(notLocked) |
| columnXPos = getAdjustedXPos(columnXPos); |
| |
| if (pt.x >= columnXPos && pt.x < columnXPos + headerInfo.column.width) |
| { |
| dropIndexFound = true; |
| isHeaderDragOutside = false; |
| |
| // If the mouse pointer over the right (ltr) or left (rtl) half |
| // of the column, the drop indicator should be shown before the next column. |
| if (pt.x > (columnXPos + headerInfo.column.width/2) || |
| //Column groups which are partially visible should |
| //show drag indicator at the right end only |
| notLocked && headerInfo.actualColNum - horizontalScrollPosition < lockedColumnCount) |
| { |
| columnXPos += headerInfo.column.width; |
| ++k; |
| } |
| |
| if (dropColumnIndex != k) |
| { |
| dropColumnIndex = k; |
| |
| if (!columnDropIndicator) |
| { |
| var dropIndicatorClass:Class |
| = getStyle("columnDropIndicatorSkin"); |
| if (!dropIndicatorClass) |
| dropIndicatorClass = DataGridColumnDropIndicator; |
| columnDropIndicator = IFlexDisplayObject( |
| new dropIndicatorClass()); |
| |
| if (columnDropIndicator is ISimpleStyleClient) |
| ISimpleStyleClient(columnDropIndicator).styleName = this; |
| |
| listContent.addChild( |
| DisplayObject(columnDropIndicator)); |
| } |
| |
| listContent.setChildIndex( |
| DisplayObject(columnDropIndicator), |
| listContent.numChildren - 1); |
| columnDropIndicator.x = columnXPos - 2; |
| columnDropIndicator.y = item.y; |
| |
| columnDropIndicator.setActualSize(3, listContent.height - item.y); |
| } |
| |
| columnDropIndicator.visible = true; |
| |
| break; |
| } |
| columnXPos = ww; |
| } |
| |
| // dispatch a headerDragOutside event if we have moved out |
| // Need not dispatch if we are already out |
| if(!dropIndexFound && isHeaderDragOutside == false) |
| { |
| isHeaderDragOutside = true; |
| |
| var advancedDataGridEvent:AdvancedDataGridEvent = new AdvancedDataGridEvent( |
| AdvancedDataGridEvent.HEADER_DRAG_OUTSIDE, |
| false, true); |
| |
| var movingColumnInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(movingColumn); |
| |
| advancedDataGridEvent.column = movingColumn; |
| advancedDataGridEvent.columnIndex = -1; |
| advancedDataGridEvent.itemRenderer = movingColumnInfo.headerItem; |
| advancedDataGridEvent.triggerEvent = event; |
| |
| dispatchEvent(advancedDataGridEvent); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| protected function columnDraggingMouseUpHandler(event:Event):void |
| { |
| if (!movingColumn) |
| return; |
| var origIndex:int = movingColumn.colNum; |
| |
| if (dropColumnIndex >= 0) |
| { |
| if (dropColumnIndex >= visibleColumns.length) |
| { |
| dropColumnIndex = visibleColumns.length - 1; |
| } |
| else |
| { |
| if (origIndex < visibleColumns[dropColumnIndex].colNum) |
| dropColumnIndex--; |
| } |
| |
| // dropColumnIndex is actually the index into the visibleColumns |
| // array. Get the corresponding index into the _columns array. |
| dropColumnIndex = visibleColumns[dropColumnIndex].colNum; |
| } |
| |
| // Shift columns. |
| shiftColumns(origIndex, dropColumnIndex, event as MouseEvent); |
| unsetColumnDragParameters(); |
| } |
| |
| /** |
| * @private |
| */ |
| protected function unsetColumnDragParameters():void |
| { |
| var sbRoot:DisplayObject = systemManager.getSandboxRoot(); |
| sbRoot.removeEventListener(MouseEvent.MOUSE_MOVE, columnDraggingMouseMoveHandler, true); |
| sbRoot.removeEventListener(MouseEvent.MOUSE_UP, columnDraggingMouseUpHandler, true); |
| sbRoot.removeEventListener(SandboxMouseEvent.MOUSE_UP_SOMEWHERE, columnDraggingMouseUpHandler); |
| systemManager.deployMouseShields(false); |
| |
| var proxy:IListItemRenderer = |
| IListItemRenderer(listContent.getChildByName("headerDragProxy")); |
| if (proxy) |
| listContent.removeChild(DisplayObject(proxy)); |
| |
| var s:Sprite = Sprite(selectionLayer.getChildByName("headerSelection")); |
| if (s) |
| selectionLayer.removeChild(s); |
| |
| if (columnDropIndicator) |
| columnDropIndicator.visible = false; |
| |
| s = Sprite(listContent.getChildByName("columnDragOverlay")); |
| if (s) |
| listContent.removeChild(s); |
| |
| listContent.scrollRect = null; |
| |
| // Add the mask which was present before column dragging |
| addClipMask(false); |
| |
| startX = NaN; |
| movingColumn = null; |
| dropColumnIndex = -1; |
| } |
| |
| /** |
| * Checks if dragging is allowed for a particular column or not. |
| * |
| * @param draggedColumn The column being dragged. |
| * |
| * @return <code>true</code> if dragging is allowed for the column. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function isDraggingAllowed(draggedColumn:AdvancedDataGridColumn):Boolean |
| { |
| return draggedColumn.draggable; |
| } |
| |
| /** |
| * Returns a SortInfo instance containing sorting information for the column. |
| * |
| * @param column The column index. |
| * |
| * @return A SortInfo instance. |
| * |
| * @see mx.controls.advancedDataGridClasses.SortInfo |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function getFieldSortInfo(column:AdvancedDataGridColumn):SortInfo |
| { |
| if (column && collection && collection.sort) |
| { |
| var colUID:String; |
| //In case there is no dataField we will use the unique column uid to identify if the column is sorted |
| if (!column.dataField) |
| colUID = itemToUID(column); |
| |
| var n:int = collection.sort.fields.length; |
| |
| for (var i:int = 0; i < n; i++) |
| { |
| if (column.dataField && collection.sort.fields[i].name == column.dataField |
| || colUID && collection.sort.fields[i].name == colUID) |
| { |
| // return 1-based, not 0-based sequence number |
| return new SortInfo(i + 1, collection.sort.fields[i].descending); |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Checks if editing is allowed for a group or summary row. |
| * |
| * @param data Data provider Object for the row. |
| * |
| * @return <code>true</code> if editing is allowed for the group or summary row. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function isDataEditable(data:Object):Boolean |
| { |
| return true; |
| } |
| |
| /** |
| * @private |
| * |
| * Invalidate everything (properties, size, displaylist) for an IListItemRenderer if it is |
| * IInvalidating. |
| */ |
| protected function invalidateRenderer(renderer:IListItemRenderer):void |
| { |
| var i:IInvalidating = renderer as IInvalidating; |
| if (i) |
| { |
| i.invalidateProperties(); |
| i.invalidateSize(); |
| i.invalidateDisplayList(); |
| } |
| } |
| |
| /** |
| * @private |
| * |
| * Reset the headers. |
| */ |
| protected function invalidateHeaders():void |
| { |
| // Refresh the headers so that the separator line is removed or added |
| var n:int = orderedHeadersList.length; |
| for (var i:int = 0; i < n; i++) |
| { |
| invalidateRenderer(orderedHeadersList[i].headerItem); |
| } |
| } |
| |
| /** |
| * @private |
| * Given a row number, get the corresponding data in the dataProvider. |
| */ |
| protected function rowNumberToData(rowNumber:int):Object |
| { |
| var iterator:IViewCursor = collection.createCursor(); |
| iterator.seek(CursorBookmark.FIRST, rowNumber); |
| if (iterator.afterLast) |
| return null; |
| return iterator.current; |
| } |
| |
| /** |
| * @private |
| * find the next item renderer down from the currently edited item renderer, and focus it. |
| */ |
| private function findNextEnterItemRenderer(event:KeyboardEvent):void |
| { |
| // some other thing like a collection change has changed the |
| // position, so bail and wait for commit to reset the editor. |
| if (_proposedEditedItemPosition !== undefined) |
| return; |
| |
| _editedItemPosition = lastEditedItemPosition; |
| |
| var rowIndex:int = _editedItemPosition.rowIndex; |
| var columnIndex:int = _editedItemPosition.columnIndex; |
| var newIndex:int = rowIndex; |
| |
| do |
| { |
| // modify direction with SHIFT (up or down) |
| newIndex += (event.shiftKey ? -1 : 1); |
| // only move if we're within range |
| if (newIndex < collection.length && newIndex >= 0) |
| { |
| rowIndex = newIndex; |
| } |
| else |
| { |
| setEditedItemPosition(null); |
| return; |
| } |
| |
| var newData:Object = rowNumberToData(newIndex); |
| if (newData == null) |
| { |
| setEditedItemPosition(null); |
| return; |
| } |
| |
| if (isDataEditable(newData)) |
| break; |
| |
| } while (true); |
| |
| // send event to create the new one |
| var advancedDataGridEvent:AdvancedDataGridEvent = |
| new AdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_EDIT_BEGINNING, false, true); |
| // ITEM_EDIT events are cancelable |
| advancedDataGridEvent.columnIndex = columnIndex; |
| advancedDataGridEvent.dataField = _columns[columnIndex].dataField; |
| advancedDataGridEvent.rowIndex = rowIndex; |
| dispatchEvent(advancedDataGridEvent); |
| } |
| |
| /** |
| * Returns the column index corresponding to the field name of a sortable field. |
| * |
| * @param name The name of a sortable field of the data provider, as defined by |
| * an instance of the SortField class. |
| * |
| * @return The column index of the sortable field. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function findSortField(name:String):int |
| { |
| if (collection && collection.sort) |
| { |
| var n:int = collection.sort.fields.length; |
| for (var i:int = 0; i < n; i++) |
| { |
| if (collection.sort.fields[i]["name"] == name) |
| return i; |
| } |
| |
| } |
| |
| return -1; |
| } |
| |
| /** |
| * Adds a data field to the list of sort fields. |
| * Indicate the data field by specifying its column location. |
| * |
| * @param columnName The name of the column that corresponds to the data field. |
| * |
| * @param columnNumber The column index in the AdvancedDataGrid control. |
| * |
| * @param collection The data collection that contains the data field. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function addSortField(columnName:String, |
| columnNumber:int, |
| collection:ICollectionView):void |
| { |
| var column:AdvancedDataGridColumn = _columns[columnNumber]; |
| |
| if (!column.sortable) |
| return; |
| |
| var desc:Boolean = column.sortDescending; |
| var fields:Array; |
| |
| var singleColumnSort:Boolean = false; |
| if (!collection.sort || !collection.sort.fields) |
| { |
| singleColumnSort = true; |
| var sort:ISort = new Sort(); |
| sort.fields = []; |
| |
| collection.sort = sort; |
| } |
| else if (collection.sort.fields.length == 0) |
| { |
| singleColumnSort = true; |
| } |
| |
| if (singleColumnSort) |
| { |
| lastSortIndex = sortIndex; |
| sortIndex = columnNumber; |
| sortColumn = column; |
| |
| var dir:String = (desc) ? "DESC" : "ASC"; |
| sortDirection = dir; |
| } |
| else |
| { |
| lastSortIndex = -1; |
| sortIndex = -1; |
| sortColumn = null; |
| sortDirection = null; |
| } |
| |
| column.sortDescending = desc; |
| var field:ISortField = new SortField(columnName, false, desc, null, column.sortCompareType, column.sortCompareFunction); |
| |
| fields = collection.sort.fields; |
| if (fields == null) |
| fields = []; |
| fields.push(field); |
| collection.sort.fields = fields; |
| } |
| |
| /** |
| * Removes a data field from the list of sort fields. |
| * Indicate the data field by specifying its column location. |
| * |
| * @param columnName The name of the column that corresponds to the data field. |
| * |
| * @param columnNumber The column index in the AdvancedDataGrid control. |
| * |
| * @param collection The data collection that contains the data field. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function removeSortField(columnName:String, |
| columnNumber:int, |
| collection:ICollectionView):void |
| { |
| var column:AdvancedDataGridColumn = _columns[columnNumber]; |
| |
| if (!collection || !collection.sort || !collection.sort.fields |
| || !collection.sort.fields.length) |
| return; |
| |
| var columnNumberToRemove:int = -1; |
| var n:int = collection.sort.fields.length; |
| |
| for (var i:int = 0; i < n; i++) |
| { |
| if (collection.sort.fields[i].name == column.dataField) |
| { |
| columnNumberToRemove = i; |
| break; |
| } |
| } |
| |
| if (columnNumberToRemove != -1) |
| collection.sort.fields.splice(columnNumberToRemove, 1); |
| } |
| |
| /** |
| * Flip the order from ascending <-> descending for the given column name |
| * in the sort fields list |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| private function flipSortOrder(columnName:String, columnNumber:int, collection:ICollectionView):String |
| { |
| if (collection.sort) |
| { |
| var column:AdvancedDataGridColumn = _columns[columnNumber]; |
| |
| collection.sort.fields[findSortField(columnName)]["descending"] |
| = ! collection.sort.fields[findSortField(columnName)]["descending"]; |
| |
| if (collection.sort.fields[findSortField(columnName)]["descending"]) |
| { |
| column.sortDescending = true; |
| return "DESC"; |
| } |
| else |
| { |
| column.sortDescending = false; |
| return "ASC"; |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * A helper method to determine which item renderer is under the mouse. |
| */ |
| protected function findRenderer(pt:Point,items:Array,info:Array,yy:Number = 0):IListItemRenderer |
| { |
| var r:IListItemRenderer; |
| var ww:Number = 0; |
| var m:int = 0; |
| var n:int = items.length; |
| var optimumColumns:Array = getOptimumColumns(); |
| for (var i:int = 0; i < n; i++) |
| { |
| if (items[i].length) |
| { |
| if (pt.y < yy + info[i].height) |
| { |
| m = items[i].length; |
| if (m == 1) |
| { |
| r = items[i][0]; |
| break; |
| } |
| |
| ww = 0; |
| for (var j:int = horizontalScrollPosition; j < m; j++) |
| { |
| ww += optimumColumns[j].width; |
| if (pt.x < ww) |
| { |
| r = items[i][j]; |
| break; |
| } |
| |
| } |
| if (r) |
| break; |
| } |
| } |
| yy += info[i].height; |
| } |
| |
| return r; |
| } |
| |
| /** |
| * A helper method to determine which item renderer is under the mouse. |
| */ |
| protected function findHeaderRenderer(pt:Point):IListItemRenderer |
| { |
| var r:IListItemRenderer; |
| var yy:Number = 0; |
| var ww:Number = 0; |
| var m:int = 0; |
| var n:int = headerItems.length; |
| var optimumColumns:Array = getOptimumColumns(); |
| for (var i:int = 0; i < n; i++) |
| { |
| if (headerItems[i].length) |
| { |
| if (pt.y < yy + headerRowInfo[i].height) |
| { |
| m = headerItems[i].length; |
| if (m == 1) |
| { |
| r = headerItems[i][0]; |
| break; |
| } |
| |
| ww = 0; |
| for (var j:int = 0; j < lockedColumnCount; j++) |
| { |
| ww += optimumColumns[j].width; |
| if (pt.x < ww) |
| { |
| r = headerItems[i][j]; |
| break; |
| } |
| |
| } |
| if (r) |
| break; |
| |
| for (j=lockedColumnCount + horizontalScrollPosition; j < m; j++) |
| { |
| ww += optimumColumns[j].width; |
| if (pt.x < ww) |
| { |
| r = headerItems[i][j]; |
| break; |
| } |
| |
| } |
| |
| } |
| } |
| yy += headerRowInfo[i].height; |
| } |
| |
| return r; |
| } |
| |
| /** |
| * @private |
| */ |
| mx_internal function getSeparators():Array |
| { |
| return separators; |
| } |
| |
| /** |
| * @private |
| */ |
| mx_internal function getLockedSeparators():Array |
| { |
| return lockedSeparators; |
| } |
| |
| /** |
| * @private |
| */ |
| private function measureItems():void |
| { |
| if (itemsNeedMeasurement) |
| { |
| itemsNeedMeasurement = false; |
| // fetch the itemRenderer so that it gets initialized |
| var obj:Object = itemRenderer; |
| if (isNaN(explicitRowHeight)) |
| { |
| if (iterator && _columns.length > 0) |
| { |
| if (!measuringObjects) |
| measuringObjects = new Dictionary(false); |
| |
| //set AdvancedDataGridBase.visibleColumns to the set of |
| //all columns |
| visibleColumns = columns; |
| columnsInvalid = true; |
| |
| var paddingTop:Number = getStyle("paddingTop"); |
| var paddingBottom:Number = getStyle("paddingBottom"); |
| |
| var data:Object = iterator.current; |
| var item:IListItemRenderer; |
| var c:AdvancedDataGridColumn; |
| var ch:Number = 0; |
| var n:int = _columns.length; |
| for (var i:int = 0; i < n; i++) |
| { |
| c = _columns[i]; |
| |
| if (!c.visible) |
| continue; |
| |
| item = getMeasuringRenderer(c, false,data); |
| setupRendererFromData(c, item, data); |
| ch = Math.max(ch, item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop); |
| } |
| |
| // unless specified otherwise, rowheight defaults to 20 |
| setRowHeight(Math.max(ch, 20)); |
| } |
| else |
| setRowHeight(20); |
| } |
| } |
| } |
| |
| /** |
| * @private |
| * Set the itemEditor instance position according to the indentation of the item it is representing. |
| */ |
| protected function layoutItemEditor():void |
| { |
| } |
| |
| /** |
| * Moves focus to the specified column header. |
| * |
| * @param columnIndex The index of the column to receive focus. |
| * If you specify an invalid column index, the method returns without moving focus. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| public function moveFocusToHeader(columnIndex:int = -1):void |
| { |
| if (!headerVisible || headerIndex != -1) |
| return; |
| |
| if (visibleColumns.length > 0) |
| { |
| if (columnIndex == -1) |
| columnIndex = visibleColumns[0].colNum; |
| |
| selectedHeaderInfo = getHeaderInfo(_columns[columnIndex]); |
| headerIndex = columnIndex; |
| selectColumnHeader(headerIndex); |
| } |
| } |
| |
| /** |
| * Selects the specified column header. |
| * |
| * @param columnNumber The index of the column to receive focus. |
| * If you specify an invalid column index, the method returns without moving focus. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function selectColumnHeader(columnNumber:int):void |
| { |
| var visibleColumnNumber:int = -1; |
| var n:int = visibleColumns.length; |
| for (var i:int = 0; i < n; i++) |
| { |
| if (visibleColumns[i].colNum == columnNumber) |
| { |
| visibleColumnNumber = i; |
| break; |
| } |
| } |
| |
| // For example, if a column header is selected but we have horizontally |
| // scrolled such that it is not visible, then we select the first visible header item |
| if (visibleColumnNumber == -1) |
| { |
| visibleColumnNumber = 0; |
| headerIndex = visibleColumns[0].colNum; |
| } |
| |
| var s:Sprite = Sprite( selectionLayer.getChildByName("headerKeyboardSelection") ); |
| // Copied from function mouseOverHandler |
| if (! s) |
| { |
| s = new FlexSprite(); |
| s.name = "headerKeyboardSelection"; |
| selectionLayer.addChild(s); |
| } |
| |
| var r:IListItemRenderer = selectedHeaderInfo.headerItem; |
| if (r) |
| { |
| var g:Graphics = s.graphics; |
| g.clear(); |
| g.beginFill( (isPressed || isKeyPressed) ? getStyle("selectionColor") : getStyle("rollOverColor") ); |
| g.drawRect(0, 0, visibleColumns[visibleColumnNumber].width, r.height+cachedPaddingTop+cachedPaddingBottom - 0.5); |
| g.endFill(); |
| |
| s.x = getAdjustedXPos(r.x); |
| s.y = r.y - cachedPaddingTop; |
| |
| // Make sure other selection is removed |
| caretIndex = -1; |
| isPressed = false; |
| selectItem(selectedHeaderInfo.headerItem, false, false); |
| } |
| } |
| |
| /** |
| * Deselects the specified column header. |
| * |
| * @param columnNumber The index of the column. |
| * If you specify an invalid column index, the method does nothing. |
| * |
| * @param completely If <code>true</code>, clear the <code>caretIndex</code> property |
| * and selects the first column header in the control. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function unselectColumnHeader(columnNumber:int, completely:Boolean=false):void |
| { |
| var s:Sprite = Sprite( selectionLayer.getChildByName("headerKeyboardSelection") ); |
| if (s) |
| selectionLayer.removeChild(s); |
| selectedHeaderInfo = null; |
| if (completely) |
| { |
| caretIndex = 0; |
| isPressed = false; |
| selectItem(listItems[caretIndex][0], false, false); |
| } |
| } |
| |
| /** |
| * Helper function to figure out if the item renderer is renderering a |
| * header. |
| * |
| * @private |
| */ |
| protected function isHeaderItemRenderer(item:IListItemRenderer):Boolean |
| { |
| // data is set to AdvancedDataGridColumn for header items |
| if (item != null && item.data is AdvancedDataGridColumn) |
| return true; |
| |
| return false; |
| } |
| |
| /** |
| * Converts an absolute column index to the corresponding index in the |
| * displayed columns. Because users can reorder columns, the |
| * absolute column index may be different from the index of the |
| * displayed column. |
| * |
| * @param columnIndex Absolute index of the column. |
| * |
| * @return The index of the column as it is currently displayed, |
| * or -1 if <code>columnIndex</code> is not found. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function absoluteToDisplayColumnIndex(columnIndex:int):int |
| { |
| var n:int = displayableColumns.length; |
| for (var i:int = 0; i < n; i++) |
| { |
| if (displayableColumns[i].colNum == columnIndex) |
| return i; |
| } |
| |
| return -1; |
| } |
| |
| /** |
| * Converts the current display column index of a column to |
| * its corresponding absolute index. |
| * Because users can reorder columns, the |
| * absolute column index may be different from the index of the |
| * displayed column. |
| * |
| * @param columnIndex Index of the column as it is currently displayed by the control. |
| * |
| * @return The absolute index of the column. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function displayToAbsoluteColumnIndex(columnIndex:int):int |
| { |
| var noColumns:int = displayableColumns.length; |
| |
| if (columnIndex >= 0 && columnIndex < noColumns) { |
| return displayableColumns[columnIndex].colNum; |
| } |
| else { |
| return -1; |
| } |
| } |
| |
| /** |
| * Converts an absolute column index to the corresponding index in the |
| * visible columns. Because users can reorder columns, the |
| * absolute column index may be different from the index of the |
| * visible column. |
| * |
| * @param columnIndex Absolute index of the column. |
| * |
| * @return The index of the column as it is currently visible, |
| * or -1 if <code>columnIndex</code> is not currently visible. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function absoluteToVisibleColumnIndex(columnIndex:int):int |
| { |
| var optimumColumns:Array = getOptimumColumns(); |
| var n:int = optimumColumns.length; |
| for (var i:int = 0; i < n; i++) |
| { |
| if (optimumColumns[i].colNum == columnIndex) |
| return i; |
| } |
| return -1; |
| } |
| |
| /** |
| * Converts the current visible column index of a column to |
| * its corresponding absolute index. |
| * Because users can reorder columns, the |
| * absolute column index may be different from the index of the |
| * visible column. |
| * |
| * @param columnIndex Index of a currently visible column in the control. |
| * |
| * @return The absolute index of the column. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function visibleToAbsoluteColumnIndex(columnIndex:int):int |
| { |
| var optimumColumns:Array = getOptimumColumns(); |
| return optimumColumns[columnIndex].colNum; |
| } |
| |
| /** |
| * Returns <code>true</code> if the specified row in a column is visible. |
| * |
| * @param columnIndex The column index. |
| * |
| * @param rowIndex A row index in the column. If omitted, the method uses the |
| * current value of the <code>verticalScrollPosition</code> property. |
| * |
| * @return <code>true</code> if the specified row in the column is visible. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function isColumnFullyVisible(columnIndex:int, rowIndex:int = -1):Boolean |
| { |
| if (rowIndex == -1) |
| rowIndex = verticalScrollPosition; |
| |
| var visibleCoords:Object = absoluteToVisibleIndices(rowIndex, columnIndex); |
| var visibleRowIndex:int = visibleCoords.rowIndex; |
| var visibleColIndex:int = visibleCoords.columnIndex; |
| |
| if (visibleRowIndex < 0) |
| return false; |
| |
| // First, check for presence in visibleColumns |
| var isFullyVisible:Boolean = (visibleColIndex != -1); |
| |
| if (isFullyVisible) |
| { |
| if (listItems.length >= 1 && visibleColumns.length >= 1) |
| { |
| var adjustedX:Number = listItems[visibleRowIndex][visibleColIndex].x; |
| if(getOptimumColumns() == displayableColumns && visibleColIndex > lockedColumnCount) |
| adjustedX = getAdjustedXPos(adjustedX); |
| |
| // Second, check if it is fully visible |
| // (a valid check if it is the last column) |
| if (adjustedX + listItems[visibleRowIndex][visibleColIndex].width |
| > listContent.width) |
| isFullyVisible = false; |
| } |
| } |
| |
| return isFullyVisible; |
| } |
| |
| /** |
| * Figure out which visible column is available at an offset from the |
| * current visible column. |
| * |
| * Use with care, because it scrolls the new column into view. |
| * |
| * @private |
| */ |
| protected function viewDisplayableColumnAtOffset(columnIndex:int, |
| offset:int, |
| rowIndex:int=-1, |
| scroll:Boolean=true) |
| :int |
| { |
| var displayColumnIndex:int = absoluteToDisplayColumnIndex(columnIndex); |
| if (displayColumnIndex == -1) |
| return -1; |
| |
| var n:int = displayableColumns.length; |
| |
| for (var newDisplayColumnIndex:int = displayColumnIndex + offset; |
| newDisplayColumnIndex >= 0 && newDisplayColumnIndex <= n-1; |
| newDisplayColumnIndex += offset) |
| { |
| if (rowIndex > -1) |
| { |
| // If rowIndex is given and item renderer is present, |
| // then it must be visible |
| var visibleCoord:Object |
| = absoluteToVisibleIndices(rowIndex, |
| displayToAbsoluteColumnIndex(newDisplayColumnIndex)); |
| var listItem:IListItemRenderer; |
| if (listItems[visibleCoord.rowIndex]) |
| listItem = listItems[visibleCoord.rowIndex][visibleCoord.columnIndex]; |
| if (listItem && !listItem.visible) |
| continue; |
| } |
| |
| var newAbsoluteColumnIndex:int = displayToAbsoluteColumnIndex(newDisplayColumnIndex); |
| if (newAbsoluteColumnIndex < 0 || newAbsoluteColumnIndex > _columns.length-1) |
| return -1; |
| |
| if (scroll) |
| { |
| if (!isColumnFullyVisible(newAbsoluteColumnIndex)) |
| scrollToViewColumn(newAbsoluteColumnIndex, columnIndex); |
| } |
| |
| return newAbsoluteColumnIndex; |
| } |
| |
| return -1; |
| } |
| |
| /** |
| * Changes the value of the <code>horizontalScrollPosition</code> property |
| * to make the specified column visible. |
| * This method is useful when all columns of the control are not currently visible. |
| * |
| * @param newColumnIndex The desired index of the column in the currently displayed columns. |
| * |
| * @param columnIndex The index of the column to display. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function scrollToViewColumn(newColumnIndex:int, columnIndex:int):void |
| { |
| var i:int, n:int; |
| if (newColumnIndex == columnIndex) |
| return; |
| |
| var newDisplayColumnIndex:int = absoluteToDisplayColumnIndex(newColumnIndex); |
| var displayColumnIndex:int = absoluteToDisplayColumnIndex(columnIndex); |
| |
| var delta:int = newDisplayColumnIndex - displayColumnIndex; |
| var newHorizontalScrollPosition:int = Math.max(0,horizontalScrollPosition + delta); |
| |
| // If moving from locked column area to unlocked column area, then |
| // change horizontal scroll position to zero so that we can bring the |
| // first unlocked column to view. |
| if (lockedColumnCount > 0 && columnIndex == lockedColumnCount-1) |
| newHorizontalScrollPosition = 0; |
| |
| var scrollEvent:ScrollEvent = new ScrollEvent(ScrollEvent.SCROLL); |
| scrollEvent.detail = ScrollEventDetail.THUMB_POSITION; |
| scrollEvent.direction = ScrollEventDirection.HORIZONTAL; |
| scrollEvent.delta = delta; |
| scrollEvent.position = newHorizontalScrollPosition; |
| dispatchEvent(scrollEvent); |
| |
| horizontalScrollPosition = newHorizontalScrollPosition; |
| } |
| |
| /** |
| * Convert an absolute row index and column index into the corresponding |
| * row index and column index of the item as it is currently displayed by the control. |
| * |
| * @param rowIndex An absolute row index. |
| * |
| * @param columnIndex An absolute column index. |
| * |
| * @return An Object containing two fields, <code>rowIndex</code> and <code>columnIndex</code>, |
| * that contain the row index and column index of the item as it is currently displayed by the control. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function absoluteToVisibleIndices(rowIndex:int, columnIndex:int):Object |
| { |
| var visibleRowIndex:int = -1; |
| var visibleColIndex:int = -1; |
| |
| // Check row display |
| if ( (rowIndex < lockedRowCount || rowIndex >= verticalScrollPosition) |
| && rowIndex <= verticalScrollPosition |
| + (listItems.length ? listItems.length - 1 : 0)) |
| { |
| if (rowIndex >= lockedRowCount && rowIndex >= verticalScrollPosition) |
| visibleRowIndex = rowIndex - verticalScrollPosition; |
| else |
| visibleRowIndex = rowIndex; |
| } |
| |
| // Check column display (optimization: calculate only if row is valid) |
| if (visibleRowIndex > -1) |
| { |
| var columnsOnScreen:Array = visibleColumns; |
| if (columnsOnScreen && columnsOnScreen.length > 0) |
| { |
| if (columnIndex >= columnsOnScreen[0].colNum |
| && columnIndex <= columnsOnScreen[columnsOnScreen.length-1].colNum) |
| { |
| if (columnIndex >= lockedColumnCount) |
| visibleColIndex = absoluteToVisibleColumnIndex(columnIndex); |
| else |
| visibleColIndex = columnIndex; |
| } |
| } |
| } |
| |
| return { |
| rowIndex : visibleRowIndex, |
| columnIndex : visibleColIndex |
| }; |
| } |
| |
| /** |
| * Returns the index of a column as it is currently displayed. |
| * This method is useful when all columns of the control are not currently visible. |
| * |
| * @param colNum Absolute index of the column. |
| * |
| * @return The index of the column as it is currently displayed, |
| * or -1 if <code>colNum</code> is not found. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function colNumToIndex(colNum:int):int |
| { |
| if (getOptimumColumns() == visibleColumns) |
| return absoluteToVisibleColumnIndex(colNum); |
| else if (getOptimumColumns() == displayableColumns) |
| return absoluteToDisplayColumnIndex(colNum); |
| else |
| return -1; |
| } |
| |
| /** |
| * Returns the column number of a currently displayed column |
| * as it is currently displayed. |
| * This method is useful when all columns of the control are not currently visible. |
| * |
| * @param columnIndex The index of the column as it is currently displayed. |
| * |
| * @return The column number of the displayed column in the control, |
| * or -1 if <code>columnIndex</code> is not found. |
| * |
| * @langversion 3.0 |
| * @playerversion Flash 9 |
| * @playerversion AIR 1.1 |
| * @productversion Flex 3 |
| */ |
| protected function indexToColNum(columnIndex:int):int |
| { |
| if (getOptimumColumns() == visibleColumns) |
| return visibleToAbsoluteColumnIndex(columnIndex); |
| else if (getOptimumColumns() == displayableColumns) |
| return displayToAbsoluteColumnIndex(columnIndex); |
| else |
| return -1; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Overridden event handlers |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| * Catches any events from the model. Optimized for editing one item. |
| * Creates columns when there are none. Inherited from list. |
| * @param event |
| */ |
| override protected function collectionChangeHandler(event:Event):void |
| { |
| //if the iterator is null that indicates we haven't been validated yet so we'll bail. |
| if (iterator == null) |
| return; |
| |
| if (event is CollectionEvent) |
| { |
| var ceEvent:CollectionEvent = CollectionEvent(event); |
| if (ceEvent.kind == CollectionEventKind.mx_internal::EXPAND) |
| { |
| //we ignore expand in list/tree |
| event.stopPropagation(); |
| } |
| if (ceEvent.kind == CollectionEventKind.UPDATE) |
| { |
| //this prevents listbase from invalidating the displaylist too early. |
| event.stopPropagation(); |
| //we only want to update the displaylist if an updated item was visible |
| //but don't have a sufficient test for that yet |
| itemsSizeChanged = true; |
| invalidateDisplayList(); |
| } |
| |
| if (ceEvent.kind == CollectionEventKind.RESET) |
| { |
| if (generatedColumns) |
| generateCols(); |
| updateSortIndexAndDirection(); |
| } |
| else if (ceEvent.kind == CollectionEventKind.REFRESH && !manualSort) |
| { |
| updateSortIndexAndDirection(); |
| } |
| else |
| { |
| // if we get a remove while editing adjust the editPosition |
| if (ceEvent.kind == CollectionEventKind.REMOVE) |
| { |
| if (editedItemPosition) |
| { |
| if (collection.length == 0) |
| { |
| if (itemEditorInstance) |
| endEdit(AdvancedDataGridEventReason.CANCELLED); |
| setEditedItemPosition(null); // nothing left to edit |
| } |
| else if (ceEvent.location <= editedItemPosition.rowIndex) |
| { |
| var curEditedItemPosition:Object = editedItemPosition; |
| |
| // if the editor is up on the item going away, cancel the session |
| if (ceEvent.location == editedItemPosition.rowIndex && itemEditorInstance) |
| endEdit(AdvancedDataGridEventReason.CANCELLED); |
| |
| if (inEndEdit) |
| _editedItemPosition = { columnIndex : editedItemPosition.columnIndex, |
| rowIndex : Math.max(0, editedItemPosition.rowIndex - ceEvent.items.length)}; |
| else |
| setEditedItemPosition({ columnIndex : curEditedItemPosition.columnIndex, |
| rowIndex : Math.max(0, curEditedItemPosition.rowIndex - ceEvent.items.length)}); |
| } |
| } |
| } |
| else if (ceEvent.kind == CollectionEventKind.REPLACE) |
| { |
| if (editedItemPosition) |
| { |
| // if the editor is up on the item going away, cancel the session |
| if (ceEvent.location == editedItemPosition.rowIndex && itemEditorInstance) |
| endEdit(AdvancedDataGridEventReason.CANCELLED); |
| } |
| } |
| } |
| } |
| |
| super.collectionChangeHandler(event); |
| |
| if (event is CollectionEvent) |
| { |
| // trace("ListBase collectionEvent"); |
| var ce:CollectionEvent = CollectionEvent(event); |
| if (ce.kind == CollectionEventKind.ADD) |
| { |
| // added first item, generate columns for it if needed |
| if (collection.length == 1) |
| if (generatedColumns) |
| generateCols(); |
| } |
| } |
| |
| // if (event.eventName != "sort" && bRowsChanged) |
| // invInitHeaders = true; |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function mouseOverHandler(event:MouseEvent):void |
| { |
| if (movingColumn) |
| return; |
| |
| if (!enabled || !selectable) |
| return; |
| |
| var r:IListItemRenderer; |
| var n:int; |
| if (enabled && headerVisible && getNumColumns() //headerItems.length |
| && !isPressed) |
| { |
| r = mouseEventToItemRenderer(event); |
| n = orderedHeadersList.length; |
| |
| var headerItem:IListItemRenderer; |
| var headerInfo:AdvancedDataGridHeaderInfo; |
| var i:int; |
| for( i = 0; i < n && r; i++) |
| { |
| headerItem = orderedHeadersList[i].headerItem; |
| if(headerItem == r) |
| { |
| headerInfo = orderedHeadersList[i]; |
| if(orderedHeadersList[i].column.sortable) |
| { |
| var s:Sprite = Sprite( |
| selectionLayer.getChildByName("headerSelection")); |
| if (!s) |
| { |
| s = new FlexSprite(); |
| s.name = "headerSelection"; |
| selectionLayer.addChild(s); |
| } |
| |
| var h:Number = r.height + cachedPaddingBottom + cachedPaddingTop; |
| var w:Number = r.getExplicitOrMeasuredWidth(); |
| var x:Number = r.x; |
| |
| //In case we have scrolled the "selection" shown need to be shifted |
| if(headerInfo.actualColNum >= lockedColumnCount) |
| { |
| x = getAdjustedXPos(r.x); |
| // In case of column grouping, it may be partially visible, so need to get the visible width as well as the |
| //x pos from which it is visible |
| if(horizontalScrollPosition > 0 && headerInfo.actualColNum - horizontalScrollPosition < lockedColumnCount) |
| { |
| var lockedWidth:Number = 0; |
| if(lockedColumnCount > 0) |
| { |
| var lastLockedInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(_columns[lockedColumnCount-1]); |
| lockedWidth = lastLockedInfo.headerItem.x + _columns[lockedColumnCount - 1].width; |
| } |
| else |
| lockedWidth = 0; |
| |
| w -= (lockedWidth - x); |
| x = lockedWidth; |
| } |
| } |
| |
| var g:Graphics = s.graphics; |
| g.clear(); |
| g.beginFill(getStyle("rollOverColor")); |
| g.drawRect(0, 0, w, h - 0.5); |
| g.endFill(); |
| |
| s.x = x; |
| s.y = r.y - cachedPaddingTop; |
| } |
| return; |
| } |
| } |
| |
| } |
| |
| if (event.buttonDown) |
| lastItemDown = r; |
| else |
| lastItemDown = null; |
| |
| super.mouseOverHandler(event); |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function mouseOutHandler(event:MouseEvent):void |
| { |
| if (movingColumn) |
| return; |
| |
| var r:IListItemRenderer; |
| var optimumColumns:Array = getOptimumColumns(); |
| var n:int; |
| if (enabled && headerVisible && listItems.length) |
| { |
| r = mouseEventToItemRenderer(event); |
| |
| if(!r) |
| { |
| n = optimumColumns.length; |
| for (var i:int = 0; i < n; i++) |
| { |
| if(optimumColumns[i].colNum == sortIndex) |
| r = getHeaderInfo(optimumColumns[i]).headerItem; |
| } |
| } |
| |
| n = orderedHeadersList.length; |
| var headerItem:IListItemRenderer; |
| for( i = 0; i < n && r; i++) |
| { |
| headerItem = orderedHeadersList[i].headerItem; |
| if(headerItem == r) |
| { |
| if(orderedHeadersList[i].column.sortable) |
| { |
| var s:Sprite = Sprite( |
| selectionLayer.getChildByName("headerSelection")); |
| if (s) |
| selectionLayer.removeChild(s); |
| } |
| return; |
| } |
| } |
| } |
| if (event.buttonDown) |
| lastItemDown = r; |
| else |
| lastItemDown = null; |
| |
| super.mouseOutHandler(event); |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function mouseDownHandler(event:MouseEvent):void |
| { |
| // trace(">>mouseDownHandler"); |
| var r:IListItemRenderer; |
| var s:Sprite; |
| r = mouseEventToItemRenderer(event); |
| |
| var optimumColumns:Array = getOptimumColumns(); |
| // if headers are visible and clickable for sorting |
| if (enabled && (sortableColumns || draggableColumns) |
| && headerVisible && hasHeaderItemsCreated()) |
| { |
| // find out if we clicked on a header |
| var n:int = orderedHeadersList.length; |
| var headerItem:IListItemRenderer; |
| for( var i:int = 0; i < n && r; i++) |
| { |
| headerItem = orderedHeadersList[i].headerItem; |
| // if we did click on a header |
| if(headerItem == r) |
| { |
| var headerInfo:AdvancedDataGridHeaderInfo = orderedHeadersList[i]; |
| // dispose the editor |
| if (itemEditorInstance) |
| endEdit(AdvancedDataGridEventReason.OTHER); |
| var c:AdvancedDataGridColumn = orderedHeadersList[i].column; |
| |
| if (sortableColumns && c.sortable) |
| { |
| lastItemDown = r; |
| s = Sprite(selectionLayer.getChildByName("headerSelection")); |
| if (!s) |
| { |
| s = new FlexSprite(); |
| s.name = "headerSelection"; |
| selectionLayer.addChild(s); |
| } |
| |
| var h:Number = r.height + cachedPaddingBottom + cachedPaddingTop; |
| var w:Number = r.getExplicitOrMeasuredWidth(); |
| var x:Number = r.x; |
| |
| //In case we have scrolled the "selection" shown need to be shifted |
| if(headerInfo.actualColNum >= lockedColumnCount) |
| { |
| x = getAdjustedXPos(r.x); |
| // In case of column grouping, it may be partially visible, so need to get the visible width as well as the |
| //x pos from which it is visible |
| if(horizontalScrollPosition > 0 && headerInfo.actualColNum - horizontalScrollPosition < lockedColumnCount) |
| { |
| var lockedWidth:Number = 0; |
| if(lockedColumnCount > 0) |
| { |
| var lastLockedInfo:AdvancedDataGridHeaderInfo = getHeaderInfo(_columns[lockedColumnCount-1]); |
| lockedWidth = lastLockedInfo.headerItem.x + _columns[lockedColumnCount - 1].width; |
| } |
| else |
| { |
| lockedWidth = 0; |
| } |
| |
| w -= (lockedWidth - x); |
| x = lockedWidth; |
| } |
| } |
| |
| var g:Graphics = s.graphics; |
| g.clear(); |
| g.beginFill(getStyle("selectionColor")); |
| g.drawRect(0, 0, w, h - 0.5); |
| g.endFill(); |
| |
| s.x = x; |
| s.y = r.y - cachedPaddingTop; |
| } |
| isPressed = true; |
| // begin column dragging |
| if (draggableColumns && isDraggingAllowed(c)) |
| { |
| startX = NaN; |
| var sbRoot:DisplayObject = systemManager.getSandboxRoot(); |
| sbRoot.addEventListener(MouseEvent.MOUSE_MOVE, columnDraggingMouseMoveHandler, true); |
| sbRoot.addEventListener(MouseEvent.MOUSE_UP, columnDraggingMouseUpHandler, true); |
| sbRoot.addEventListener(SandboxMouseEvent.MOUSE_UP_SOMEWHERE, columnDraggingMouseUpHandler); |
| systemManager.deployMouseShields(true); |
| movingColumn = c; |
| } |
| |
| return; |
| } |
| } |
| } |
| lastItemDown = null; |
| |
| var isItemEditor:Boolean = itemRendererContains(itemEditorInstance, DisplayObject(event.target)); |
| |
| // If it isn't an item renderer, or an item editor do default behavior |
| if (!isItemEditor) |
| { |
| var pos:Point; |
| if (r && r.data) |
| { |
| lastItemDown = r; |
| |
| pos = itemRendererToIndices(r); |
| |
| var bEndedEdit:Boolean = true; |
| |
| if (itemEditorInstance) |
| { |
| //for header renderers pos would be null |
| if (pos == null || displayableColumns[pos.x].editable == false) |
| bEndedEdit = endEdit(AdvancedDataGridEventReason.OTHER); |
| else |
| bEndedEdit = endEdit(editedItemPosition.rowIndex == pos.y ? |
| AdvancedDataGridEventReason.NEW_COLUMN : |
| AdvancedDataGridEventReason.NEW_ROW); |
| } |
| |
| // if we didn't end edit session, don't do default behavior (call super) |
| if (!bEndedEdit) |
| return; |
| } |
| else |
| { |
| // trace("end edit?"); |
| if (itemEditorInstance) |
| endEdit(AdvancedDataGridEventReason.OTHER); |
| } |
| |
| // Move focus out of header if mouse pressed on any list item |
| if (headerIndex != -1) |
| { |
| var pt:Point = itemRendererToIndices(r); |
| if (pt) |
| { |
| unselectColumnHeader(headerIndex, true); |
| headerIndex = -1; |
| caretIndex = pt.y; |
| } |
| } |
| |
| super.mouseDownHandler(event); |
| |
| if (r) |
| { |
| if (pos && displayableColumns[pos.x].rendererIsEditor) |
| resetDragScrolling(); |
| } |
| } |
| else |
| resetDragScrolling(); |
| // trace("<<mouseDownHandler"); |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function mouseUpHandler(event:MouseEvent):void |
| { |
| if (!collection || !collection.length) |
| return; |
| |
| var advancedDataGridEvent:AdvancedDataGridEvent; |
| var r:IListItemRenderer; |
| var n:int; |
| var i:int; |
| var pos:Point; |
| |
| r = mouseEventToItemRenderer(event); |
| |
| if (enabled && (sortableColumns || draggableColumns) |
| && collection && headerVisible && hasHeaderItemsCreated()) |
| { |
| n = orderedHeadersList.length; |
| for (i = 0; i < n; i++) |
| { |
| if (r && r == orderedHeadersList[i].headerItem) |
| { |
| var c:AdvancedDataGridColumn = orderedHeadersList[i].column; |
| if (sortableColumns && c.sortable && lastItemDown == r) |
| { |
| lastItemDown = null; |
| advancedDataGridEvent= new AdvancedDataGridEvent( |
| AdvancedDataGridEvent.HEADER_RELEASE, |
| false, true); |
| // HEADER_RELEASE event is cancelable |
| if(c.colNum == -1 || isNaN(c.colNum)) |
| advancedDataGridEvent.columnIndex = -1; |
| else |
| advancedDataGridEvent.columnIndex = c.colNum; |
| advancedDataGridEvent.dataField = c.dataField; |
| advancedDataGridEvent.itemRenderer = r; |
| advancedDataGridEvent.triggerEvent = event; |
| if (Object(r).hasOwnProperty("mouseEventToHeaderPart")) |
| advancedDataGridEvent.headerPart = Object(r).mouseEventToHeaderPart(event); |
| dispatchEvent(advancedDataGridEvent); |
| } |
| isPressed = false; |
| return; |
| } |
| } |
| } |
| |
| if (movingColumn) |
| return; |
| |
| super.mouseUpHandler(event); |
| |
| // if the item is visible, then only create item editor for it |
| if (r && r.data && r != itemEditorInstance && lastItemDown == r && r.visible |
| && isDataEditable(r.data)) |
| { |
| pos = itemRendererToIndices(r); |
| |
| if (pos && pos.y >= 0 && !dontEdit) |
| { |
| if (displayableColumns[pos.x].editable) |
| { |
| advancedDataGridEvent = new AdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_EDIT_BEGINNING, false, true); |
| // ITEM_EDIT events are cancelable |
| advancedDataGridEvent.columnIndex = displayableColumns[pos.x].colNum; |
| advancedDataGridEvent.dataField = displayableColumns[pos.x].dataField; |
| advancedDataGridEvent.rowIndex = pos.y; |
| advancedDataGridEvent.itemRenderer = r; |
| dispatchEvent(advancedDataGridEvent); |
| } |
| else |
| { |
| // if the item is not editable, set lastPosition to it anyways |
| // so future tabbing starts from there |
| lastEditedItemPosition = { columnIndex: displayableColumns[pos.x].colNum, rowIndex: pos.y }; |
| } |
| } |
| } |
| else if (lastItemDown && lastItemDown != itemEditorInstance) |
| { |
| pos = itemRendererToIndices(lastItemDown); |
| |
| if (pos && pos.y >= 0 && editable && !dontEdit) |
| { |
| if (displayableColumns[pos.x].editable) |
| { |
| advancedDataGridEvent = new AdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_EDIT_BEGINNING, false, true); |
| // ITEM_EDIT events are cancelable |
| advancedDataGridEvent.columnIndex = displayableColumns[pos.x].colNum; |
| advancedDataGridEvent.dataField = displayableColumns[pos.x].dataField; |
| advancedDataGridEvent.rowIndex = pos.y; |
| advancedDataGridEvent.itemRenderer = lastItemDown; |
| dispatchEvent(advancedDataGridEvent); |
| } |
| else |
| { |
| // if the item is not editable, set lastPosition to it any |
| // so future tabbing starts from there |
| lastEditedItemPosition = { columnIndex: pos.x, rowIndex: pos.y }; |
| } |
| } |
| } |
| |
| lastItemDown = null; |
| } |
| |
| /** |
| * @private |
| * when the grid gets focus, focus an item renderer |
| */ |
| override protected function focusInHandler(event:FocusEvent):void |
| { |
| // trace(">>DGFocusIn ", selectedIndex); |
| |
| if (losingFocus) |
| { |
| losingFocus = false; |
| // trace("losing focus via tab"); |
| // trace("<<DGFocusIn "); |
| return; |
| } |
| |
| if (editable.length) |
| { |
| addEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler); |
| addEventListener(MouseEvent.MOUSE_DOWN, mouseFocusChangeHandler); |
| } |
| |
| if (event.target != this) |
| { |
| // trace("subcomponent got focus ignoring"); |
| // trace("<<DGFocusIn "); |
| return; |
| } |
| |
| super.focusInHandler(event); |
| |
| if (editable && editable.length && !isPressed) // don't do this if we're mouse focused |
| { |
| _editedItemPosition = lastEditedItemPosition; |
| |
| var foundOne:Boolean = false; |
| |
| // start somewhere |
| if (!_editedItemPosition) |
| _editedItemPosition = { rowIndex: 0, columnIndex: 0 }; |
| |
| for (; |
| _editedItemPosition.columnIndex != _columns.length; |
| _editedItemPosition.columnIndex++) |
| { |
| // If the editedItemPosition is valid, focus it, |
| // otherwise find one. |
| if (_columns[_editedItemPosition.columnIndex].editable && |
| _columns[_editedItemPosition.columnIndex].visible) |
| { |
| var row:Array = listItems[_editedItemPosition.rowIndex]; |
| if (row && row[_editedItemPosition.columnIndex]) |
| { |
| foundOne = true; |
| break; |
| } |
| } |
| } |
| |
| // leave at last column or an RTE can occur |
| if (_editedItemPosition.columnIndex >= _columns.length) |
| _editedItemPosition.columnIndex = _columns.length - 1; |
| |
| if (foundOne) |
| { |
| // trace("setting focus", _editedItemPosition.columnIndex, _editedItemPosition.rowIndex); |
| setEditedItemPosition(_editedItemPosition); |
| } |
| |
| } |
| |
| // trace("<<DGFocusIn "); |
| } |
| |
| /** |
| * @private |
| * when the grid loses focus, close the editor |
| */ |
| override protected function focusOutHandler(event:FocusEvent):void |
| { |
| // trace(">>DGFocusOut " + itemEditorInstance + " " + event.relatedObject, event.target); |
| if (event.target == this) |
| super.focusOutHandler(event); |
| |
| // just leave if item editor is losing focus back to grid. Usually happens |
| // when someone clicks out of the editor onto a new item renderer. |
| if (event.relatedObject == this && itemRendererContains(itemEditorInstance, DisplayObject(event.target))) |
| return; |
| |
| // just leave if the cell renderer is losing focus to nothing while its editor exists. |
| // this happens when we make the cell renderer invisible as we put up the editor |
| // if the renderer can have focus. |
| if (event.relatedObject == null && itemRendererContains(editedItemRenderer, DisplayObject(event.target))) |
| return; |
| |
| // just leave if item editor is losing focus to nothing. Usually happens |
| // when someone clicks out of the textfield |
| if (event.relatedObject == null && itemRendererContains(itemEditorInstance, DisplayObject(event.target))) |
| return; |
| |
| // however, if we're losing focus to anything other than the editor or the grid |
| // hide the editor; |
| if (itemEditorInstance && (!event.relatedObject || !itemRendererContains(itemEditorInstance, event.relatedObject))) |
| { |
| // trace("call endEdit from focus out"); |
| endEdit(AdvancedDataGridEventReason.OTHER); |
| removeEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler); |
| removeEventListener(MouseEvent.MOUSE_DOWN, mouseFocusChangeHandler); |
| } |
| // trace("<<DGFocusOut " + itemEditorInstance + " " + event.relatedObject); |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function keyDownHandler(event:KeyboardEvent):void |
| { |
| if (itemEditorInstance || event.target != event.currentTarget) |
| return; |
| |
| if (headerIndex != -1) // header navigation via keyboard |
| { |
| headerNavigationHandler(event); |
| return; |
| } |
| // hit esc to move focus back to the grid itself |
| else if (event.keyCode == Keyboard.ESCAPE) |
| { |
| // no more editing |
| setEditedItemPosition(null); |
| // make sure there is nothing to jump back to |
| lastEditedItemPosition = null; |
| // lose focus |
| endEdit(AdvancedDataGridEventReason.CANCELLED); |
| return; |
| } |
| // Handle keyboard access to the header i.e. up key when in the first row |
| else if (headerVisible && selectedIndex == 0 && caretIndex == 0 |
| && event.keyCode == Keyboard.UP |
| && !event.ctrlKey && !event.shiftKey) |
| { |
| moveFocusToHeader(); |
| } |
| else if (event.keyCode == Keyboard.UP && caretIndex == 0 && selectedIndex == -1) |
| { |
| // Bug 202639 Pressing up arrow after a shift-arrow row selection should move to header |
| moveFocusToHeader(); |
| } |
| else if ( event.shiftKey |
| && (event.keyCode == Keyboard.PAGE_UP || event.keyCode == Keyboard.PAGE_DOWN) ) |
| { |
| moveSelectionHorizontally(event.keyCode, event.shiftKey, event.ctrlKey); |
| } |
| |
| if (event.keyCode != Keyboard.SPACE) |
| { |
| super.keyDownHandler(event); |
| } |
| else if (caretIndex != -1) |
| { |
| moveSelectionVertically(event.keyCode, event.shiftKey, event.ctrlKey); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function keyUpHandler(event:KeyboardEvent):void |
| { |
| if (isKeyPressed && headerIndex != -1) |
| { |
| isKeyPressed = false; |
| selectedHeaderInfo = getHeaderInfo(_columns[headerIndex]); |
| selectColumnHeader(headerIndex); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| override protected function mouseWheelHandler(event:MouseEvent):void |
| { |
| if (itemEditorInstance) |
| endEdit(AdvancedDataGridEventReason.OTHER); |
| |
| super.mouseWheelHandler(event); |
| } |
| |
| /** |
| * @private |
| * if some drags from the same row as an editor we can be left |
| * with updates disabled |
| */ |
| override protected function dragStartHandler(event:DragEvent):void |
| { |
| if (collectionUpdatesDisabled) |
| { |
| collection.enableAutoUpdate(); |
| collectionUpdatesDisabled = false; |
| } |
| super.dragStartHandler(event); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Event handlers |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * @private |
| */ |
| private function columnResizeMouseOverHandler(event:MouseEvent):void |
| { |
| if (!enabled || !resizableColumns) |
| return; |
| var target:DisplayObject = DisplayObject(event.target); |
| var index:int = target.parent.getChildIndex(target); |
| var optimumColumns:Array = getOptimumColumns(); |
| if (index < 0 || index >= optimumColumns.length) |
| return; |
| if (!optimumColumns[index].resizable) |
| return; |
| |
| // hide the mouse, attach and show the cursor |
| var stretchCursorClass:Class = getStyle("stretchCursor"); |
| resizeCursorID = cursorManager.setCursor(stretchCursorClass, |
| CursorManagerPriority.HIGH); |
| } |
| |
| /** |
| * @private |
| */ |
| private function columnResizeMouseOutHandler(event:MouseEvent):void |
| { |
| if (!enabled || !resizableColumns) |
| return; |
| |
| var target:DisplayObject = DisplayObject(event.target); |
| var parent:DisplayObjectContainer = target.parent; |
| if (!parent) |
| return; |
| var index:int = parent.getChildIndex(target); |
| var optimumColumns:Array = getOptimumColumns(); |
| if (index < 0 || index >= optimumColumns.length) |
| return; |
| if (!optimumColumns[index] || !optimumColumns[index].resizable) |
| return; |
| |
| if (resizeCursorID != CursorManager.NO_CURSOR) { |
| cursorManager.removeCursor(resizeCursorID); |
| resizeCursorID = CursorManager.NO_CURSOR; |
| } |
| } |
| |
| /** |
| * @private |
| * Indicates where the right side of a resized column appears. |
| */ |
| private function columnResizeMouseDownHandler(event:MouseEvent):void |
| { |
| if (!enabled || !resizableColumns) |
| return; |
| |
| var target:DisplayObject = DisplayObject(event.target); |
| var parent:DisplayObjectContainer = target.parent; |
| if (!parent) |
| return; |
| var index:int = parent.getChildIndex(target); |
| |
| // If the separator is not in locked region, column index need to be adjusted |
| if (lockedColumnCount > 0 && |
| parent == UIComponent(getLines().getChildByName("header"))) |
| index += (lockedColumnCount - 1); |
| |
| var optimumColumns:Array = getOptimumColumns(); |
| if (index < 0 || index >= optimumColumns.length) |
| return; |
| if (!optimumColumns[index] || !optimumColumns[index].resizable) |
| return; |
| |
| if (itemEditorInstance) |
| endEdit(AdvancedDataGridEventReason.OTHER); |
| |
| startX = DisplayObject(event.target).x; |
| lastPt = new Point(event.stageX, event.stageY); |
| lastPt = listContent.globalToLocal(lastPt); |
| |
| /* var n:int = separators.length; |
| for (var i:int = 0; i < n; i++) |
| { |
| if (separators[i] == event.target) |
| { |
| resizingColumn = optimumColumns[i]; |
| break; |
| } |
| } |
| |
| if (!resizingColumn) |
| return; |
| */ |
| |
| resizingColumn = optimumColumns[index]; |
| var headerItem:IListItemRenderer = getHeaderInfo(optimumColumns[index]).headerItem; |
| |
| if (index > lockedColumnCount) |
| { |
| minX = getAdjustedXPos(headerItem.x); |
| startX = getAdjustedXPos(startX); |
| } |
| else |
| { |
| minX = headerItem.x; |
| } |
| |
| minX += resizingColumn.minWidth; |
| isPressed = true; |
| |
| var sbRoot:DisplayObject = systemManager.getSandboxRoot(); |
| sbRoot.addEventListener(MouseEvent.MOUSE_MOVE, columnResizingHandler, true); |
| sbRoot.addEventListener(MouseEvent.MOUSE_UP, columnResizeMouseUpHandler, true); |
| sbRoot.addEventListener(SandboxMouseEvent.MOUSE_UP_SOMEWHERE, columnResizeMouseUpHandler); |
| systemManager.deployMouseShields(true); |
| |
| var resizeSkinClass:Class = getStyle("columnResizeSkin"); |
| resizeGraphic = new resizeSkinClass(); |
| listContent.addChild(DisplayObject(resizeGraphic)); |
| |
| var pt:Point = new Point(event.stageX, event.stageY); |
| pt = listContent.globalToLocal(pt); |
| |
| resizeGraphic.move(pt.x, target.y); |
| resizeGraphic.setActualSize(resizeGraphic.measuredWidth, |
| unscaledHeight-target.y); |
| } |
| |
| /** |
| * @private |
| */ |
| private function columnResizingHandler(event:MouseEvent):void |
| { |
| if (!MouseEvent(event).buttonDown) |
| { |
| columnResizeMouseUpHandler(event); |
| // return from here, as the resizingColumn |
| // set to null. |
| return; |
| } |
| |
| var vsw:int = verticalScrollBar ? verticalScrollBar.width : 0; |
| |
| var pt:Point = new Point(event.stageX, event.stageY); |
| pt = listContent.globalToLocal(pt); |
| lastPt = pt; |
| |
| var separatorWidth:Number = 0; |
| if (lockedSeparators && lockedSeparators.length > 0) |
| separatorWidth = lockedSeparators[0].width; |
| else if (separators && separators.length > 0) |
| separatorWidth = separators[0].width; |
| |
| // substract the separators width, |
| // so that separator will be visible after column resizing |
| // TODO - we should substract the resized column separatos's width |
| var maxWidth:Number = unscaledWidth - separatorWidth - vsw ; |
| var index:int; |
| if (getOptimumColumns() == visibleColumns) |
| index = absoluteToVisibleColumnIndex(resizingColumn.colNum); |
| else |
| index = absoluteToDisplayColumnIndex(resizingColumn.colNum); |
| |
| resizeGraphic.move(Math.min(Math.max(minX, pt.x), maxWidth), resizeGraphic.y); |
| } |
| |
| /** |
| * @private |
| * Determines how much to resize the column. |
| */ |
| private function columnResizeMouseUpHandler(event:Event):void |
| { |
| if (!enabled || !resizableColumns) |
| return; |
| |
| isPressed = false; |
| |
| var sbRoot:DisplayObject = systemManager.getSandboxRoot(); |
| sbRoot.removeEventListener(MouseEvent.MOUSE_MOVE, columnResizingHandler, true); |
| sbRoot.removeEventListener(MouseEvent.MOUSE_UP, columnResizeMouseUpHandler, true); |
| sbRoot.removeEventListener(SandboxMouseEvent.MOUSE_UP_SOMEWHERE, columnResizeMouseUpHandler); |
| systemManager.deployMouseShields(false); |
| |
| listContent.removeChild(DisplayObject(resizeGraphic)); |
| |
| if (resizeCursorID != CursorManager.NO_CURSOR) { |
| cursorManager.removeCursor(resizeCursorID); |
| resizeCursorID = CursorManager.NO_CURSOR; |
| } |
| |
| var c:AdvancedDataGridColumn = resizingColumn; |
| resizingColumn = null; |
| |
| // need to find the visible column index here. |
| // var n:int = displayableColumns.length; |
| // var i:int; |
| // for (i = 0; i < n; i++) |
| // { |
| // if (c == displayableColumns[i]) |
| // break; |
| // } |
| // if (i >= displayableColumns.length) |
| // return; |
| |
| var vsw:int = verticalScrollBar ? verticalScrollBar.width : 0; |
| |
| var mouseEvent:MouseEvent = event as MouseEvent; |
| |
| var pt:Point; |
| |
| if (mouseEvent) |
| { |
| pt = new Point(mouseEvent.stageX, mouseEvent.stageY); |
| pt = listContent.globalToLocal(pt); |
| } |
| else |
| { |
| pt = lastPt; |
| } |
| |
| var separatorWidth:Number = 0; |
| if (lockedSeparators && lockedSeparators.length > 0) |
| separatorWidth = lockedSeparators[0].width; |
| else if (separators && separators.length > 0) |
| separatorWidth = separators[0].width; |
| |
| // substract the separators width, |
| // so that separator will be visible after column resizing |
| // TODO - we should substract the resized column separatos's width |
| var maxWidth:Number = unscaledWidth - separatorWidth - vsw ; |
| |
| // resize the column |
| var widthChange:Number = Math.min(Math.max(minX, pt.x), maxWidth) - startX; |
| resizeColumn(c.colNum, Math.floor(c.width + widthChange)); |
| |
| // event |
| var advancedDataGridEvent:AdvancedDataGridEvent = |
| new AdvancedDataGridEvent(AdvancedDataGridEvent.COLUMN_STRETCH); |
| advancedDataGridEvent.columnIndex = c.colNum; |
| advancedDataGridEvent.dataField = c.dataField; |
| advancedDataGridEvent.localX = pt.x; |
| dispatchEvent(advancedDataGridEvent); |
| } |
| |
| /** |
| * @private |
| */ |
| private function editorMouseDownHandler(event:Event):void |
| { |
| if(event is MouseEvent && owns(DisplayObject(event.target))) |
| return; |
| |
| endEdit(AdvancedDataGridEventReason.OTHER); |
| } |
| |
| /** |
| * @private |
| */ |
| protected function editorKeyDownHandler(event:KeyboardEvent):void |
| { |
| // ESC just kills the editor, no new data |
| if (event.keyCode == Keyboard.ESCAPE) |
| { |
| endEdit(AdvancedDataGridEventReason.CANCELLED); |
| } |
| else if (event.ctrlKey && event.charCode == 46) |
| { // Check for Ctrl-. |
| endEdit(AdvancedDataGridEventReason.CANCELLED); |
| } |
| else if (event.charCode == Keyboard.ENTER && event.keyCode != 229) |
| { |
| // multiline editors can take the enter key. |
| if (_columns[_editedItemPosition.columnIndex].editorUsesEnterKey) |
| return; |
| |
| // Enter edits the item, moves down a row |
| // The 229 keyCode is for IME compatability. When entering an IME expression, |
| // the enter key is down, but the keyCode is 229 instead of the enter key code. |
| // Thanks to Yukari for this little trick... |
| if (endEdit(AdvancedDataGridEventReason.NEW_ROW) && !dontEdit) |
| findNextEnterItemRenderer(event); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| private function editorStageResizeHandler(event:Event):void |
| { |
| if (event.target is DisplayObjectContainer && |
| DisplayObjectContainer(event.target).contains(this)) |
| endEdit(AdvancedDataGridEventReason.OTHER); |
| } |
| |
| /** |
| * @private |
| * This gets called when the tab key is hit. |
| */ |
| private function mouseFocusChangeHandler(event:MouseEvent):void |
| { |
| // trace("mouseFocus handled by " + this); |
| |
| if (itemEditorInstance && |
| !event.isDefaultPrevented() && |
| itemRendererContains(itemEditorInstance, DisplayObject(event.target))) |
| { |
| event.preventDefault(); |
| } |
| } |
| |
| /** |
| * @private |
| * This gets called when the tab key is hit. |
| */ |
| private function keyFocusChangeHandler(event:FocusEvent):void |
| { |
| // trace("tabHandled by " + this); |
| |
| if (event.keyCode == Keyboard.TAB && |
| ! event.isDefaultPrevented() && |
| findNextItemRenderer(event.shiftKey)) |
| { |
| event.preventDefault(); |
| } |
| } |
| |
| /** |
| * @private |
| * Hides the itemEditorInstance. |
| */ |
| private function itemEditorFocusOutHandler(event:FocusEvent):void |
| { |
| // trace("itemEditorFocusOut " + event.relatedObject); |
| if (event.relatedObject && contains(event.relatedObject)) |
| return; |
| |
| // ignore textfields losing focus on mousedowns |
| if (!event.relatedObject) |
| return; |
| |
| // trace("endEdit from itemEditorFocusOut"); |
| if (itemEditorInstance) |
| endEdit(AdvancedDataGridEventReason.OTHER); |
| } |
| |
| /** |
| * @private |
| */ |
| private function itemEditorItemEditBeginningHandler(event:AdvancedDataGridEvent):void |
| { |
| // trace("itemEditorItemEditBeginningHandler"); |
| if (!event.isDefaultPrevented()) |
| setEditedItemPosition({columnIndex: event.columnIndex, rowIndex: event.rowIndex}); |
| else if (!itemEditorInstance) |
| { |
| _editedItemPosition = null; |
| setFocus(); |
| } |
| } |
| |
| /** |
| * @private |
| * focus an item renderer in the grid - harder than it looks |
| */ |
| private function itemEditorItemEditBeginHandler(event:AdvancedDataGridEvent):void |
| { |
| // weak reference for deactivation |
| if (root) |
| systemManager.addEventListener(Event.DEACTIVATE, deactivateHandler, false, 0, true); |
| |
| // if not prevented and if data is not null (might be from dataservices) |
| if (!event.isDefaultPrevented() && listItems[actualRowIndex][actualColIndex].data != null) |
| { |
| createItemEditor(event.columnIndex, event.rowIndex); |
| |
| if (editedItemRenderer is IDropInListItemRenderer && itemEditorInstance is IDropInListItemRenderer) |
| IDropInListItemRenderer(itemEditorInstance).listData = IDropInListItemRenderer(editedItemRenderer).listData; |
| // if rendererIsEditor, don't apply the data as the data may have already changed in some way. |
| // This can happen if clicking on a checkbox rendererIsEditor as the checkbox will try to change |
| // its value as we try to stuff in an old value here. |
| if (!_columns[event.columnIndex].rendererIsEditor) |
| itemEditorInstance.data = editedItemRenderer.data; |
| |
| if (itemEditorInstance is IInvalidating) |
| IInvalidating(itemEditorInstance).validateNow(); |
| |
| if (itemEditorInstance is IIMESupport) |
| IIMESupport(itemEditorInstance).imeMode = |
| (_columns[event.columnIndex].imeMode == null) ? _imeMode : _columns[event.columnIndex].imeMode; |
| |
| var fm:IFocusManager = focusManager; |
| // trace("setting focus to item editor"); |
| if (itemEditorInstance is IFocusManagerComponent) |
| fm.setFocus(IFocusManagerComponent(itemEditorInstance)); |
| fm.defaultButtonEnabled = false; |
| |
| var itemFocusInEvent:AdvancedDataGridEvent = |
| new AdvancedDataGridEvent(AdvancedDataGridEvent.ITEM_FOCUS_IN); |
| itemFocusInEvent.columnIndex = _editedItemPosition.columnIndex; |
| itemFocusInEvent.rowIndex = _editedItemPosition.rowIndex; |
| itemFocusInEvent.itemRenderer = itemEditorInstance; |
| dispatchEvent(itemFocusInEvent); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| private function itemEditorItemEditEndHandler(event:AdvancedDataGridEvent):void |
| { |
| if (!event.isDefaultPrevented()) |
| { |
| var bChanged:Boolean = false; |
| |
| if (event.reason == AdvancedDataGridEventReason.NEW_COLUMN) |
| { |
| if (!collectionUpdatesDisabled) |
| { |
| collection.disableAutoUpdate(); |
| collectionUpdatesDisabled = true; |
| } |
| } |
| else |
| { |
| if (collectionUpdatesDisabled) |
| { |
| collection.enableAutoUpdate(); |
| collectionUpdatesDisabled = false; |
| } |
| } |
| |
| if (itemEditorInstance && event.reason != AdvancedDataGridEventReason.CANCELLED) |
| { |
| var newData:Object = itemEditorInstance[_columns[event.columnIndex].editorDataField]; |
| var property:String = _columns[event.columnIndex].dataField; |
| var data:Object = event.itemRenderer.data; |
| var typeInfo:String = ""; |
| for each(var variable:XML in describeType(data).variable) |
| { |
| if (property == variable.@name.toString()) |
| { |
| typeInfo = variable.@type.toString(); |
| break; |
| } |
| } |
| |
| if (typeInfo == "String") |
| { |
| if (!(newData is String)) |
| newData = newData.toString(); |
| } |
| else if (typeInfo == "uint") |
| { |
| if (!(newData is uint)) |
| newData = uint(newData); |
| } |
| else if (typeInfo == "int") |
| { |
| if (!(newData is int)) |
| newData = int(newData); |
| } |
| else if (typeInfo == "Number") |
| { |
| if (!(newData is int)) |
| newData = Number(newData); |
| } |
| if (data[property] != newData) |
| { |
| bChanged = true; |
| data[property] = newData; |
| } |
| if (bChanged && !(data is IPropertyChangeNotifier)) |
| { |
| collection.itemUpdated(data, property); |
| } |
| if (event.itemRenderer is IDropInListItemRenderer) |
| { |
| var listData:AdvancedDataGridListData = AdvancedDataGridListData(IDropInListItemRenderer(event.itemRenderer).listData); |
| listData.label = _columns[event.columnIndex].itemToLabel(data); |
| IDropInListItemRenderer(event.itemRenderer).listData = listData; |
| } |
| event.itemRenderer.data = data; |
| } |
| } |
| else |
| { |
| if (event.reason != AdvancedDataGridEventReason.OTHER) |
| { |
| if (itemEditorInstance && _editedItemPosition) |
| { |
| // edit session is continued so restore focus and selection |
| if (selectedIndex != _editedItemPosition.rowIndex) |
| selectedIndex = _editedItemPosition.rowIndex; |
| var fm:IFocusManager = focusManager; |
| // trace("setting focus to itemEditorInstance", selectedIndex); |
| if (itemEditorInstance is IFocusManagerComponent) |
| fm.setFocus(IFocusManagerComponent(itemEditorInstance)); |
| } |
| } |
| } |
| |
| if (event.reason == AdvancedDataGridEventReason.OTHER || !event.isDefaultPrevented()) |
| { |
| if (_editedItemPosition |
| && event.rowIndex == _editedItemPosition.rowIndex |
| && event.columnIndex == _editedItemPosition.columnIndex) |
| destroyItemEditor(); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| protected function headerReleaseHandler(event:AdvancedDataGridEvent):void |
| { |
| if (! event.isDefaultPrevented()) |
| { |
| if (itemEditorInstance) |
| endEdit(AdvancedDataGridEventReason.OTHER); |
| |
| var advancedDataGridEvent:AdvancedDataGridEvent = |
| new AdvancedDataGridEvent(AdvancedDataGridEvent.SORT, false, true); |
| |
| advancedDataGridEvent.columnIndex = event.columnIndex; |
| advancedDataGridEvent.dataField = event.dataField; |
| advancedDataGridEvent.triggerEvent = event.triggerEvent; |
| if (event.triggerEvent) |
| { |
| var mouseEvent:MouseEvent = event.triggerEvent as MouseEvent; |
| if (mouseEvent) |
| { |
| advancedDataGridEvent.multiColumnSort = mouseEvent.ctrlKey; |
| advancedDataGridEvent.removeColumnFromSort = mouseEvent.shiftKey; |
| } |
| } |
| |
| dispatchEvent(advancedDataGridEvent); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| protected function sortHandler(event:AdvancedDataGridEvent):void |
| { |
| var columnName:String = event.dataField; |
| var columnNumber:int = event.columnIndex; |
| |
| if (!sortableColumns || !_columns[columnNumber].sortable) |
| return; |
| |
| //In case there is no dataField we will use the unique column uid to identify if the column is sorted |
| if (columnName == null) |
| columnName = itemToUID(_columns[columnNumber]); |
| |
| // If normal click for single column sort |
| // or |
| // If ctrl+click when there is no previous sorting |
| if (!event.multiColumnSort) |
| { |
| if (collection.sort && collection.sort.fields.length == 1 |
| && (columnName && findSortField(columnName) > -1)) |
| { |
| // 1. Flipping order of single column sort |
| // |
| // Not allowed in default UI. You can't flip the sort order of a single |
| // column sort using the header text part (i.e. multiColumnSort==false). |
| // You can only flip by clicking on the icon part |
| // (i.e. multiColumnSort==true), see below. |
| if (sortExpertMode == true) |
| sortDirection = flipSortOrder(columnName, columnNumber, collection); |
| } |
| else |
| { |
| // 2. Single column sort |
| collection.sort = null; |
| addSortField(columnName, columnNumber, collection); |
| } |
| } |
| else |
| { |
| if (event.removeColumnFromSort) |
| { |
| removeSortField(columnName, columnNumber, collection); |
| } |
| // Ctrl+click without any previous sort is same as single column sort |
| // Or New column added to multi column sort |
| else if (findSortField(columnName) == -1) |
| { |
| addSortField(columnName, columnNumber, collection); |
| } |
| else if (findSortField(columnName) > -1) // Flipping order in multi column sort |
| { |
| if (collection.sort.fields.length == 1) |
| { |
| // 4. Flipping the order of a column in single column sort |
| sortDirection = flipSortOrder(columnName, columnNumber, collection); |
| } |
| else |
| { |
| // 5. Flipping the order of a column in multi column sort |
| // descending <-> ascending |
| flipSortOrder(columnName, columnNumber, collection); |
| sortDirection = null; |
| } |
| } |
| } |
| |
| collection.refresh(); |
| |
| // If navigating header via keyboard, and you mouse click on some |
| // other header to sort it, then move the keyboard navigation focus |
| // to that column header. |
| if (headerIndex != -1) |
| { |
| selectedHeaderInfo = getHeaderInfo(_columns[event.columnIndex]); |
| headerIndex = event.columnIndex; |
| selectColumnHeader(headerIndex); |
| } |
| |
| invalidateHeaders(); |
| } |
| |
| /** |
| * @private |
| */ |
| private function deactivateHandler(event:Event):void |
| { |
| // if stage losing activation, set focus to DG so when we get it back |
| // we popup an editor again |
| if (itemEditorInstance) |
| { |
| endEdit(AdvancedDataGridEventReason.OTHER); |
| losingFocus = true; |
| setFocus(); |
| } |
| } |
| |
| /** |
| * @private |
| */ |
| protected function headerNavigationHandler(event:KeyboardEvent):void |
| { |
| if (headerIndex == -1) |
| return; |
| |
| // If rtl layout, need to swap LEFT and RIGHT so correct action |
| // is done. |
| var keyCode:uint = mapKeycodeForLayoutDirection(event); |
| |
| var newColumnIndex:int; |
| |
| if (keyCode == Keyboard.DOWN) |
| { |
| unselectColumnHeader(headerIndex, true); |
| headerIndex = -1; |
| } |
| else if (keyCode == Keyboard.LEFT) |
| { |
| newColumnIndex = viewDisplayableColumnAtOffset(headerIndex, -1); |
| if (newColumnIndex != -1) |
| { |
| unselectColumnHeader(headerIndex); |
| |
| selectedHeaderInfo = getHeaderInfo(_columns[newColumnIndex]); |
| headerIndex = newColumnIndex; |
| selectColumnHeader(headerIndex); |
| } |
| } |
| else if (keyCode == Keyboard.RIGHT) |
| { |
| newColumnIndex = viewDisplayableColumnAtOffset(headerIndex, +1); |
| if (newColumnIndex != -1) |
| { |
| unselectColumnHeader(headerIndex); |
| selectedHeaderInfo = getHeaderInfo(_columns[newColumnIndex]); |
| headerIndex = newColumnIndex; |
| selectColumnHeader(headerIndex); |
| } |
| } |
| else if (keyCode == Keyboard.SPACE) |
| { |
| if (sortableColumns && _columns[headerIndex].sortable) |
| { |
| isKeyPressed = true; |
| selectedHeaderInfo = getHeaderInfo(_columns[headerIndex]); |
| selectColumnHeader(headerIndex); |
| |
| var advancedDataGridEvent:AdvancedDataGridEvent = |
| new AdvancedDataGridEvent(AdvancedDataGridEvent.SORT, false, true); |
| |
| advancedDataGridEvent.columnIndex = headerIndex; |
| advancedDataGridEvent.dataField = _columns[headerIndex].dataField; |
| advancedDataGridEvent.multiColumnSort = event.ctrlKey; |
| advancedDataGridEvent.removeColumnFromSort = event.shiftKey; |
| |
| dispatchEvent(advancedDataGridEvent); |
| } |
| } |
| // horizontal scrolling when focus is on header |
| else if ( event.shiftKey |
| && (keyCode == Keyboard.PAGE_UP |
| || keyCode == Keyboard.PAGE_DOWN) ) |
| { |
| moveSelectionHorizontally(keyCode, event.shiftKey, event.ctrlKey); |
| } |
| |
| event.stopPropagation(); |
| } |
| } |
| |
| } |