blob: 57fb41d5fae3c544683b56b6a764c73d077ccd36 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// 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.dataGridClasses
{
import flash.display.DisplayObject;
import flash.display.GradientType;
import flash.display.Graphics;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import mx.controls.DataGrid;
import mx.controls.listClasses.IDropInListItemRenderer;
import mx.controls.listClasses.IListItemRenderer;
import mx.core.EdgeMetrics;
import mx.core.FlexSprite;
import mx.core.IFlexDisplayObject;
import mx.core.IUIComponent;
import mx.core.LayoutDirection;
import mx.core.UIComponent;
import mx.core.UIComponentGlobals;
import mx.core.mx_internal;
import mx.effects.easing.Back;
import mx.events.DataGridEvent;
import mx.events.SandboxMouseEvent;
import mx.managers.CursorManager;
import mx.managers.CursorManagerPriority;
import mx.skins.halo.DataGridColumnDropIndicator;
import mx.styles.ISimpleStyleClient;
import mx.styles.StyleManager;
use namespace mx_internal;
/**
* The DataGridHeader class defines the default header
* renderer for a DataGrid control.
* By default, the header renderer
* draws the text associated with each header in the list, and an optional
* sort arrow (if sorted by that column).
*
* @see mx.controls.DataGrid
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public class DataGridHeader extends DataGridHeaderBase
{
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function DataGridHeader()
{
super();
addEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler);
addEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler);
addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
}
/**
* @private
* Additional affordance given to header separators.
*/
private var separatorAffordance:Number = 3;
/**
* a function to clear selections
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override mx_internal function clearSelectionLayer():void
{
while (selectionLayer.numChildren > 0)
{
selectionLayer.removeChildAt(0);
}
}
/**
* The DataGrid control associated with this renderer.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected var dataGrid:DataGrid;
/**
* An Array of header renderer instances.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected var headerItems:Array = [];
/**
* The cached header height, in pixels.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected var cachedHeaderHeight:Number = 0;
/**
* The cached padding for the bottom of the renderer, in pixels.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected var cachedPaddingBottom:Number = 0;
/**
* The cached padding for the top of the renderer, in pixels.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected var cachedPaddingTop:Number = 0;
/**
* Whether we need the separator on the far right
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var needRightSeparator:Boolean = false;
/**
* Whether we need the separator events on the far right
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var needRightSeparatorEvents:Boolean = false;
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override protected function createChildren():void
{
dataGrid = parent as DataGrid;
// this is the layer where we draw selection indicators
selectionLayer = new UIComponent();
addChild(selectionLayer);
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override protected function measure():void
{
super.measure();
var calculatedHeight:Number = dataGrid.calculateHeaderHeight();
cachedHeaderHeight = dataGrid._explicitHeaderHeight ? dataGrid.headerHeight : calculatedHeight;
cachedPaddingBottom = getStyle("paddingBottom");
cachedPaddingTop = getStyle("paddingTop");
measuredHeight = cachedHeaderHeight;
var ww:Number = 0;
var n:int = visibleColumns ? visibleColumns.length : 0;
for (var i:int = 0; i < n; i++)
ww += visibleColumns[i].preferredWidth;
measuredWidth = ww;
}
/**
* @inheritDoc
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override protected function updateDisplayList(w:Number, h:Number):void
{
allowItemSizeChangeNotification = false;
// make new ones
var cols:Array = visibleColumns;
if (headerItemsChanged && (cols && cols.length > 0 || dataGrid.headerVisible))
{
headerItemsChanged = false;
var xx:Number = 0;
var yy:Number = 0;
var hh:Number = 0;
var ww:Number = 0;
var rh:Number;
var colNum:int = 0; // visible columns compensate for firstCol offset
var rowData:DataGridListData;
var item:IListItemRenderer;
var extraItem:IListItemRenderer;
var data:Object;
var uid:String;
var c:DataGridColumn;
var maxHeaderHeight:Number = 0;
// copy current list of renderers
var oldHeaderItems:Array = headerItems.slice();
while (cols && colNum < cols.length)
{
c = cols[colNum];
item = c.cachedHeaderRenderer;
if (!item)
{
item = dataGrid.createColumnItemRenderer(c, true, c);
item.styleName = c;
c.cachedHeaderRenderer = item;
}
rowData = new DataGridListData((c.headerText != null) ? c.headerText : c.dataField,
c.dataField, colNum, uid, dataGrid, 0);
if (item is IDropInListItemRenderer)
IDropInListItemRenderer(item).listData = rowData;
item.data = c;
item.visible = true;
// if it isn't parented by us, add it (could be in lockedColumns header)
if (item.parent != this)
addChild(DisplayObject(item));
headerItems[colNum] = item;
// set prefW so we can compute prefH
item.explicitWidth = ww = c.width;
UIComponentGlobals.layoutManager.validateClient(item, true);
// but size it regardless of what prefW is
rh = item.getExplicitOrMeasuredHeight();
item.setActualSize(ww, dataGrid._explicitHeaderHeight ?
cachedHeaderHeight - cachedPaddingTop - cachedPaddingBottom : rh);
item.move(xx, yy + cachedPaddingTop);
xx += ww;
colNum++;
hh = Math.ceil(Math.max(hh, dataGrid._explicitHeaderHeight ?
cachedHeaderHeight : rh + cachedPaddingBottom + cachedPaddingTop));
maxHeaderHeight = Math.max(maxHeaderHeight, dataGrid._explicitHeaderHeight ?
cachedHeaderHeight - cachedPaddingTop - cachedPaddingBottom : rh);
}
// expand all headers to be of maximum height
for (var i:int = 0; i < headerItems.length; i++)
headerItems[i].setActualSize(headerItems[i].width, maxHeaderHeight);
for (i = 0; i < oldHeaderItems.length; i++)
{
item = oldHeaderItems[i];
if (item && headerItems.indexOf(item) == -1)
{
// if it is parented by us, remove it (could be in lockedColumns header)
if (item.parent == this)
removeChild(DisplayObject(item));
}
}
while (headerItems.length > colNum)
{
// remove extra columns
extraItem = headerItems.pop();
// if it is parented by us, remove it (could be in lockedColumns header)
if (extraItem.parent == this)
{
// make sure it isn't already in there elsewhere
if (headerItems.indexOf(extraItem) == -1)
removeChild(DisplayObject(extraItem));
}
}
}
var headerBG:UIComponent =
UIComponent(getChildByName("headerBG"));
if (headerBGSkinChanged)
{
headerBGSkinChanged = false;
if (headerBG)
removeChild(headerBG);
headerBG = null;
}
if (!headerBG)
{
headerBG = new UIComponent();
headerBG.name = "headerBG";
addChildAt(DisplayObject(headerBG), 0);
var headerBGSkinClass:Class = getStyle("headerBackgroundSkin");
var headerBGSkin:IFlexDisplayObject = new headerBGSkinClass();
if (headerBGSkin is ISimpleStyleClient)
ISimpleStyleClient(headerBGSkin).styleName = this;
headerBG.addChild(DisplayObject(headerBGSkin));
}
if (dataGrid.headerVisible)
{
headerBG.visible = true;
drawHeaderBackgroundSkin(IFlexDisplayObject(headerBG.getChildAt(0)));
dataGrid._drawSeparators();
}
else
{
headerBG.visible = false;
dataGrid._clearSeparators();
}
dataGrid._placeSortArrow();
allowItemSizeChangeNotification = true;
}
mx_internal function _drawHeaderBackground(headerBG:UIComponent):void
{
drawHeaderBackground(headerBG);
}
/**
* Draws the background of the headers into the given
* UIComponent. The graphics drawn may be scaled horizontally
* if the component's width changes or this method will be
* called again to redraw at a different width and/or height
*
* @param headerBG A UIComponent that will contain the header
* background graphics.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function drawHeaderBackground(headerBG:UIComponent):void
{
var tot:Number = width;
var hh:Number = cachedHeaderHeight;
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
{
headerBGSkin.setActualSize(unscaledWidth, Math.ceil(cachedHeaderHeight));
}
mx_internal function _clearSeparators():void
{
clearSeparators();
}
/**
* Removes column header separators that the user normally uses
* to resize columns.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function clearSeparators():void
{
if (!separators)
return;
var lines:Sprite = Sprite(getChildByName("lines"));
while (lines.numChildren)
{
lines.removeChildAt(lines.numChildren - 1);
separators.pop();
}
}
mx_internal function _drawSeparators():void
{
drawSeparators();
}
/**
* Creates and displays the column header separators that the user
* normally uses to resize columns. This implementation uses
* the same Sprite as the lines and column backgrounds and adds
* instances of the <code>headerSeparatorSkin</code> and attaches mouse
* listeners to them in order to know when the user wants
* to resize a column.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function drawSeparators():void
{
if (!separators)
separators = [];
var lines:UIComponent = UIComponent(getChildByName("lines"));
if (!lines)
{
lines = new UIComponent();
lines.name = "lines";
addChild(lines);
}
else
setChildIndex(lines, numChildren - 1);
// required to deal with some 2.x clipping behavior
lines.scrollRect = new Rectangle(0, 0, unscaledWidth, unscaledHeight + 1);
if (headerSepSkinChanged)
{
headerSepSkinChanged = false;
clearSeparators();
}
var n:int = visibleColumns ? visibleColumns.length : 0;
if (!needRightSeparator && n > 0)
n--;
for (var i:int = 0; i < n; i++)
{
var sep:UIComponent;
var sepSkin:IFlexDisplayObject;
if (i < lines.numChildren)
{
sep = UIComponent(lines.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));
lines.addChild(sep);
UIComponentGlobals.layoutManager.validateClient(sep, true);
separators.push(sep);
}
// if not separator
if ( !(i == visibleColumns.length-1 && !needRightSeparatorEvents) )
{
DisplayObject(sep).addEventListener(
MouseEvent.MOUSE_OVER, columnResizeMouseOverHandler);
DisplayObject(sep).addEventListener(
MouseEvent.MOUSE_OUT, columnResizeMouseOutHandler);
DisplayObject(sep).addEventListener(
MouseEvent.MOUSE_DOWN, columnResizeMouseDownHandler);
}
else
{
// if not separator
if ( (i == visibleColumns.length-1 && !needRightSeparatorEvents) )
{
DisplayObject(sep).removeEventListener(
MouseEvent.MOUSE_OVER, columnResizeMouseOverHandler);
DisplayObject(sep).removeEventListener(
MouseEvent.MOUSE_OUT, columnResizeMouseOutHandler);
DisplayObject(sep).removeEventListener(
MouseEvent.MOUSE_DOWN, columnResizeMouseDownHandler);
}
}
var cols:Array = visibleColumns;
if (!(cols && cols.length > 0 || dataGrid.headerVisible))
{
sep.visible = false;
continue;
}
// Spark skins don't measure if the width is specified.
var skinWidth:Number;
if (sepSkin is IUIComponent)
skinWidth = IUIComponent(sepSkin).getExplicitOrMeasuredWidth();
else
skinWidth = sepSkin.measuredWidth;
sep.visible = true;
sep.x = headerItems[i].x +
visibleColumns[i].width - Math.round(skinWidth / 2);
if (i > 0)
{
sep.x = Math.max(sep.x,
separators[i - 1].x + Math.round(skinWidth / 2));
}
sep.y = 0;
sepSkin.setActualSize(skinWidth, Math.ceil(cachedHeaderHeight));
// Draw invisible background for separator affordance
sep.graphics.clear();
sep.graphics.beginFill(0xFFFFFF, 0);
sep.graphics.drawRect(-separatorAffordance, 0,
sepSkin.measuredWidth + separatorAffordance,
cachedHeaderHeight);
sep.graphics.endFill();
sep.mouseEnabled = true;
}
while (lines.numChildren > n)
{
lines.removeChildAt(lines.numChildren - 1);
separators.pop();
}
UIComponentGlobals.layoutManager.validateClient(lines, true);
}
/**
* Draws the highlight indicator into the given Sprite
* at the position, width and height specified using the
* color specified.
*
* @param indicator A Sprite that should contain the graphics
* that make a renderer look highlighted.
*
* @param x The suggested x position for the indicator.
*
* @param y The suggested y position for the indicator.
*
* @param width The suggested width for the indicator.
*
* @param height The suggested height for the indicator.
*
* @param color The suggested color for the indicator.
*
* @param itemRenderer The item renderer that is being highlighted.
*
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function drawHeaderIndicator(indicator:Sprite, x:Number, y:Number, width:Number, height:Number, color:uint, itemRenderer:IListItemRenderer):void
{
var g:Graphics = indicator.graphics;
g.clear();
g.beginFill(color);
g.drawRect(0, 0, width, height);
g.endFill();
indicator.x = x;
indicator.y = y;
}
/**
* Draws the selection indicator into the given Sprite
* at the position, width and height specified using the
* color specified.
*
* @param indicator A Sprite that should contain the graphics
* that make a renderer look selected.
*
* @param x The suggested x position for the indicator.
*
* @param y The suggested y position for the indicator.
*
* @param width The suggested width for the indicator.
*
* @param height The suggested height for the indicator.
*
* @param color The suggested color for the indicator.
*
* @param itemRenderer The item renderer that is being selected.
*
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function drawSelectionIndicator(indicator:Sprite, x:Number, y:Number, width:Number, height:Number, color:uint, itemRenderer:IListItemRenderer):void
{
var g:Graphics = indicator.graphics;
g.clear();
g.beginFill(color);
g.drawRect(0, 0, width, height);
g.endFill();
indicator.x = x;
indicator.y = y;
}
/**
* Draws the overlay on the dragged column into the given Sprite
* at the position, width and height specified using the
* color specified.
*
* @param indicator A Sprite that should contain the graphics
* that indicate that a column is being dragged.
*
* @param x The suggested x position for the indicator.
*
* @param y The suggested y position for the indicator.
*
* @param width The suggested width for the indicator.
*
* @param height The suggested height for the indicator.
*
* @param color The suggested color for the indicator.
*
* @param itemRenderer The item renderer that is being dragged.
*
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function drawColumnDragOverlay(indicator:Sprite, x:Number, y:Number, width:Number, height:Number, color:uint, itemRenderer:IListItemRenderer):void
{
var g:Graphics = indicator.graphics;
g.clear();
g.beginFill(color);
g.drawRect(0, 0, width, height);
g.endFill();
indicator.x = x;
indicator.y = y;
}
//--------------------------------------------------------------------------
//
// Event handlers
//
//--------------------------------------------------------------------------
/**
* @private
*/
private function columnResizeMouseOverHandler(event:MouseEvent):void
{
if (!enabled || !dataGrid.resizableColumns)
return;
var target:DisplayObject = DisplayObject(event.target);
var index:int = target.parent.getChildIndex(target);
if (!visibleColumns[index].resizable)
return;
// hide the mouse, attach and show the cursor
var stretchCursorClass:Class = getStyle("stretchCursor");
resizeCursorID = cursorManager.setCursor(stretchCursorClass,
CursorManagerPriority.HIGH, 0, 0);
}
/**
* @private
*/
private function columnResizeMouseOutHandler(event:MouseEvent):void
{
if (!enabled || !dataGrid.resizableColumns)
return;
var target:DisplayObject = DisplayObject(event.target);
var index:int = target.parent.getChildIndex(target);
if (!visibleColumns[index].resizable)
return;
cursorManager.removeCursor(resizeCursorID);
}
/**
* @private
* Indicates where the right side of a resized column appears.
*/
private function columnResizeMouseDownHandler(event:MouseEvent):void
{
if (!enabled || !dataGrid.resizableColumns)
return;
var target:DisplayObject = DisplayObject(event.target);
var index:int = target.parent.getChildIndex(target);
if (!visibleColumns[index].resizable)
return;
startX = DisplayObject(event.target).x + x;
lastPt = new Point(event.stageX, event.stageY);
lastPt = dataGrid.globalToLocal(lastPt);
var n:int = separators.length;
var colIndex:int = 0;
for (var i:int = 0; i < n; i++)
{
if (separators[i] == event.target)
{
resizingColumn = visibleColumns[i];
colIndex = i;
}
else
separators[i].mouseEnabled = false;
}
if (!resizingColumn)
return;
minX = headerItems[colIndex].x + x + resizingColumn.minWidth;
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();
if (resizeGraphic is Sprite)
Sprite(resizeGraphic).mouseEnabled = false;
dataGrid.addChild(DisplayObject(resizeGraphic));
resizeGraphic.move(DisplayObject(event.target).x + x, 0);
resizeGraphic.setActualSize(resizeGraphic.measuredWidth,
dataGrid.height / dataGrid.scaleY);
}
/**
* @private
*/
private function columnResizingHandler(event:MouseEvent):void
{
if (!MouseEvent(event).buttonDown)
{
columnResizeMouseUpHandler(event);
return;
}
var vsw:int = dataGrid.vScrollBar ? dataGrid.vScrollBar.width : 0;
var pt:Point = new Point(event.stageX, event.stageY);
pt = dataGrid.globalToLocal(pt);
lastPt = pt;
resizeGraphic.move(Math.min(Math.max(minX, pt.x),
(dataGrid.width / dataGrid.scaleX) - separators[0].width - vsw), 0);
}
/**
* @private
* Determines how much to resize the column.
*/
private function columnResizeMouseUpHandler(event:Event):void
{
if (!enabled || !dataGrid.resizableColumns)
return;
// Set this to null so sort doesn't happen.
lastItemDown = null;
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);
dataGrid.removeChild(DisplayObject(resizeGraphic));
resizeGraphic = null;
cursorManager.removeCursor(resizeCursorID);
var c:DataGridColumn = resizingColumn;
resizingColumn = null;
var vsw:int = dataGrid.vScrollBar ? dataGrid.vScrollBar.width : 0;
var mouseEvent:MouseEvent = event as MouseEvent;
var pt:Point;
if (mouseEvent)
{
pt = new Point(mouseEvent.stageX, mouseEvent.stageY);
pt = dataGrid.globalToLocal(pt);
}
else
pt = lastPt;
// resize the column
var widthChange:Number = Math.min(Math.max(minX, pt.x),
(dataGrid.width / dataGrid.scaleX) - separators[0].width - vsw) - startX;
dataGrid.resizeColumn(c.colNum, Math.floor(c.width + widthChange));
invalidateDisplayList(); // force redraw of columns
// event
var dataGridEvent:DataGridEvent =
new DataGridEvent(DataGridEvent.COLUMN_STRETCH);
dataGridEvent.columnIndex = c.colNum;
dataGridEvent.dataField = c.dataField;
dataGridEvent.localX = pt.x;
dataGrid.dispatchEvent(dataGridEvent);
}
/**
* @private
*/
private function columnDraggingMouseMoveHandler(event:MouseEvent):void
{
if (!event.buttonDown)
{
columnDraggingMouseUpHandler(event);
return;
}
var item:IListItemRenderer;
var c:DataGridColumn = dataGrid.movingColumn;
var s:Sprite;
var i:int = 0;
var n:int = headerItems.length;
var dgSelectionLayer:Sprite;
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;
var vm:EdgeMetrics = dataGrid.viewMetrics;
// Create, position and draw selection layer over both headers.
dgSelectionLayer = new UIComponent();
dgSelectionLayer.name = "columnDragSelectionLayer";
dgSelectionLayer.alpha = 0.6;
dataGrid.addChild(dgSelectionLayer);
dgSelectionLayer.x = vm.left;
dgSelectionLayer.y = vm.top;
// Create and position proxy.
var proxy:IListItemRenderer = dataGrid.createColumnItemRenderer(c, true, c);
proxy.name = "headerDragProxy";
var rowData:DataGridListData = new DataGridListData((c.headerText != null) ? c.headerText : c.dataField,
c.dataField, c.colNum, uid, dataGrid, 0);
if (proxy is IDropInListItemRenderer)
IDropInListItemRenderer(proxy).listData = rowData;
dgSelectionLayer.addChild(DisplayObject(proxy));
proxy.data = c;
proxy.styleName = getStyle("headerDragProxyStyleName");
UIComponentGlobals.layoutManager.validateClient(proxy, true);
proxy.setActualSize(c.width, dataGrid._explicitHeaderHeight ?
dataGrid.headerHeight : proxy.getExplicitOrMeasuredHeight());
for (i = 0; i < n; i++)
{
item = headerItems[i];
if (item.data == dataGrid.movingColumn)
break;
}
proxy.move(item.x + x, item.y);
// Create, position and draw column overlay.
s = new FlexSprite();
s.name = "columnDragOverlay";
s.alpha = 0.6;
dataGrid.addChild(s);
if (c.width > 0)
{
drawColumnDragOverlay(s, item.x + x, 0, c.width,
dataGrid.height / dataGrid.scaleY - vm.bottom - s.y,
getStyle("disabledColor"), item);
}
s = Sprite(selectionLayer.getChildByName("headerSelection"));
if (s)
{
s.width = dataGrid.movingColumn.width;
dgSelectionLayer.addChild(s);
s.x += x;
}
// Clip the contents so the header drag proxy doesn't show
// outside the list.
dgSelectionLayer.scrollRect = new Rectangle(0, 0,
dataGrid.width / dataGrid.scaleX,
unscaledHeight);
return;
}
// Global coordinates.
var deltaX:Number = event.stageX - startX;
dgSelectionLayer = Sprite(dataGrid.getChildByName("columnDragSelectionLayer"));
// 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(dgSelectionLayer.getChildByName("headerSelection"));
if (s)
s.x += deltaXInLocalCoordinates;
// Move header proxy.
item = IListItemRenderer(dgSelectionLayer.getChildByName("headerDragProxy"));
if (item)
item.move(item.x + deltaXInLocalCoordinates, item.y);
// Global coordinates.
startX += deltaX;
var allVisibleColumns:Array = dataGrid.getAllVisibleColumns();
var pt:Point = new Point(event.stageX, event.stageY);
pt = dataGrid.globalToLocal(pt);
lastPt = pt;
n = allVisibleColumns.length;
var xx:Number = dataGrid.viewMetrics.left;
var ww:Number;
for (i = 0; i < n; i++)
{
ww = allVisibleColumns[i].width;
if (pt.x < xx + ww)
{
// 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 > xx + ww / 2)
{
i++;
xx += ww;
}
if (dropColumnIndex != i)
{
dropColumnIndex = i;
if (!columnDropIndicator)
{
var dropIndicatorClass:Class
= getStyle("columnDropIndicatorSkin");
if (!dropIndicatorClass)
dropIndicatorClass = DataGridColumnDropIndicator;
columnDropIndicator = IFlexDisplayObject(
new dropIndicatorClass());
if (columnDropIndicator is ISimpleStyleClient)
ISimpleStyleClient(columnDropIndicator).styleName = this;
dataGrid.addChild(
DisplayObject(columnDropIndicator));
var m:Shape = new Shape();
m.graphics.beginFill(0xffffff);
m.graphics.drawRect(0, 0, 10, 10);
m.graphics.endFill();
dataGrid.addChild(m);
columnDropIndicator.mask = m;
}
dataGrid.setChildIndex(
DisplayObject(columnDropIndicator),
dataGrid.numChildren - 1);
columnDropIndicator.visible = true;
m = columnDropIndicator.mask as Shape;
m.x = dataGrid.viewMetrics.left;
m.y = dataGrid.viewMetrics.top;
m.width = dataGrid.width / dataGrid.scaleX - m.x - dataGrid.viewMetrics.right;
m.height = dataGrid.height / dataGrid.scaleY - m.x - dataGrid.viewMetrics.bottom;
columnDropIndicator.setActualSize(3, dataGrid.height / dataGrid.scaleY);
columnDropIndicator.x = xx - columnDropIndicator.width + (layoutDirection == LayoutDirection.LTR ? 0 : 1);
columnDropIndicator.y = 0;
}
break;
}
xx += ww;
}
}
/**
* @private
*
* @param event MouseEvent or SandboxMouseEvent.
*/
private function columnDraggingMouseUpHandler(event:Event):void
{
if (!dataGrid.movingColumn)
return;
var origIndex:int = dataGrid.movingColumn.colNum;
var allVisibleColumns:Array = dataGrid.getAllVisibleColumns();
if (dropColumnIndex >= 0)
{
if (dropColumnIndex >= allVisibleColumns.length)
{
dropColumnIndex = allVisibleColumns.length - 1;
}
else
{
if (origIndex < allVisibleColumns[dropColumnIndex].colNum)
dropColumnIndex--;
}
// dropColumnIndex is actually the index into the headerItems
// array. Get the corresponding index into the _columns array.
dropColumnIndex = allVisibleColumns[dropColumnIndex].colNum;
}
// Shift columns.
dataGrid.shiftColumns(origIndex, dropColumnIndex, event as MouseEvent);
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 dgSelectionLayer:Sprite = Sprite(dataGrid.getChildByName("columnDragSelectionLayer"));
if (!dgSelectionLayer)
{
startX = NaN;
dataGrid.movingColumn = null;
dropColumnIndex = -1;
return;
}
var proxy:IListItemRenderer =
IListItemRenderer(getChildByName("headerDragProxy"));
if (proxy)
dgSelectionLayer.removeChild(DisplayObject(proxy));
var s:Sprite = Sprite(dgSelectionLayer.getChildByName("headerSelection"));
if (s)
dgSelectionLayer.removeChild(s);
if (columnDropIndicator)
columnDropIndicator.visible = false;
s = Sprite(dataGrid.getChildByName("columnDragOverlay"));
if (s)
dataGrid.removeChild(s);
dataGrid.removeChild(dgSelectionLayer);
startX = NaN;
dataGrid.movingColumn = null;
dropColumnIndex = -1;
invalidateDisplayList(); // force redraw
}
/**
* @private
*/
protected function mouseOverHandler(event:MouseEvent):void
{
var r:IListItemRenderer;
var i:int;
if (resizingColumn || dataGrid.movingColumn)
return;
if (dataGrid.enabled && dataGrid.sortableColumns && dataGrid.headerVisible)
{
// find out if we hit the sort arrow
s = Sprite(getChildByName("sortArrowHitArea"));
if (event.target == s)
{
var n:int = visibleColumns.length;
for (i = 0; i < n; i++)
{
if (visibleColumns[i].colNum == dataGrid.sortIndex)
{
r = headerItems[i];
break;
}
}
}
else
{
for (i = 0; i < separators.length; i++)
{
if (event.target == separators[i] && visibleColumns[i].resizable)
return;
}
var pt:Point = new Point(event.stageX, event.stageY);
pt = globalToLocal(pt);
for (i = 0; i < headerItems.length; i++)
{
if (headerItems[i].x + headerItems[i].width >= pt.x)
{
r = headerItems[i];
break;
}
}
if (i >= headerItems.length)
return;
}
s = Sprite(getChildByName("sortArrowHitArea"));
if (visibleColumns[i].sortable)
{
var s:Sprite = Sprite(
selectionLayer.getChildByName("headerSelection"));
if (!s)
{
s = new FlexSprite();
s.name = "headerSelection";
s.mouseEnabled = false;
selectionLayer.addChild(s);
}
drawHeaderIndicator(s, r.x, 0, visibleColumns[i].width, cachedHeaderHeight - 0.5, getStyle("rollOverColor"), r);
}
}
if (event.buttonDown)
lastItemDown = r;
else
lastItemDown = null;
}
/**
* @private
*/
protected function mouseOutHandler(event:MouseEvent):void
{
var r:IListItemRenderer;
var i:int;
if (resizeGraphic || dataGrid.movingColumn)
return;
if (dataGrid.enabled && dataGrid.sortableColumns && dataGrid.headerVisible)
{
// find out if we hit the sort arrow
s = Sprite(getChildByName("sortArrowHitArea"));
if (event.target == s)
{
var n:int = visibleColumns.length;
for (i = 0; i < n; i++)
{
if (visibleColumns[i].colNum == dataGrid.sortIndex)
{
r = headerItems[i];
break;
}
}
if (i >= visibleColumns.length)
return;
}
else
{
for (i = 0; i < separators.length; i++)
{
if (event.target == separators[i] && visibleColumns[i].resizable)
return;
}
var pt:Point = new Point(event.stageX, event.stageY);
pt = globalToLocal(pt);
for (i = 0; i < headerItems.length; i++)
{
if (headerItems[i].x + headerItems[i].width >= pt.x)
{
r = headerItems[i];
break;
}
}
if (i >= headerItems.length)
i = headerItems.length - 1;
}
if (visibleColumns.length > 0 && visibleColumns[i].sortable)
{
var s:Sprite = Sprite(
selectionLayer.getChildByName("headerSelection"));
if (s)
selectionLayer.removeChild(s);
}
}
if (event.buttonDown)
lastItemDown = r;
else
lastItemDown = null;
}
/**
* @private
*/
protected function mouseDownHandler(event:MouseEvent):void
{
// trace(">>mouseDownHandler");
var r:IListItemRenderer;
var s:Sprite;
var i:int;
systemManager.getSandboxRoot().addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, true, 0, true);
systemManager.getSandboxRoot().addEventListener(SandboxMouseEvent.MOUSE_UP_SOMEWHERE, mouseLeaveHandler, false, 0, true);
// find out if we hit the sort arrow
s = Sprite(getChildByName("sortArrowHitArea"));
if (event.target == s)
{
var n:int = visibleColumns.length;
for (i = 0; i < n; i++)
{
if (visibleColumns[i].colNum == dataGrid.sortIndex)
{
r = headerItems[i];
break;
}
}
}
else
{
for (i = 0; i < separators.length; i++)
{
if (event.target == separators[i] && visibleColumns[i].resizable)
return;
}
var pt:Point = new Point(event.stageX, event.stageY);
pt = globalToLocal(pt);
for (i = 0; i < headerItems.length; i++)
{
if (headerItems[i].x + headerItems[i].width >= pt.x)
{
r = headerItems[i];
break;
}
}
if (i >= headerItems.length)
return;
}
// if headers are visible and clickable for sorting
if (dataGrid.enabled && (dataGrid.sortableColumns || dataGrid.draggableColumns)
&& dataGrid.headerVisible)
{
if (dataGrid.sortableColumns && visibleColumns[i].sortable)
{
lastItemDown = r;
s = Sprite(selectionLayer.getChildByName("headerSelection"));
if (!s)
{
s = new FlexSprite();
s.name = "headerSelection";
selectionLayer.addChild(s);
}
drawSelectionIndicator(s, r.x, 0, visibleColumns[i].width, cachedHeaderHeight - 0.5, getStyle("selectionColor"), r);
}
// begin column dragging
if (dataGrid.draggableColumns && visibleColumns[i].draggable)
{
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);
dataGrid.movingColumn = visibleColumns[i];
}
}
}
/**
* @private
*/
protected function mouseUpHandler(event:MouseEvent):void
{
var dataGridEvent:DataGridEvent;
var r:IListItemRenderer;
var s:Sprite;
var n:int;
var i:int;
systemManager.getSandboxRoot().removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, true);
systemManager.getSandboxRoot().removeEventListener(SandboxMouseEvent.MOUSE_UP_SOMEWHERE, mouseLeaveHandler);
// find out if we hit the sort arrow
s = Sprite(getChildByName("sortArrowHitArea"));
if (event.target == s)
{
n = visibleColumns.length;
for (i = 0; i < n; i++)
{
if (visibleColumns[i].colNum == dataGrid.sortIndex)
{
r = headerItems[i];
break;
}
}
}
else
{
for (i = 0; i < separators.length; i++)
{
if (event.target == separators[i] && visibleColumns[i].resizable)
return;
}
var pt:Point = new Point(event.stageX, event.stageY);
pt = globalToLocal(pt);
for (i = 0; i < headerItems.length; i++)
{
if (headerItems[i].x + headerItems[i].width >= pt.x)
{
r = headerItems[i];
break;
}
}
if (i >= headerItems.length)
return;
}
if (dataGrid.enabled && (dataGrid.sortableColumns || dataGrid.draggableColumns)
&& dataGrid.dataProvider && dataGrid.headerVisible)
{
if (r == lastItemDown)
{
if (dataGrid.sortableColumns && visibleColumns[i].sortable)
{
lastItemDown = null;
dataGridEvent = new DataGridEvent(DataGridEvent.HEADER_RELEASE, false, true);
// HEADER_RELEASE event is cancelable
dataGridEvent.columnIndex = visibleColumns[i].colNum;
dataGridEvent.dataField = visibleColumns[i].dataField;
dataGridEvent.itemRenderer = r;
dataGrid.dispatchEvent(dataGridEvent);
}
}
}
}
private function mouseLeaveHandler(event:Event):void
{
systemManager.getSandboxRoot().removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, true);
systemManager.getSandboxRoot().removeEventListener(SandboxMouseEvent.MOUSE_UP_SOMEWHERE, mouseLeaveHandler);
}
mx_internal function _placeSortArrow():void
{
placeSortArrow();
}
/**
* Draws the sort arrow graphic on the column that is the current sort key.
* This implementation creates or reuses an instance of the skin specified
* by <code>sortArrowSkin</code> style property and places
* it in the appropriate column header. It
* also shrinks the size of the column header if the text in the header
* would be obscured by the sort arrow.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
protected function placeSortArrow():void
{
var sortArrowHitArea:Sprite =
Sprite(getChildByName("sortArrowHitArea"));
if (dataGrid.sortIndex == -1 && dataGrid.lastSortIndex == -1)
{
if (sortArrow)
sortArrow.visible = false;
if (sortArrowHitArea)
sortArrowHitArea.visible = false;
return;
}
if (!dataGrid.headerVisible)
{
if (sortArrow)
sortArrow.visible = false;
if (sortArrowHitArea)
sortArrowHitArea.visible = false;
return;
}
if (!sortArrow)
{
var sortArrowClass:Class = getStyle("sortArrowSkin");
sortArrow = new sortArrowClass();
DisplayObject(sortArrow).name = enabled ? "sortArrow" : "sortArrowDisabled";
if (sortArrow is ISimpleStyleClient)
ISimpleStyleClient(sortArrow).styleName = this;
addChild(DisplayObject(sortArrow));
}
var xx:Number;
var n:int;
var i:int;
var found:Boolean = false;
if (headerItems && headerItems.length)
{
n = headerItems.length;
for (i = 0; i < n; i++)
{
if (visibleColumns[i].colNum == dataGrid.sortIndex)
{
xx = headerItems[i].x + visibleColumns[i].width;
headerItems[i].setActualSize(visibleColumns[i].width - sortArrow.measuredWidth - 8, headerItems[i].height);
if (!isNaN(headerItems[i].explicitWidth))
headerItems[i].explicitWidth = headerItems[i].width;
UIComponentGlobals.layoutManager.validateClient(headerItems[i], true);
// Create hit area to capture mouse clicks behind arrow.
if (!sortArrowHitArea)
{
sortArrowHitArea = new FlexSprite();
sortArrowHitArea.name = "sortArrowHitArea";
addChild(sortArrowHitArea);
}
else
sortArrowHitArea.visible = true;
sortArrowHitArea.x = headerItems[i].x + headerItems[i].width;
sortArrowHitArea.y = headerItems[i].y;
var g:Graphics = sortArrowHitArea.graphics;
g.clear();
g.beginFill(0, 0);
g.drawRect(0, 0, sortArrow.measuredWidth + 8,
headerItems[i].height);
g.endFill();
found = true;
break;
}
}
}
if (isNaN(xx))
sortArrow.visible = false;
else
sortArrow.visible = true;
if (visibleColumns.length && dataGrid.lastSortIndex >= 0 && dataGrid.lastSortIndex != dataGrid.sortIndex)
if (visibleColumns[0].colNum <= dataGrid.lastSortIndex &&
dataGrid.lastSortIndex <= visibleColumns[visibleColumns.length - 1].colNum)
{
n = headerItems.length;
for (var j:int = 0; j < n; j++)
{
if (visibleColumns[j].colNum == dataGrid.lastSortIndex)
{
headerItems[j].setActualSize(visibleColumns[j].width, headerItems[j].height);
UIComponentGlobals.layoutManager.validateClient(headerItems[j], true);
break;
}
}
}
var d:Boolean = (dataGrid.sortDirection == "ASC");
sortArrow.width = sortArrow.measuredWidth;
sortArrow.height = sortArrow.measuredHeight;
DisplayObject(sortArrow).scaleY = (d) ? -1.0 : 1.0;
sortArrow.x = xx - sortArrow.measuredWidth - 8;
var hh:Number = cachedHeaderHeight;
sortArrow.y = (hh - sortArrow.measuredHeight) / 2 + ((d) ? sortArrow.measuredHeight: 0);
if (found && sortArrow.x < headerItems[i].x)
sortArrow.visible = false;
if (!sortArrow.visible && sortArrowHitArea)
sortArrowHitArea.visible = false;
}
//--------------------------------------------------------------------------
//
// Overridden methods: UIComponent
//
//--------------------------------------------------------------------------
/**
* @private
*/
override public function invalidateSize():void
{
if (allowItemSizeChangeNotification)
super.invalidateSize();
}
/**
* @copy mx.core.IUIComponent#enabled
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
override public function set enabled(value:Boolean):void
{
super.enabled = value;
if (sortArrow)
{
removeChild(DisplayObject(sortArrow));
sortArrow = null;
placeSortArrow();
}
}
/**
* @private
*/
override public function styleChanged(styleProp:String):void
{
super.styleChanged(styleProp);
if (styleProp == "headerBackgroundSkin")
{
headerBGSkinChanged = true;
}
else if (styleProp == "headerSeparatorSkin")
{
headerSepSkinChanged = true;
}
}
/**
* @private
*/
private var resizeCursorID:int = CursorManager.NO_CURSOR;
/**
* @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
*/
mx_internal function getSeparators():Array
{
return separators;
}
/**
* @private
* The column that is being resized.
*/
private function get resizingColumn():DataGridColumn
{
return dataGrid.resizingColumn;
}
/**
* @private
* The column that is being resized.
*/
private function set resizingColumn(value:DataGridColumn):void
{
dataGrid.resizingColumn = value;
}
/**
* Specifies a graphic that shows the proposed column width as the user stretches it.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
private var resizeGraphic:IFlexDisplayObject;
/**
* @private
*/
private var lastItemDown:IListItemRenderer;
/**
* @private
* Index of column before which to drop
*/
private var dropColumnIndex:int = -1;
/**
* @private
*/
mx_internal var columnDropIndicator:IFlexDisplayObject;
/**
* The small arrow graphic used to show sortable columns and direction.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
mx_internal var sortArrow:IFlexDisplayObject;
/**
* diagnostics
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
mx_internal function get rendererArray():Array
{
return headerItems;
}
/**
* The offset, in pixels, from the left side of the content of the renderer.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var leftOffset:Number = 0;
/**
* The offset, in pixels, from the top of the content of the renderer.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var topOffset:Number = 0;
/**
* The offset, in pixels, from the right side of the content of the renderer.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var rightOffset:Number = 0;
/**
* The offset, in pixels, from the bottom of the content of the renderer.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var bottomOffset:Number = 0;
/**
* @private
*/
private var allowItemSizeChangeNotification:Boolean = true;
private var headerBGSkinChanged:Boolean = false;
private var headerSepSkinChanged:Boolean = false;
}
}