//////////////////////////////////////////////////////////////////////////////// // // 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.dragClasses { import flash.display.DisplayObject; import flash.display.DisplayObjectContainer; import flash.display.InteractiveObject; import flash.events.Event; import flash.events.IEventDispatcher; import flash.events.KeyboardEvent; import flash.events.MouseEvent; import flash.geom.Point; import flash.system.ApplicationDomain; import flash.utils.getQualifiedClassName; import mx.core.DragSource; import mx.core.IFlexModule; import mx.core.IUIComponent; import mx.core.UIComponent; import mx.core.mx_internal; import mx.effects.EffectInstance; import mx.effects.Move; import mx.effects.Zoom; import mx.events.DragEvent; import mx.events.EffectEvent; import mx.events.InterDragManagerEvent; import mx.events.InterManagerRequest; import mx.events.SandboxMouseEvent; import mx.managers.CursorManager; import mx.managers.DragManager; import mx.managers.ISystemManager; import mx.managers.SystemManager; import mx.modules.ModuleManager; import mx.styles.CSSStyleDeclaration; import mx.styles.IStyleManager2; import mx.styles.StyleManager; use namespace mx_internal; [ExcludeClass] /** * @private * A helper class for DragManager that displays the drag image */ public class DragProxy extends UIComponent { include "../../core/Version.as"; //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * Constructor. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function DragProxy(dragInitiator:IUIComponent, dragSource:DragSource) { super(); this.dragInitiator = dragInitiator; this.dragSource = dragSource; var sm:ISystemManager = dragInitiator.systemManager. topLevelSystemManager as ISystemManager; var ed:IEventDispatcher = sandboxRoot = sm.getSandboxRoot(); ed.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler, true); ed.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, true); ed.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler); ed.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler); } //-------------------------------------------------------------------------- // // Overridden methods // //-------------------------------------------------------------------------- /** * @private */ override public function initialize():void { super.initialize(); // in case we go offscreen dragInitiator.systemManager.getSandboxRoot().addEventListener(SandboxMouseEvent.MOUSE_UP_SOMEWHERE, mouseLeaveHandler); // Make sure someone has focus, otherwise we // won't get keyboard events. if (!getFocus()) setFocus(); } /** * @private */ override public function get styleManager():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 (this.dragInitiator is IFlexModule) return StyleManager.getStyleManager(IFlexModule(dragInitiator).moduleFactory); return super.styleManager; } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- /** * @private * Class of current cursor being displayed. */ private var cursorClass:Class = null; /** * @private * ID of current cursor. */ private var cursorID:int = CursorManager.NO_CURSOR; /** * @private * Last keyboard event received */ private var lastKeyEvent:KeyboardEvent; /** * @private * Last Mouse event received */ private var lastMouseEvent:MouseEvent; /** * @private * Root of sandbox */ private var sandboxRoot:IEventDispatcher; //-------------------------------------------------------------------------- // // Properties // //-------------------------------------------------------------------------- /** * @private */ public var dragInitiator:IUIComponent; /** * @private */ public var dragSource:DragSource; /** * @private */ public var xOffset:Number; /** * @private */ public var yOffset:Number; /** * @private */ public var startX:Number; /** * @private */ public var startY:Number; /** * @private */ public var target:DisplayObject = null; /** * @private * Current drag action - NONE, COPY, MOVE or LINK */ public var action:String; /** * @private * whether move is allowed or not */ public var allowMove:Boolean = true; //-------------------------------------------------------------------------- // // Methods // //-------------------------------------------------------------------------- /** * @private */ public function showFeedback():void { var newCursorClass:Class = cursorClass; var styleSheet:CSSStyleDeclaration = styleManager.getMergedStyleDeclaration("mx.managers.DragManager"); if (action == DragManager.COPY) newCursorClass = styleSheet.getStyle("copyCursor"); else if (action == DragManager.LINK) newCursorClass = styleSheet.getStyle("linkCursor"); else if (action == DragManager.NONE) newCursorClass = styleSheet.getStyle("rejectCursor"); else newCursorClass = styleSheet.getStyle("moveCursor"); if (newCursorClass != cursorClass) { cursorClass = newCursorClass; if (cursorID != CursorManager.NO_CURSOR) cursorManager.removeCursor(cursorID); cursorID = cursorManager.setCursor(cursorClass, 2, 0, 0); } } /** * @private */ public function checkKeyEvent(event:KeyboardEvent):void { if (target) { // Ignore repeat events. We only send the dragOver // event when the key state changes. if (lastKeyEvent && (event.type == lastKeyEvent.type) && (event.keyCode == lastKeyEvent.keyCode)) { return; } lastKeyEvent = event; // Dispatch a "dragOver" event. var dragEvent:DragEvent = new DragEvent(DragEvent.DRAG_OVER); dragEvent.dragInitiator = dragInitiator; dragEvent.dragSource = dragSource; dragEvent.action = action; dragEvent.ctrlKey = event.ctrlKey; dragEvent.altKey = event.altKey; dragEvent.shiftKey = event.shiftKey; var pt:Point = new Point(); pt.x = lastMouseEvent.localX; pt.y = lastMouseEvent.localY; pt = DisplayObject(lastMouseEvent.target).localToGlobal(pt); pt = DisplayObject(target).globalToLocal(pt); dragEvent.localX = pt.x; dragEvent.localY = pt.y; _dispatchDragEvent(target, dragEvent); showFeedback(); } } //-------------------------------------------------------------------------- // // Overridden event handlers // //-------------------------------------------------------------------------- /** * @private */ override protected function keyDownHandler(event:KeyboardEvent):void { // On every key down call the mouseMove because the drag // feedback may change checkKeyEvent(event); } /** * @private */ override protected function keyUpHandler(event:KeyboardEvent):void { checkKeyEvent(event); } //-------------------------------------------------------------------------- // // Event handlers // //-------------------------------------------------------------------------- /** * @private */ public function stage_mouseMoveHandler(event:MouseEvent):void { if (event.target != stage) return; mouseMoveHandler(event); } /** * @private */ private function dispatchDragEvent(type:String, mouseEvent:MouseEvent, eventTarget:Object):void { var dragEvent:DragEvent = new DragEvent(type); var pt:Point = new Point(); dragEvent.dragInitiator = dragInitiator; dragEvent.dragSource = dragSource; dragEvent.action = action; dragEvent.ctrlKey = mouseEvent.ctrlKey; dragEvent.altKey = mouseEvent.altKey; dragEvent.shiftKey = mouseEvent.shiftKey; pt.x = lastMouseEvent.localX; pt.y = lastMouseEvent.localY; pt = DisplayObject(lastMouseEvent.target).localToGlobal(pt); pt = DisplayObject(eventTarget).globalToLocal(pt); dragEvent.localX = pt.x; dragEvent.localY = pt.y; _dispatchDragEvent(DisplayObject(eventTarget), dragEvent); } /** * @private */ private function _dispatchDragEvent(target:DisplayObject, event:DragEvent):void { // in trusted mode, the target could be in another application domain // in untrusted mode, the mouse events shouldn't work so we shouldn't be here // same domain if (isSameOrChildApplicationDomain(target)) target.dispatchEvent(event); else { // wake up all the other DragManagers var me:InterManagerRequest = new InterManagerRequest(InterManagerRequest.INIT_MANAGER_REQUEST); me.name = "mx.managers::IDragManager"; sandboxRoot.dispatchEvent(me); // bounce this message off the sandbox root and hope // another DragManager picks it up var mde:InterDragManagerEvent = new InterDragManagerEvent(InterDragManagerEvent.DISPATCH_DRAG_EVENT, false, false, event.localX, event.localY, event.relatedObject, event.ctrlKey, event.altKey, event.shiftKey, event.buttonDown, event.delta, target, event.type, event.dragInitiator, event.dragSource, event.action, event.draggedItem ); sandboxRoot.dispatchEvent(mde); } } private function isSameOrChildApplicationDomain(target:Object):Boolean { var swfRoot:DisplayObject = SystemManager.getSWFRoot(target); if (swfRoot) return true; if (ModuleManager.getAssociatedFactory(target)) return true; var me:InterManagerRequest = new InterManagerRequest(InterManagerRequest.SYSTEM_MANAGER_REQUEST); me.name = "hasSWFBridges"; sandboxRoot.dispatchEvent(me); // if no bridges, it might be a private/internal class so return true and hope we're right if (!me.value) return true; return false; } /** * @private */ public function mouseMoveHandler(event:MouseEvent):void { var dragEvent:DragEvent; var dropTarget:DisplayObject; var i:int; lastMouseEvent = event; var pt:Point = new Point(); var point:Point = new Point(event.localX, event.localY); var stagePoint:Point = DisplayObject(event.target).localToGlobal(point); point = DisplayObject(sandboxRoot).globalToLocal(stagePoint); var mouseX:Number = point.x; var mouseY:Number = point.y; x = mouseX - xOffset; y = mouseY - yOffset; // The first time through we only want to position the proxy. if (!event) { return; } // trace("===>DragProxy:mouseMove"); var targetList:Array; /* of DisplayObject */ targetList = []; DragProxy.getObjectsUnderPoint(DisplayObject(sandboxRoot), stagePoint, targetList); var newTarget:DisplayObject = null; // trace(" ", targetList.length, "objects under point"); // targetList is in depth order, and we want the top of the list. However, we // do not want the target to be a decendent of us. var targetIndex:int = targetList.length - 1; while (targetIndex >= 0) { newTarget = targetList[targetIndex]; if (newTarget != this && !contains(newTarget)) break; targetIndex--; } // trace(" skipped", targetList.length - targetIndex - 1); // If we already have a target, send it a dragOver event // if we're still over it. // If we're not over it, send it a dragExit event. if (target) { var foundIt:Boolean = false; var oldTarget:DisplayObject = target; dropTarget = newTarget; while (dropTarget) { if (dropTarget == target) { // trace(" dispatch DRAG_OVER on", dropTarget); // Dispatch a "dragOver" event dispatchDragEvent(DragEvent.DRAG_OVER, event, dropTarget); foundIt = true; break; } else { // trace(" dispatch DRAG_ENTER on", dropTarget); // Dispatch a "dragEnter" event and see if a new object // steals the target. dispatchDragEvent(DragEvent.DRAG_ENTER, event, dropTarget); // If the potential target accepted the drag, our target // now points to the dropTarget. Bail out here, but make // sure we send a dragExit event to the oldTarget. if (target == dropTarget) { foundIt = false; break; } } dropTarget = dropTarget.parent; } if (!foundIt) { // trace(" dispatch DRAG_EXIT on", oldTarget); // Dispatch a "dragExit" event on the old target. dispatchDragEvent(DragEvent.DRAG_EXIT, event, oldTarget); if (target == oldTarget) target = null; } } // If we don't have an existing target, go look for one. if (!target) { action = DragManager.MOVE; // Dispatch a "dragEnter" event. dropTarget = newTarget; while (dropTarget) { if (dropTarget != this) { // trace(" dispatch DRAG_ENTER on", dropTarget); dispatchDragEvent(DragEvent.DRAG_ENTER, event, dropTarget); if (target) break; } dropTarget = dropTarget.parent; } if (!target) action = DragManager.NONE; } // trace("<===DragProxy:mouseMove"); showFeedback(); } /** * @private */ public function mouseLeaveHandler(event:Event):void { mouseUpHandler(lastMouseEvent); } /** * @private */ public function mouseUpHandler(event:MouseEvent):void { var dragEvent:DragEvent; var sm:ISystemManager = dragInitiator.systemManager. topLevelSystemManager as ISystemManager; var ed:IEventDispatcher = sandboxRoot; ed.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler, true); ed.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, true); ed.removeEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler); // in case we go offscreen ed.removeEventListener(SandboxMouseEvent.MOUSE_UP_SOMEWHERE, mouseLeaveHandler); ed.removeEventListener(KeyboardEvent.KEY_UP, keyUpHandler); var delegate:Object = automationDelegate; if (target && action != DragManager.NONE) { // Dispatch a "dragDrop" event. dragEvent = new DragEvent(DragEvent.DRAG_DROP); dragEvent.dragInitiator = dragInitiator; dragEvent.dragSource = dragSource; dragEvent.action = action; dragEvent.ctrlKey = event.ctrlKey; dragEvent.altKey = event.altKey; dragEvent.shiftKey = event.shiftKey; var pt:Point = new Point(); pt.x = lastMouseEvent.localX; pt.y = lastMouseEvent.localY; pt = DisplayObject(lastMouseEvent.target).localToGlobal(pt); pt = DisplayObject(target).globalToLocal(pt); dragEvent.localX = pt.x; dragEvent.localY = pt.y; if (delegate) delegate.recordAutomatableDragDrop(target, dragEvent); _dispatchDragEvent(target, dragEvent); } else { action = DragManager.NONE; } // Do the drop effect. // If the drop was accepted, zoom the proxy image into // the current mouse location. // If the drop was rejected, move the proxy image // back to its original location. if (action == DragManager.NONE) { // Tween back to original position var m1:Move = new Move(this); m1.addEventListener(EffectEvent.EFFECT_END, effectEndHandler); m1.xFrom = x; m1.yFrom = y; m1.xTo = startX; m1.yTo = startY; m1.duration = 200; m1.play(); } else { // Zoom into mouse location to show drag was accepted. var e:Zoom = new Zoom(this); e.zoomWidthFrom = e.zoomHeightFrom = 1.0; e.zoomWidthTo = e.zoomHeightTo = 0; e.duration = 200; e.play(); var m:Move = new Move(this); m.addEventListener(EffectEvent.EFFECT_END, effectEndHandler); m.xFrom = x; m.yFrom = y; m.xTo = parent.mouseX; m.yTo = parent.mouseY; m.duration = 200; m.play(); } // Dispatch a "dragComplete" event. dragEvent = new DragEvent(DragEvent.DRAG_COMPLETE); dragEvent.dragInitiator = dragInitiator; dragEvent.dragSource = dragSource; dragEvent.relatedObject = InteractiveObject(target); dragEvent.action = action; dragEvent.ctrlKey = event.ctrlKey; dragEvent.altKey = event.altKey; dragEvent.shiftKey = event.shiftKey; dragInitiator.dispatchEvent(dragEvent); if (delegate && action == DragManager.NONE) delegate.recordAutomatableDragCancel(dragInitiator, dragEvent); // Hide the drag cursor cursorManager.removeCursor(cursorID); cursorID = CursorManager.NO_CURSOR; this.lastMouseEvent = null; } /** * @private */ private function effectEndHandler(event:EffectEvent):void { DragManager.endDrag(); } /** * Player doesn't handle this correctly so we have to do it ourselves * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ private static function getObjectsUnderPoint(obj:DisplayObject, pt:Point, arr:Array):void { if (!obj.visible) return; if ((obj is UIComponent) && !UIComponent(obj).$visible) return; if (obj.hitTestPoint(pt.x, pt.y, true)) { if (obj is InteractiveObject && InteractiveObject(obj).mouseEnabled) arr.push(obj); if (obj is DisplayObjectContainer) { var doc:DisplayObjectContainer = obj as DisplayObjectContainer; if (doc.mouseChildren) { // we use this test so we can test in other application domains if ("rawChildren" in doc) { var rc:Object = doc["rawChildren"]; n = rc.numChildren; for (i = 0; i < n; i++) { try { getObjectsUnderPoint(rc.getChildAt(i), pt, arr); } catch (e:Error) { //another sandbox? } } } else { if (doc.numChildren) { var n:int = doc.numChildren; for (var i:int = 0; i < n; i++) { try { var child:DisplayObject = doc.getChildAt(i); getObjectsUnderPoint(child, pt, arr); } catch (e:Error) { //another sandbox? } } } } } } } } } }