{
// Log this exception unless the cause is EditFailedException. These exception
// are the result of the user declining a particular action that is necessary
// before the edit can happen (such as making a file writable).
final EditFailedException editFailedException = EditFailedException.findAsCause( e );
if( editFailedException == null )
{
Sapphire.service( LoggingService.class ).log( e );
}
}
finally
{
suspension.dispose();
outline.listeners().queue().process();
}
parentNode.getContentTree().setSelectedNode( selectionPostDelete );
}
}
dragElements.clear();
}
}
);
final DropTarget target = new DropTarget( tree, DND.DROP_COPY | DND.DROP_MOVE );
target.setTransfer( transfers );
target.addDropListener
(
new DropTargetAdapter()
{
public void dragOver( final DropTargetEvent event )
{
if( event.item != null )
{
final TreeItem dragOverItem = (TreeItem) event.item;
final MasterDetailsContentNodePart dragOverNode = (MasterDetailsContentNodePart) dragOverItem.getData();
final MasterDetailsContentNodePart parentNode = dragOverNode.getParentNode();
final List<MasterDetailsContentNodePart> siblingNodes = parentNode.nodes().visible();
final Point pt = dragOverItem.getDisplay().map( null, tree, event.x, event.y );
final Rectangle bounds = dragOverItem.getBounds();
boolean dragOverNodeAcceptedDrop = false;
if( pt.y > bounds.y + bounds.height / 3 && pt.y < bounds.y + bounds.height - bounds.height / 3 )
{
for( final PropertyDef dragOverTargetChildProperty : dragOverNode.getChildNodeFactoryProperties() )
{
if( dragOverTargetChildProperty instanceof ListProperty && ! dragOverTargetChildProperty.isReadOnly() )
{
dragOverNodeAcceptedDrop = true;
event.feedback = DND.FEEDBACK_SELECT;
break;
}
}
}
if( ! dragOverNodeAcceptedDrop )
{
MasterDetailsContentNodePart precedingNode = null;
MasterDetailsContentNodePart trailingNode = null;
if( pt.y < bounds.y + bounds.height / 2 )
{
precedingNode = findPrecedingItem( siblingNodes, dragOverNode );
trailingNode = dragOverNode;
event.feedback = DND.FEEDBACK_INSERT_BEFORE;
}
else
{
precedingNode = dragOverNode;
trailingNode = findTrailingItem( siblingNodes, dragOverNode );
event.feedback = DND.FEEDBACK_INSERT_AFTER;
}
boolean ok = false;
if( precedingNode != null )
{
final Element precedingElement = precedingNode.getModelElement();
if( precedingElement.parent() instanceof ElementList && precedingNode.controls( precedingElement ) )
{
ok = true;
}
}
if( ! ok && trailingNode != null )
{
final Element trailingElement = trailingNode.getModelElement();
if( trailingElement.parent() instanceof ElementList && trailingNode.controls( trailingElement ) )
{
ok = true;
}
}
if( ! ok )
{
event.feedback = DND.FEEDBACK_NONE;
}
}
}
event.feedback |= DND.FEEDBACK_SCROLL;
}
@SuppressWarnings( "unchecked" )
public void drop( final DropTargetEvent event )
{
if( event.data == null || event.item == null)
{
event.detail = DND.DROP_NONE;
return;
}
// Determine where something was dropped.
final List<ElementData> droppedElements = (List<ElementData>) event.data;
final TreeItem dropTargetItem = (TreeItem) event.item;
final MasterDetailsContentNodePart dropTargetNode = (MasterDetailsContentNodePart) dropTargetItem.getData();
final MasterDetailsContentNodePart parentNode = dropTargetNode.getParentNode();
final List<MasterDetailsContentNodePart> siblingNodes = parentNode.nodes().visible();
final Point pt = tree.getDisplay().map( null, tree, event.x, event.y );
final Rectangle bounds = dropTargetItem.getBounds();
MasterDetailsContentNodePart precedingNode = null;
MasterDetailsContentNodePart trailingNode = null;
boolean dropTargetNodeAcceptedDrop = false;
if( pt.y > bounds.y + bounds.height / 3 && pt.y < bounds.y + bounds.height - bounds.height / 3 )
{
for( final PropertyDef dropTargetChildProperty : dropTargetNode.getChildNodeFactoryProperties() )
{
if( dropTargetChildProperty instanceof ListProperty && ! dropTargetChildProperty.isReadOnly() )
{
dropTargetNodeAcceptedDrop = true;
break;
}
}
}
if( ! dropTargetNodeAcceptedDrop )
{
if( pt.y < bounds.y + bounds.height / 2 )
{
precedingNode = findPrecedingItem( siblingNodes, dropTargetNode );
trailingNode = dropTargetNode;
}
else
{
precedingNode = dropTargetNode;
trailingNode = findTrailingItem( siblingNodes, dropTargetNode );
}
}
// Determine whether the drop was valid from model standpoint and figure out
// where in the model the dropped elements are to be inserted.
ElementList<?> list = null;
int position = -1;
if( precedingNode != null )
{
final Element precedingElement = precedingNode.getModelElement();
if( precedingElement.parent() instanceof ElementList && ! precedingElement.parent().definition().isReadOnly() &&
precedingNode.controls( precedingElement ) )
{
list = (ElementList<?>) precedingElement.parent();
final Set<ElementType> possibleListElementTypes = list.definition().service( PossibleTypesService.class ).types();
for( final ElementData droppedElement : droppedElements )
{
if( ! possibleListElementTypes.contains( droppedElement.type() ) )
{
list = null;
break;
}
}
if( list != null )
{
position = list.indexOf( precedingElement ) + 1;
}
}
}
if( list == null && trailingNode != null )
{
final Element trailingElement = trailingNode.getModelElement();
if( trailingElement.parent() instanceof ElementList && ! trailingElement.parent().definition().isReadOnly() &&
trailingNode.controls( trailingElement ) )
{
list = (ElementList<?>) trailingElement.parent();
final Set<ElementType> possibleListElementTypes = list.definition().service( PossibleTypesService.class ).types();
for( final ElementData droppedElement : droppedElements )
{
if( ! possibleListElementTypes.contains( droppedElement.type() ) )
{
list = null;
break;
}
}
if( list != null )
{
position = list.indexOf( trailingElement );
}
}
}
if( list == null )
{
for( PropertyDef dropTargetChildProperty : dropTargetNode.getChildNodeFactoryProperties() )
{
if( dropTargetChildProperty instanceof ListProperty && ! dropTargetChildProperty.isReadOnly() )
{
final ListProperty dropTargetChildListProperty = (ListProperty) dropTargetChildProperty;
boolean compatible = true;
final Set<ElementType> possibleListElementTypes = dropTargetChildListProperty.service( PossibleTypesService.class ).types();
for( final ElementData droppedElement : droppedElements )
{
if( ! possibleListElementTypes.contains( droppedElement.type() ) )
{
compatible = false;
break;
}
}
if( compatible )
{
list = dropTargetNode.getLocalModelElement().property( dropTargetChildListProperty );
position = list.size();
}
}
}
}
if( list == null )
{
event.detail = DND.DROP_NONE;
return;
}
// Prevent a drop within a drag element.
for( Property p = list; p != null; p = p.element().parent() )
{
for( final Element dragElement : dragElements )
{
if( p.element() == dragElement )
{
event.detail = DND.DROP_NONE;
return;
}
}
}
// Perform the removal and insertion into the new location.
final Disposable suspension = outline.listeners().queue().suspend( SelectionChangedEventFilter.INSTANCE );
try
{
if( event.detail == DND.DROP_MOVE )
{
for( Element dragElement : dragElements )
{
final ElementList<?> dragElementContainer = (ElementList<?>) dragElement.parent();
if( dragElementContainer == list && dragElementContainer.indexOf( dragElement ) < position )
{
position--;
}
dragElementContainer.remove( dragElement );
}
}
final List<MasterDetailsContentNodePart> newSelection = new ArrayList<MasterDetailsContentNodePart>();
for( final ElementData droppedElement : droppedElements )
{
final Element insertedElement = list.insert( droppedElement.type(), position );
insertedElement.copy( droppedElement );
newSelection.add( parentNode.findNode( insertedElement ) );
position++;
}
parentNode.getContentTree().setSelectedNodes( newSelection );
}
catch( Exception e )
{
// Log this exception unless the cause is EditFailedException. These exception
// are the result of the user declining a particular action that is necessary
// before the edit can happen (such as making a file writable).
final EditFailedException editFailedException = EditFailedException.findAsCause( e );
if( editFailedException == null )
{
Sapphire.service( LoggingService.class ).log( e );
}