Jump to: navigation, search

Difference between revisions of "Graphical Modeling Framework/Recipes"

(Recipe: HowTo reorder children in a GMF compartment with Drag & Drop)
([Reorder-Chapter] Fixed problem that target feedback cannot be shown, my fix only removes it after dropping)
 
(4 intermediate revisions by 3 users not shown)
Line 35: Line 35:
  
 
<pre>
 
<pre>
public class CompartmentEditPolicy
+
package whatever.package.you.want;
extends org.eclipse.gef.editpolicies.FlowLayoutEditPolicy
+
 
{
+
import org.eclipse.core.commands.ExecutionException;
 +
import org.eclipse.core.runtime.IAdaptable;
 +
import org.eclipse.core.runtime.IProgressMonitor;
 +
import org.eclipse.emf.common.util.EList;
 +
import org.eclipse.emf.ecore.EObject;
 +
import org.eclipse.emf.transaction.TransactionalEditingDomain;
 +
import org.eclipse.gef.EditPart;
 +
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
 +
import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil;
 +
import org.eclipse.gmf.runtime.emf.commands.core.commands.RepositionEObjectCommand;
 +
import org.eclipse.gmf.runtime.notation.View;
 +
 
 +
public class CompartmentRepositionEObjectCommand extends RepositionEObjectCommand {
 +
 
 +
EditPart childToMove = null;
 +
int newIndex = 0;
 +
 +
public CompartmentRepositionEObjectCommand(
 +
TransactionalEditingDomain editingDomain, String label,
 +
EList elements, EObject element, int displacement) {
 +
super(editingDomain, label, elements, element, displacement);
 +
}
 +
 +
public CompartmentRepositionEObjectCommand(EditPart childToMove,
 +
TransactionalEditingDomain editingDomain, String label,
 +
EList elements, EObject element, int displacement, int newIndex) {
 +
super(editingDomain, label, elements, element, displacement);
 +
 +
this.childToMove = childToMove;
 +
this.newIndex = newIndex;
 +
}
 +
 +
public CommandResult doExecuteWithResult(
 +
IProgressMonitor progressMonitor, IAdaptable info)
 +
throws ExecutionException {
 +
CommandResult rs = super.doExecuteWithResult(progressMonitor, info);
 +
 +
EditPart compartment = childToMove.getParent();
 +
 +
ViewUtil.repositionChildAt((View)compartment.getModel(), (View)childToMove.getModel(), newIndex);
 +
compartment.refresh();
 +
 +
return rs;
 +
}
 +
}
 +
</pre>
 +
 
 +
<pre>
 +
package whatever.package.you.want;
 +
 
 +
import whatever.package.you.want.CompartmentRepositionEObjectCommand;
 +
import org.eclipse.emf.common.util.EList;
 +
import org.eclipse.emf.ecore.EStructuralFeature;
 +
import org.eclipse.emf.transaction.TransactionalEditingDomain;
 +
import org.eclipse.gef.EditPart;
 +
import org.eclipse.gef.EditPolicy;
 +
import org.eclipse.gef.Request;
 +
import org.eclipse.gef.commands.Command;
 +
import org.eclipse.gef.requests.CreateRequest;
 +
import org.eclipse.gmf.runtime.diagram.core.commands.AddCommand;
 +
import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy;
 +
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
 +
import org.eclipse.gmf.runtime.diagram.ui.editpolicies.ResizableEditPolicyEx;
 +
import org.eclipse.gmf.runtime.emf.commands.core.commands.RepositionEObjectCommand;
 +
import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter;
 +
import org.eclipse.gmf.runtime.notation.View;
 +
 
 +
public class CompartmentEditPolicy extends org.eclipse.gef.editpolicies.FlowLayoutEditPolicy {
  
 
private EStructuralFeature feature = null;
 
private EStructuralFeature feature = null;
Line 81: Line 148:
 
(EList)((View)child.getParent().getModel()).getElement().eGet(feature),  
 
(EList)((View)child.getParent().getModel()).getElement().eGet(feature),  
 
((View)child.getModel()).getElement(),  
 
((View)child.getModel()).getElement(),  
displacement, newIndex);
+
displacement, newIndex) {
  
//TODO ev. reintroduce target feedback (actual problem: line is not deleted after dropping)
+
 
eraseLayoutTargetFeedback(null);
+
public CommandResult doExecuteWithResult(
 +
            IProgressMonitor progressMonitor, IAdaptable info) {
 +
// remove target feedback after dropping
 +
eraseLayoutTargetFeedback(null);
 +
return super.doExecuteWithResult(progressMonitor, info);
 +
}
 +
};
  
 
return new ICommandProxy(command);
 
return new ICommandProxy(command);
Line 110: Line 183:
  
 
}
 
}
 +
 
</pre>
 
</pre>
 +
 +
 +
 +
== Recipe:  HowTo add children in a GMF compartment at a specific position==
 +
'''Problem:'''
 +
 +
You have created a node class which can contain other nodes. E.g. a class bookshelf that contains objects of the class book. Therefore you created a compartment. Now you want to add a book to the bookshelf at a specific position and not always at the end.
 +
 +
'''Solution:'''
 +
 +
In the xxxCompartmentEditPart class rewrite the createFigure() method:
  
 
<pre>
 
<pre>
public class CompartmentRepositionEObjectCommand extends RepositionEObjectCommand {
+
public IFigure createFigure() {
 +
 +
ResizableCompartmentFigure rcf = (ResizableCompartmentFigure) super.createFigure();
 +
FlowLayout layout = new FlowLayout();
 +
layout.setMajorSpacing(getMapMode().DPtoLP(5));
 +
layout.setMinorSpacing(getMapMode().DPtoLP(5));
 +
layout.setHorizontal(false);
  
EditPart childToMove = null;
+
rcf.getContentPane().setLayoutManager(layout);
int newIndex = 0;
+
 
 +
rcf.setTitleVisibility(false);
 +
 +
return rcf;
 +
}
 +
</pre>
 +
 
 +
In the createDefaultEditPolicies() method of the xxxCompartmentEditPart class change the following EditPolicy:
 +
<pre>
 +
  - change
 +
installEditPolicy(EditPolicyRoles.CREATION_ROLE, new CreationEditPolicy());
 +
  + to
 +
installEditPolicy(EditPolicyRoles.CREATION_ROLE, new CompartmentChildCreationEditPolicy());
 +
</pre>
 +
 
 +
and add the CompartmentChildCreateCommand and CompartmentChildCreationEditPolicy classes:
 +
 
 +
 
 +
<pre>
 +
package org.diagram.edit.commands;
 +
 
 +
import org.eclipse.core.commands.ExecutionException;
 +
import org.eclipse.core.runtime.IAdaptable;
 +
import org.eclipse.core.runtime.IProgressMonitor;
 +
import org.eclipse.emf.transaction.TransactionalEditingDomain;
 +
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
 +
import org.eclipse.gmf.runtime.diagram.core.services.ViewService;
 +
import org.eclipse.gmf.runtime.diagram.ui.commands.CreateCommand;
 +
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest.ViewDescriptor;
 +
import org.eclipse.gmf.runtime.notation.View;
 +
import org.eclipse.jface.util.Assert;
 +
 
 +
public class CompartmentChildCreateCommand extends CreateCommand {
 +
 
 +
int index;
 
 
public CompartmentRepositionEObjectCommand(
+
public CompartmentChildCreateCommand (TransactionalEditingDomain editingDomain, ViewDescriptor viewDescriptor,
TransactionalEditingDomain editingDomain, String label,
+
View containerView, int index) {
EList elements, EObject element, int displacement) {
+
super(editingDomain, viewDescriptor, containerView);
super(editingDomain, label, elements, element, displacement);
+
this.index = index;
 
}
 
}
 
 
public CompartmentRepositionEObjectCommand(EditPart childToMove,
+
@Override
TransactionalEditingDomain editingDomain, String label,
+
    protected CommandResult doExecuteWithResult(IProgressMonitor monitor, IAdaptable info)
EList elements, EObject element, int displacement, int newIndex) {
+
    throws ExecutionException {
super(editingDomain, label, elements, element, displacement);
+
 
+
View view =
this.childToMove = childToMove;
+
ViewService.getInstance().createView(
this.newIndex = newIndex;
+
viewDescriptor.getViewKind(),
 +
viewDescriptor.getElementAdapter(),
 +
containerView,
 +
viewDescriptor.getSemanticHint(),
 +
index,
 +
viewDescriptor.isPersisted(),
 +
viewDescriptor.getPreferencesHint());
 +
Assert.isNotNull(view, "failed to create a view"); //$NON-NLS-1$
 +
viewDescriptor.setView(view);
 +
   
 +
    return CommandResult.newOKCommandResult(viewDescriptor);
 +
}
 +
 +
}
 +
 
 +
</pre>
 +
 
 +
<pre>
 +
package org.diagram.edit.policies;
 +
 
 +
import java.util.Iterator;
 +
import java.util.List;
 +
 
 +
import org.diagram.edit.commands.CompartmentChildCreateCommand;
 +
import org.eclipse.draw2d.FlowLayout;
 +
import org.eclipse.draw2d.IFigure;
 +
import org.eclipse.draw2d.geometry.Point;
 +
import org.eclipse.draw2d.geometry.Rectangle;
 +
import org.eclipse.draw2d.geometry.Transposer;
 +
import org.eclipse.emf.transaction.TransactionalEditingDomain;
 +
import org.eclipse.gef.EditPart;
 +
import org.eclipse.gef.GraphicalEditPart;
 +
import org.eclipse.gef.Request;
 +
import org.eclipse.gef.commands.Command;
 +
import org.eclipse.gef.requests.DropRequest;
 +
import org.eclipse.gmf.runtime.diagram.ui.commands.CreateCommand;
 +
import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy;
 +
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
 +
import org.eclipse.gmf.runtime.diagram.ui.editpolicies.CreationEditPolicy;
 +
import org.eclipse.gmf.runtime.diagram.ui.l10n.DiagramUIMessages;
 +
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest;
 +
import org.eclipse.gmf.runtime.emf.commands.core.command.CompositeTransactionalCommand;
 +
import org.eclipse.gmf.runtime.notation.View;
 +
 
 +
public class CompartmentChildCreationEditPolicy extends CreationEditPolicy {
 +
 
 +
@Override
 +
protected Command getCreateCommand(CreateViewRequest request) {
 +
        TransactionalEditingDomain editingDomain = ((IGraphicalEditPart) getHost())
 +
        .getEditingDomain();
 +
    CompositeTransactionalCommand cc = new CompositeTransactionalCommand(
 +
        editingDomain, DiagramUIMessages.AddCommand_Label);
 +
   
 +
    Iterator descriptors = request.getViewDescriptors().iterator();
 +
 
 +
while (descriptors.hasNext()) {
 +
CreateViewRequest.ViewDescriptor descriptor =
 +
(CreateViewRequest.ViewDescriptor)descriptors.next();
 +
 
 +
CreateCommand createCommand =
 +
new CompartmentChildCreateCommand(editingDomain,
 +
descriptor,
 +
(View)(getHost().getModel()), getFeedbackIndexFor(request));
 +
 
 +
cc.compose(createCommand);
 
}
 
}
 +
return new ICommandProxy(cc.reduce());
 +
}
 +
 
 
public CommandResult doExecuteWithResult(
+
protected int getFeedbackIndexFor(Request request) {
IProgressMonitor progressMonitor, IAdaptable info)
+
List children = getHost().getChildren();
throws ExecutionException {
+
if (children.isEmpty())
CommandResult rs = super.doExecuteWithResult(progressMonitor, info);
+
return -1;
+
EditPart compartment = childToMove.getParent();  
+
Transposer transposer = new Transposer();
 +
transposer.setEnabled (!isHorizontal());
 
 
ViewUtil.repositionChildAt((View)compartment.getModel(), (View)childToMove.getModel(), newIndex);
+
Point p = transposer.t(getLocationFromRequest(request));
compartment.refresh();
+
 
+
// Current row bottom, initialize to above the top.
return rs;
+
int rowBottom = Integer.MIN_VALUE;
 +
int candidate = -1;
 +
for (int i = 0; i < children.size(); i++) {
 +
EditPart child = (EditPart) children.get(i);
 +
Rectangle rect = transposer.t(getAbsoluteBounds(((GraphicalEditPart)child)));
 +
if (rect.y > rowBottom) {
 +
/*
 +
* We are in a new row, so if we don't have a candidate but yet are within the
 +
* previous row, then the current entry becomes the candidate. This is because
 +
* we know we must be to the right of center of the last Figure in the
 +
* previous row, so this Figure (which is at the start of a new row) is the
 +
* candidate.
 +
*/
 +
if (p.y <= rowBottom) {
 +
if (candidate == -1)
 +
candidate = i;
 +
break;
 +
} else
 +
candidate = -1; // Mouse's Y is outside the row, so reset the candidate
 +
}
 +
rowBottom = Math.max(rowBottom, rect.bottom());
 +
if (candidate == -1) {
 +
/*
 +
* See if we have a possible candidate. It is a candidate if the cursor is
 +
* left of the center of this candidate.
 +
*/
 +
if (p.x <= rect.x + (rect.width / 2))
 +
candidate = i;
 +
}
 +
if (candidate != -1) {
 +
// We have a candidate, see if the rowBottom has grown to include the mouse Y.
 +
if (p.y <= rowBottom) {
 +
/*
 +
* Now we have determined that the cursor.Y is above the bottom of the
 +
* current row of figures. Stop now, to prevent the next row from being
 +
* searched
 +
*/
 +
break;
 +
}
 +
}
 +
}
 +
return candidate;
 +
}
 +
 +
protected boolean isHorizontal() {
 +
IFigure figure = ((GraphicalEditPart)getHost()).getContentPane();
 +
return ((FlowLayout)figure.getLayoutManager()).isHorizontal();
 +
}
 +
 +
private Point getLocationFromRequest(Request request) {
 +
return ((DropRequest)request).getLocation();
 +
}
 +
 +
private Rectangle getAbsoluteBounds(GraphicalEditPart ep) {
 +
Rectangle bounds = ep.getFigure().getBounds().getCopy();
 +
ep.getFigure().translateToAbsolute(bounds);
 +
return bounds;
 
}
 
}
 
}
 
}
 +
 
</pre>
 
</pre>
  
 
[[Category:GMF]]
 
[[Category:GMF]]

Latest revision as of 11:12, 12 July 2013

Recipe: HowTo reorder children in a GMF compartment with Drag & Drop

Problem:

You have created a node class which can contain other nodes. E.g. a class bookshelf that contains objects of the class book. Therefore you created a compartment. After adding books to the bookshelf in your editor, you want to rearrange the books by drag & drop. But it doesn't work...

Solution:

In the xxxCompartmentEditPart class rewrite the createFigure() method:

	public IFigure createFigure() {
		
		ResizableCompartmentFigure rcf = (ResizableCompartmentFigure) super.createFigure();
		FlowLayout layout = new FlowLayout();
		layout.setMajorSpacing(getMapMode().DPtoLP(5));
		layout.setMinorSpacing(getMapMode().DPtoLP(5));
		layout.setHorizontal(false);

		rcf.getContentPane().setLayoutManager(layout);

		rcf.setTitleVisibility(false);
		
		return rcf;
	}

In the createDefaultEditPolicies() method of the xxxCompartmentEditPart class add an EditPolicy:

installEditPolicy(EditPolicy.LAYOUT_ROLE, new CompartmentEditPolicy(xxxPackage.Literals.xxx));

Note: xxxPackage.Literals.xxx has to be the EStructuralFeature of the EList Attribute that contains the Objects which should be reordered.

Use the classes CompartmentEditPolicy and CompartmentRepositionEObjectCommand:

package whatever.package.you.want;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gef.EditPart;
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil;
import org.eclipse.gmf.runtime.emf.commands.core.commands.RepositionEObjectCommand;
import org.eclipse.gmf.runtime.notation.View;

public class CompartmentRepositionEObjectCommand extends RepositionEObjectCommand {

	EditPart childToMove = null;
	int newIndex = 0;
	
	public CompartmentRepositionEObjectCommand(
			TransactionalEditingDomain editingDomain, String label,
			EList elements, EObject element, int displacement) {
		super(editingDomain, label, elements, element, displacement);
	}
	
	public CompartmentRepositionEObjectCommand(EditPart childToMove,
			TransactionalEditingDomain editingDomain, String label,
			EList elements, EObject element, int displacement, int newIndex) {
		super(editingDomain, label, elements, element, displacement);
		
		this.childToMove = childToMove;
		this.newIndex = newIndex;
	}
	
	public CommandResult doExecuteWithResult(
			IProgressMonitor progressMonitor, IAdaptable info)
			throws ExecutionException {
		CommandResult rs = super.doExecuteWithResult(progressMonitor, info);
				
		EditPart compartment = childToMove.getParent(); 
		
		ViewUtil.repositionChildAt((View)compartment.getModel(), (View)childToMove.getModel(), newIndex);
		compartment.refresh();		
		
		return rs;
	}
}
package whatever.package.you.want;

import whatever.package.you.want.CompartmentRepositionEObjectCommand;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.requests.CreateRequest;
import org.eclipse.gmf.runtime.diagram.core.commands.AddCommand;
import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editpolicies.ResizableEditPolicyEx;
import org.eclipse.gmf.runtime.emf.commands.core.commands.RepositionEObjectCommand;
import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter;
import org.eclipse.gmf.runtime.notation.View;

public class CompartmentEditPolicy extends org.eclipse.gef.editpolicies.FlowLayoutEditPolicy {

	private EStructuralFeature feature = null;

	protected Command createAddCommand(EditPart child, EditPart after) {
		int index = getHost().getChildren().indexOf(after);
		TransactionalEditingDomain editingDomain = ((IGraphicalEditPart) getHost()).getEditingDomain();
		AddCommand command = new AddCommand(editingDomain, new EObjectAdapter((View)getHost().getModel()),
				new EObjectAdapter((View)child.getModel()), index);
		return new ICommandProxy(command);
	}

	protected EditPolicy createChildEditPolicy(EditPart child) {
		ResizableEditPolicyEx policy = new ResizableEditPolicyEx();
		policy.setResizeDirections(0);
		return policy;
	}

	protected Command createMoveChildCommand(EditPart child, EditPart after) {	

		int newIndex;
		int displacement;

		int childIndex = getHost().getChildren().indexOf(child);
		int afterIndex = getHost().getChildren().indexOf(after);	

		if(afterIndex == -1) {
			newIndex = getHost().getChildren().size()-1;			
			displacement = newIndex - childIndex;
		} else {		
			newIndex = afterIndex;
			displacement = afterIndex - childIndex;
			if (childIndex <= afterIndex) {
				newIndex--;
				displacement--;			
			}
		}


		TransactionalEditingDomain editingDomain = ((IGraphicalEditPart) getHost()).getEditingDomain();

		RepositionEObjectCommand command = new CompartmentRepositionEObjectCommand(child, editingDomain, "", 
				(EList)((View)child.getParent().getModel()).getElement().eGet(feature), 
				((View)child.getModel()).getElement(), 
				displacement, newIndex) {


			public CommandResult doExecuteWithResult(
		            IProgressMonitor progressMonitor, IAdaptable info) {
				// remove target feedback after dropping
				eraseLayoutTargetFeedback(null);
				return super.doExecuteWithResult(progressMonitor, info);
			}
		};

		return new ICommandProxy(command);
	}

	protected Command getCreateCommand(CreateRequest request) {
		return null;
	}

	protected Command getDeleteDependantCommand(Request request) {
		return null;
	}

	protected Command getOrphanChildrenCommand(Request request) {
		return null;
	}

	/**
	 * @param feature has to be an EList
	 */
	public CompartmentEditPolicy(EStructuralFeature feature) {
		super();
		this.feature = feature;
	}

}


Recipe: HowTo add children in a GMF compartment at a specific position

Problem:

You have created a node class which can contain other nodes. E.g. a class bookshelf that contains objects of the class book. Therefore you created a compartment. Now you want to add a book to the bookshelf at a specific position and not always at the end.

Solution:

In the xxxCompartmentEditPart class rewrite the createFigure() method:

	public IFigure createFigure() {
		
		ResizableCompartmentFigure rcf = (ResizableCompartmentFigure) super.createFigure();
		FlowLayout layout = new FlowLayout();
		layout.setMajorSpacing(getMapMode().DPtoLP(5));
		layout.setMinorSpacing(getMapMode().DPtoLP(5));
		layout.setHorizontal(false);

		rcf.getContentPane().setLayoutManager(layout);

		rcf.setTitleVisibility(false);
		
		return rcf;
	}

In the createDefaultEditPolicies() method of the xxxCompartmentEditPart class change the following EditPolicy:

  - change
installEditPolicy(EditPolicyRoles.CREATION_ROLE, new CreationEditPolicy());
  + to
installEditPolicy(EditPolicyRoles.CREATION_ROLE, new CompartmentChildCreationEditPolicy());

and add the CompartmentChildCreateCommand and CompartmentChildCreationEditPolicy classes:


package org.diagram.edit.commands;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
import org.eclipse.gmf.runtime.diagram.core.services.ViewService;
import org.eclipse.gmf.runtime.diagram.ui.commands.CreateCommand;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest.ViewDescriptor;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.jface.util.Assert;

public class CompartmentChildCreateCommand extends CreateCommand {

	int index;
	
	public CompartmentChildCreateCommand (TransactionalEditingDomain editingDomain, ViewDescriptor viewDescriptor,
			View containerView, int index) {
		super(editingDomain, viewDescriptor, containerView);
		this.index = index;
	}
	
	@Override
    protected CommandResult doExecuteWithResult(IProgressMonitor monitor, IAdaptable info)
    throws ExecutionException {

	View view =
		ViewService.getInstance().createView(
			viewDescriptor.getViewKind(),
			viewDescriptor.getElementAdapter(),
			containerView,
			viewDescriptor.getSemanticHint(),
			index,
			viewDescriptor.isPersisted(),
			viewDescriptor.getPreferencesHint());
	Assert.isNotNull(view, "failed to create a view"); //$NON-NLS-1$
	viewDescriptor.setView(view);
    
    return CommandResult.newOKCommandResult(viewDescriptor);
}
	
}

package org.diagram.edit.policies;

import java.util.Iterator;
import java.util.List;

import org.diagram.edit.commands.CompartmentChildCreateCommand;
import org.eclipse.draw2d.FlowLayout;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.draw2d.geometry.Transposer;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.requests.DropRequest;
import org.eclipse.gmf.runtime.diagram.ui.commands.CreateCommand;
import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editpolicies.CreationEditPolicy;
import org.eclipse.gmf.runtime.diagram.ui.l10n.DiagramUIMessages;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest;
import org.eclipse.gmf.runtime.emf.commands.core.command.CompositeTransactionalCommand;
import org.eclipse.gmf.runtime.notation.View;

public class CompartmentChildCreationEditPolicy extends CreationEditPolicy {

	@Override
	protected Command getCreateCommand(CreateViewRequest request) {
        TransactionalEditingDomain editingDomain = ((IGraphicalEditPart) getHost())
        .getEditingDomain();
    CompositeTransactionalCommand cc = new CompositeTransactionalCommand(
        editingDomain, DiagramUIMessages.AddCommand_Label);
    
    Iterator descriptors = request.getViewDescriptors().iterator();

	while (descriptors.hasNext()) {
		CreateViewRequest.ViewDescriptor descriptor =
			(CreateViewRequest.ViewDescriptor)descriptors.next();

		CreateCommand createCommand =
			new CompartmentChildCreateCommand(editingDomain,
				descriptor, 
				(View)(getHost().getModel()), getFeedbackIndexFor(request));

		cc.compose(createCommand);
	}
	return new ICommandProxy(cc.reduce());
	}

	
	protected int getFeedbackIndexFor(Request request) {
		List children = getHost().getChildren();
		if (children.isEmpty())
			return -1;
			
		Transposer transposer = new Transposer();
		transposer.setEnabled (!isHorizontal());
		
		Point p = transposer.t(getLocationFromRequest(request));

		// Current row bottom, initialize to above the top.
		int rowBottom = Integer.MIN_VALUE;
		int candidate = -1;
		for (int i = 0; i < children.size(); i++) {
			EditPart child = (EditPart) children.get(i);
			Rectangle rect = transposer.t(getAbsoluteBounds(((GraphicalEditPart)child)));
			if (rect.y > rowBottom) {
				/*
				 * We are in a new row, so if we don't have a candidate but yet are within the
				 * previous row, then the current entry becomes the candidate. This is because
				 * we know we must be to the right of center of the last Figure in the
				 * previous row, so this Figure (which is at the start of a new row) is the
				 * candidate.
				 */
				if (p.y <= rowBottom) {
					if (candidate == -1)
						candidate = i;
					break;
				} else
					candidate = -1; // Mouse's Y is outside the row, so reset the candidate
			}
			rowBottom = Math.max(rowBottom, rect.bottom());
			if (candidate == -1) {
				/*
				 * See if we have a possible candidate. It is a candidate if the cursor is
				 * left of the center of this candidate.
				 */
				if (p.x <= rect.x + (rect.width / 2))
					candidate = i;
			}
			if (candidate != -1) {
				// We have a candidate, see if the rowBottom has grown to include the mouse Y.
				if (p.y <= rowBottom) {
					/*
					 * Now we have determined that the cursor.Y is above the bottom of the
					 * current row of figures. Stop now, to prevent the next row from being
					 * searched
					 */
					break;
				}
			}
		}
		return candidate;
	}
	
	protected boolean isHorizontal() {
		IFigure figure = ((GraphicalEditPart)getHost()).getContentPane();
		return ((FlowLayout)figure.getLayoutManager()).isHorizontal();
	}
	
	private Point getLocationFromRequest(Request request) {
		return ((DropRequest)request).getLocation();
	}
	
	private Rectangle getAbsoluteBounds(GraphicalEditPart ep) {
		Rectangle bounds = ep.getFigure().getBounds().getCopy();
		ep.getFigure().translateToAbsolute(bounds);
		return bounds;
	}
}