Archive

Archive for December, 2010

Bug on Tree : scrolls automatically after a drag/drog

December 9, 2010 Leave a comment

Here is an override to solve that problem :

package ui.controls
{
import mx.collections.ICollectionView;
import mx.collections.IViewCursor;
import mx.collections.ItemResponder;
import mx.collections.errors.ItemPendingError;
import mx.controls.Tree;
import mx.core.mx_internal;
import mx.events.DragEvent;
import mx.managers.DragManager;

use namespace mx_internal;

/**
* This override of the List correct the bug which happen after dragging one time, the List scroll automatically when the mouse is moving over.
*
* */
public class DroppableListItemTree extends Tree
{
public function DroppableListItemTree()
{
super();
}

/**
* Handles DragEvent.DRAG_COMPLETE events. This method
* removes the item from the data provider.
*
* @param event The DragEvent object.
*/
override protected function dragCompleteHandler(event:DragEvent):void
{
isPressed = false;

resetDragScrolling()

if (event.isDefaultPrevented())
return;

try
{
if (event.dragSource.hasFormat(“treeItems”))
{
// if we’ve moved the elements, then remove them here
if (event.action == DragManager.MOVE && dragMoveEnabled)
{
// if we moved onto ourselves, we already handled this in
// dragDropHandler
if (event.relatedObject != this)
{
var items:Array = event.dragSource.dataForFormat(“treeItems”) as Array;
var parent:*;
var index:int;
var i:int;
var n:int;

//do the remove
n = items.length;
for (i = 0; i < n; i++)
{
parent = getParentItem(items[i]);
index = getChildIndexInParent(parent, items[i]);
removeChildItem(parent, items[i], index);
}

// then add it to the target control (copy operations are
// handled in the target's dragDropHandler), but the MOVE
// operations need to be handled here (see comment in
// dragDropHandler about this)
if (event.relatedObject is Tree)
{
var targetTree:Tree = Tree(event.relatedObject);
if (!targetTree.dataProvider) {
// Create an empty collection to drop items into.
targetTree.dataProvider = [];
targetTree.validateNow();
}

n = items.length;
for (i = 0; i < n; i++)
{
var item:Object = items[i];

targetTree.addChildItem(targetTree._dropData.parent,
item,
targetTree._dropData.index);
}
}
}
clearSelected(false);
}
}
}
catch(e:ItemPendingError)
{
e.addResponder(new ItemResponder(seekPendingDuringDragResultHandler, seekPendingDuringDragFailureHandler,
new TreeSeekPending(event, dragCompleteHandler)));
}
lastDragEvent = null;
}

/**
* Handles DragEvent.DRAG_DROP events. This method hides
* the drop feedback by calling the hideDropFeedback() method.
*
*

If the action is a COPY,
* then this method makes a deep copy of the object
* by calling the ObjectUtil.copy() method,
* and replaces the copy’s uid property (if present)
* with a new value by calling the UIDUtil.createUID() method.

*
* @param event The DragEvent object.
*
* @see mx.utils.ObjectUtil
* @see mx.utils.UIDUtil
*/
override protected function dragDropHandler(event:DragEvent):void
{
resetDragScrolling()

if (event.isDefaultPrevented())
return;

hideDropFeedback(event);

if (event.dragSource.hasFormat(“treeItems”))
{
var items:Array = event.dragSource.dataForFormat(“treeItems”) as Array;
var i:int;
var n:int;

// if we’re moving to ourselves, we need to treat it specially and check for “parent”
// problems where we could recurse forever.
if (event.action == DragManager.MOVE && dragMoveEnabled)
{
if (event.dragInitiator == this)
{
// If we’re dropping onto ourselves or a child of a descendant then dont actually drop

calculateDropIndex(event);

// If we did start this drag op then we need to remove first
var index:int;
var parent:*;
var parentItem:*;
var dropIndex:int = _dropData.index;

//get ancestors of the drop target item
var dropParentStack:Array = getParentStack(_dropData.parent);
dropParentStack.unshift(_dropData.parent);

n = items.length;
for (i = 0; i < n; i++)
{
parent = getParentItem(items[i]);
index = getChildIndexInParent(parent, items[i]);
//check ancestors of the dropTarget if the item matches, we're invalid

for each (parentItem in dropParentStack)
{
//we dont want to drop into one of our own sets of children
if (items[i] === parentItem)
return;
}

//we remove before we add due to the behavior
//of structures with parent pointers like e4x
removeChildItem(parent, items[i], index);

//is the removed item before the drop location?
// then we need to shift the dropIndex accordingly
if (parent == _dropData.parent && index < _dropData.index)
dropIndex–;

addChildItem(_dropData.parent, items[i], dropIndex);
}

return;
}
}

// If not dropping onto ourselves, then add the
// items here if it's a copy operation.
// If it's a move operation (and not on ourselves), then they
// are added in dragCompleteHandler and are removed from
// the source's dragCompleteHandler. We do both in dragCompleteHandler
// because in order to be re-parented, they must be removed from their
// original source FIRST. This means our code isn't coupled fantastically
// as dragCompleteHandler must get the destination tree and
// cast it to a Tree.

if (event.action == DragManager.COPY)
{
if (!dataProvider) {
// Create an empty collection to drop items into.
dataProvider = [];
validateNow();
}

n = items.length;
for (i = 0; i < n; i++)
{
var item:Object = copyItemWithUID(items[i]);

addChildItem(_dropData.parent,
item,
_dropData.index);
}
}
}
lastDragEvent = null;
}

/**
* Handles DragEvent.DRAG_EXIT events. This method hides
* the UI feeback by calling the hideDropFeedback() method.
*
* @param event The DragEvent object.
*/
override protected function dragExitHandler(event:DragEvent):void
{
resetDragScrolling();

if (event.isDefaultPrevented())
return;

lastDragEvent = null;

hideDropFeedback(event);

DragManager.showFeedback(DragManager.NONE);
}

/**
* @private
* Returns the stack of parents from a child item.
*/
private function getParentStack(item:Object):Array
{
var stack:Array = [];
if (item == null)
return stack;

var parent:* = getParentItem(item);
while (parent)
{
stack.push(parent);
parent = getParentItem(parent);
}
return stack;
}

/**
* @private
*/
private var haveItemIndices:Boolean;

/**
* @private
*/
private function checkItemIndices(event:DragEvent):void
{
if (haveItemIndices)
return;

// if we’re moving to ourselves, we need to make sure we have
// everything paged below before we allow a drop
if ((event.action == DragManager.MOVE || event.action == DragManager.NONE) && dragMoveEnabled)
{
if (event.dragInitiator == this)
{
var items:Array = event.dragSource.dataForFormat(“treeItems”) as Array;
var n:int = items.length;
for (var i:int = 0; i < n; i++)
{
var parent:Object = getParentItem(items[i]);
getChildIndexInParent(parent, items[i]);
}
haveItemIndices = true;
}
}
}

/**
* @private
* Finds the index distance between a parent and child
*/
private function getChildIndexInParent(parent:Object, child:Object):int
{
var index:int = 0;
if (!parent)
{
var cursor:IViewCursor = ICollectionView(iterator.view).createCursor();
while (!cursor.afterLast)
{
if (child === cursor.current)
break;
index++;
cursor.moveNext();
}
}
else
{
if (parent != null &&
_dataDescriptor.isBranch(parent, iterator.view) &&
_dataDescriptor.hasChildren(parent, iterator.view))
{
var children:ICollectionView = getChildren(parent, iterator.view);
if (children.contains(child))
{
cursor = children.createCursor();
while (!cursor.afterLast)
{
if (child === cursor.current)
break;
cursor.moveNext();
index++;
}

}
else
{
//throw new Error("Parent item does not contain specified child: " + itemToUID(child));
}
}
}
return index;
}

/**
* @private
*/
override protected function dragEnterHandler(event:DragEvent):void
{
if (event.isDefaultPrevented())
return;

lastDragEvent = event;
haveItemIndices = false;

try
{
if (iteratorValid && event.dragSource.hasFormat("treeItems"))
{
//if (collectionThrowsIPE)
//checkItemIndices(event);

DragManager.acceptDragDrop(this);
DragManager.showFeedback(event.ctrlKey ?
DragManager.COPY :
DragManager.MOVE);
showDropFeedback(event);
return;
}
}
catch(e:ItemPendingError)
{
if (!lastTreeSeekPending)
{
lastTreeSeekPending = new TreeSeekPending(event, dragEnterHandler)
e.addResponder(new ItemResponder(seekPendingDuringDragResultHandler, seekPendingDuringDragFailureHandler,
lastTreeSeekPending));
}
}
catch(e1:Error)
{
}
hideDropFeedback(event);
DragManager.showFeedback(DragManager.NONE);
}

/**
* @private
*/
override protected function dragOverHandler(event:DragEvent):void
{
if (event.isDefaultPrevented())
return;

lastDragEvent = event;

try
{
if (iteratorValid && event.dragSource.hasFormat("treeItems"))
{
if (collectionThrowsIPE)
checkItemIndices(event);

DragManager.showFeedback(event.ctrlKey ?
DragManager.COPY :
DragManager.MOVE);
showDropFeedback(event);
return;
}
}
catch(e:ItemPendingError)
{
if (!lastTreeSeekPending)
{
lastTreeSeekPending = new TreeSeekPending(event, dragOverHandler)
e.addResponder(new ItemResponder(seekPendingDuringDragResultHandler, seekPendingDuringDragFailureHandler,
lastTreeSeekPending));
}
}
catch(e1:Error)
{
}
hideDropFeedback(event);
DragManager.showFeedback(DragManager.NONE);
}

/**
* @private
*/
private var lastTreeSeekPending:TreeSeekPending;

/**
* @private
* The default failure handler when a seek fails due to a page fault.
*/
private function seekPendingDuringDragFailureHandler(data:Object,
info:TreeSeekPending):void
{
}

/**
* @private
* The default result handler when a seek fails due to a page fault.
* This method re-attempts setting the drag feedback
*/
private function seekPendingDuringDragResultHandler(data:Object,
info:TreeSeekPending):void
{
lastTreeSeekPending = null;

if (lastDragEvent)
info.retryFunction(info.event);
}
}
}

import mx.events.DragEvent;

class TreeSeekPending
{
public function TreeSeekPending(event:DragEvent, retryFunction:Function)
{
this.event = event;
this.retryFunction = retryFunction;
}

public var event:DragEvent;

public var retryFunction:Function;

}

Categories: Uncategorized