////////////////////////////////////////////////////////////////////////////////
//
// 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.managers
{
import flash.display.DisplayObject;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.geom.Point;
import mx.core.DragSource;
import mx.core.IFlexDisplayObject;
import mx.core.IFlexModule;
import mx.core.IFlexModuleFactory;
import mx.core.ILayoutDirectionElement;
import mx.core.IUIComponent;
import mx.core.LayoutDirection;
import mx.core.UIComponentGlobals;
import mx.core.mx_internal;
import mx.events.DragEvent;
import mx.events.Request;
import mx.managers.dragClasses.DragProxy;
import mx.styles.CSSStyleDeclaration;
import mx.styles.IStyleManager2;
import mx.styles.StyleManager;
import mx.utils.MatrixUtil;
use namespace mx_internal;
[ExcludeClass]
/**
* @private
*/
public class DragManagerImpl extends EventDispatcher implements IDragManager
{
include "../core/Version.as";
//--------------------------------------------------------------------------
//
// Class variables
//
//--------------------------------------------------------------------------
/**
* @private
*/
private static var sm:ISystemManager;
/**
* @private
*/
private static var instance:IDragManager;
/**
* @private
*
* Place to hook in additional classes
*/
public static var mixins:Array;
//--------------------------------------------------------------------------
//
// Class methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
public static function getInstance():IDragManager
{
if (!instance)
{
sm = SystemManagerGlobals.topLevelSystemManagers[0];
instance = new DragManagerImpl();
}
return instance;
}
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* @private
*/
public function DragManagerImpl()
{
super();
if (instance)
throw new Error("Instance already exists.");
if (mixins)
{
var n:int = mixins.length;
for (var i:int = 0; i < n; i++)
{
new mixins[i](this);
}
}
sandboxRoot = sm.getSandboxRoot();
if (sm.isTopLevelRoot())
{
sm.addEventListener(MouseEvent.MOUSE_DOWN, sm_mouseDownHandler, false, 0, true);
sm.addEventListener(MouseEvent.MOUSE_UP, sm_mouseUpHandler, false, 0, true);
}
if (hasEventListener("initialize"))
dispatchEvent(new Event("initialize"));
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
* The highest place we can listen for events in our DOM
*/
private var sandboxRoot:IEventDispatcher;
/**
* @private
* Object that initiated the drag.
*/
private var dragInitiator:IUIComponent;
/**
* @private
* Object being dragged around.
*/
public var dragProxy:DragProxy;
/**
* @private
*/
public var bDoingDrag:Boolean = false;
/**
* @private
*/
private var mouseIsDown:Boolean = false;
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
/**
* Read-only property that returns true
* if a drag is in progress.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get isDragging():Boolean
{
return bDoingDrag;
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* Initiates a drag and drop operation.
*
* @param dragInitiator IUIComponent that specifies the component initiating
* the drag.
*
* @param dragSource DragSource object that contains the data
* being dragged.
*
* @param mouseEvent The MouseEvent that contains the mouse information
* for the start of the drag.
*
* @param dragImage The image to drag. This argument is optional.
* If omitted, a standard drag rectangle is used during the drag and
* drop operation. If you specify an image, you must explicitly set a
* height and width of the image or else it will not appear.
*
* @param xOffset Number that specifies the x offset, in pixels, for the
* dragImage
. This argument is optional. If omitted, the drag proxy
* is shown at the upper-left corner of the drag initiator. The offset is expressed
* in pixels from the left edge of the drag proxy to the left edge of the drag
* initiator, and is usually a negative number.
*
* @param yOffset Number that specifies the y offset, in pixels, for the
* dragImage
. This argument is optional. If omitted, the drag proxy
* is shown at the upper-left corner of the drag initiator. The offset is expressed
* in pixels from the top edge of the drag proxy to the top edge of the drag
* initiator, and is usually a negative number.
*
* @param imageAlpha Number that specifies the alpha value used for the
* drag image. This argument is optional. If omitted, the default alpha
* value is 0.5. A value of 0.0 indicates that the image is transparent;
* a value of 1.0 indicates it is fully opaque.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function doDrag(
dragInitiator:IUIComponent,
dragSource:DragSource,
mouseEvent:MouseEvent,
dragImage:IFlexDisplayObject = null, // instance of dragged item(s)
xOffset:Number = 0,
yOffset:Number = 0,
imageAlpha:Number = 0.5,
allowMove:Boolean = true):void
{
var proxyWidth:Number;
var proxyHeight:Number;
// Can't start a new drag if we're already in the middle of one...
if (bDoingDrag)
return;
// Can't do a drag if the mouse isn't down
if (!(mouseEvent.type == MouseEvent.MOUSE_DOWN ||
mouseEvent.type == MouseEvent.CLICK ||
mouseIsDown ||
mouseEvent.buttonDown))
{
return;
}
bDoingDrag = true;
if (hasEventListener("doDrag"))
dispatchEvent(new Event("doDrag"));
this.dragInitiator = dragInitiator;
// The drag proxy is a UIComponent with a single child -
// an instance of the dragImage.
dragProxy = new DragProxy(dragInitiator, dragSource);
var e:Event;
if (hasEventListener("popUpChildren"))
e = new DragEvent("popUpChildren", false, true, dragProxy);
if (!e || dispatchEvent(e))
sm.popUpChildren.addChild(dragProxy);
if (!dragImage)
{
// No drag image specified, use default
var dragManagerStyleDeclaration:CSSStyleDeclaration =
getStyleManager(dragInitiator).getMergedStyleDeclaration("mx.managers.DragManager");
var dragImageClass:Class =
dragManagerStyleDeclaration.getStyle("defaultDragImageSkin");
dragImage = new dragImageClass();
dragProxy.addChild(DisplayObject(dragImage));
proxyWidth = dragInitiator.width;
proxyHeight = dragInitiator.height;
}
else
{
dragProxy.addChild(DisplayObject(dragImage));
if (dragImage is ILayoutManagerClient )
UIComponentGlobals.layoutManager.validateClient(ILayoutManagerClient (dragImage), true);
if (dragImage is IUIComponent)
{
proxyWidth = (dragImage as IUIComponent).getExplicitOrMeasuredWidth();
proxyHeight = (dragImage as IUIComponent).getExplicitOrMeasuredHeight();
}
else
{
proxyWidth = dragImage.measuredWidth;
proxyHeight = dragImage.measuredHeight;
}
}
// Set the layoutDirection of the dragProxy and dragImage to match the dragInitiator
// to ensure that they will be in the right position and orientation.
if (dragInitiator is ILayoutDirectionElement &&
ILayoutDirectionElement(dragInitiator).layoutDirection == LayoutDirection.RTL)
dragProxy.layoutDirection = LayoutDirection.RTL;
dragImage.setActualSize(proxyWidth, proxyHeight);
dragProxy.setActualSize(proxyWidth, proxyHeight);
// Alpha
dragProxy.alpha = imageAlpha;
dragProxy.allowMove = allowMove;
// Make sure any scale/rotation from the initiator will be reflected.
var concatenatedMatrix:Matrix =
MatrixUtil.getConcatenatedMatrix(DisplayObject(dragInitiator),
DisplayObject(sandboxRoot));
// Zero out the translation part of the matrix, as we're going to
// position the dragProxy explicitly further below.
concatenatedMatrix.tx = 0;
concatenatedMatrix.ty = 0;
// Combine with the matrix of the dragImage if it has any.
var m:Matrix = dragImage.transform.matrix;
if (m)
{
concatenatedMatrix.concat(dragImage.transform.matrix);
dragImage.transform.matrix = concatenatedMatrix;
}
// Find mouse coordinates in global space
var nonNullTarget:Object = mouseEvent.target;
if (nonNullTarget == null)
nonNullTarget = dragInitiator;
var point:Point = new Point(mouseEvent.localX, mouseEvent.localY);
point = DisplayObject(nonNullTarget).localToGlobal(point);
point = DisplayObject(sandboxRoot).globalToLocal(point);
var mouseX:Number = point.x;
var mouseY:Number = point.y;
// Find the proxy origin in global space
var proxyOrigin:Point = DisplayObject(dragInitiator).localToGlobal(new Point(-xOffset, -yOffset));
proxyOrigin = DisplayObject(sandboxRoot).globalToLocal(proxyOrigin);
// Set dragProxy.offset to the mouse offset within the drag proxy.
dragProxy.xOffset = mouseX - proxyOrigin.x;
dragProxy.yOffset = mouseY - proxyOrigin.y;
// Setup the initial position of the drag proxy.
dragProxy.x = proxyOrigin.x;
dragProxy.y = proxyOrigin.y;
// Remember the starting location of the drag proxy so it can be
// "snapped" back if the drop was refused.
dragProxy.startX = dragProxy.x;
dragProxy.startY = dragProxy.y;
// Turn on caching.
if (dragImage is DisplayObject)
DisplayObject(dragImage).cacheAsBitmap = true;
var delegate:Object = dragProxy.automationDelegate;
if (delegate)
delegate.recordAutomatableDragStart(dragInitiator, mouseEvent);
}
/**
* Call this method from your dragEnter
event handler if you accept
* the drag/drop data.
* For example:
*
*
DragManager.acceptDragDrop(event.target);* * @param target The drop target accepting the drag. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function acceptDragDrop(target:IUIComponent):void { // trace("-->acceptDragDrop for DragManagerImpl", sm, target); if (dragProxy) dragProxy.target = target as DisplayObject; if (hasEventListener("acceptDragDrop")) dispatchEvent(new Request("acceptDragDrop", false, false, target)); } /** * Sets the feedback indicator for the drag and drop operation. * Possible values are
DragManager.COPY
, DragManager.MOVE
,
* DragManager.LINK
, or DragManager.NONE
.
*
* @param feedback The type of feedback indicator to display.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function showFeedback(feedback:String):void
{
// trace("-->showFeedback for DragManagerImpl", sm, feedback);
if (dragProxy)
{
if (feedback == DragManager.MOVE && !dragProxy.allowMove)
feedback = DragManager.COPY;
dragProxy.action = feedback;
}
if (hasEventListener("showFeedback"))
dispatchEvent(new Request("showFeedback", false, false, feedback));
}
/**
* Returns the current drag and drop feedback.
*
* @return Possible return values are DragManager.COPY
,
* DragManager.MOVE
,
* DragManager.LINK
, or DragManager.NONE
.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function getFeedback():String
{
if (hasEventListener("getFeedback"))
{
var request:Request = new Request("getFeedback", false, true);
if (!dispatchEvent(request))
{
return request.value as String;
}
}
// trace("<--getFeedback for DragManagerImpl", sm);
return dragProxy ? dragProxy.action : DragManager.NONE;
}
/**
* @private
*/
public function endDrag():void
{
var e:Event;
if (hasEventListener("endDrag"))
{
e = new Event("endDrag", false, true);
}
if (!e || dispatchEvent(e))
{
if (dragProxy)
{
sm.popUpChildren.removeChild(dragProxy);
if (dragProxy.numChildren > 0)
dragProxy.removeChildAt(0); // The drag image is the only child
dragProxy = null;
}
}
dragInitiator = null;
bDoingDrag = false;
}
/**
* @private
*/
static private function getStyleManager(dragInitiator:IUIComponent):IStyleManager2
{
// If the dragInitiator has a styleManager, use that one.
// In a situation where a main application that loads a module with drag initiator,
// the main application may not link in the DragManager and appropriate styles.
// We want to use the styles of the module of the dragInitiator. See SDK-24324.
if (dragInitiator is IFlexModule)
return StyleManager.getStyleManager(IFlexModule(dragInitiator).moduleFactory);
return StyleManager.getStyleManager(sm as IFlexModuleFactory);
}
//--------------------------------------------------------------------------
//
// Event handlers
//
//--------------------------------------------------------------------------
/**
* @private
*/
private function sm_mouseDownHandler(event:MouseEvent):void
{
mouseIsDown = true;
}
/**
* @private
*/
private function sm_mouseUpHandler(event:MouseEvent):void
{
mouseIsDown = false;
}
}
}