Jump to: navigation, search

Graphical Modeling Framework/Recipes

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


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...


In the xxxCompartmentEditPart class rewrite the createFigure() method:

	public IFigure createFigure() {
		ResizableCompartmentFigure rcf = (ResizableCompartmentFigure) super.createFigure();
		FlowLayout layout = new FlowLayout();


		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:

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();
		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) {

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

		RepositionEObjectCommand command = new CompartmentRepositionEObjectCommand(child, editingDomain, "", 
				displacement, newIndex);	

		//TODO ev. reintroduce target feedback (actual problem: line is not deleted after dropping)

		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) {
		this.feature = feature;

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);
		return rs;

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


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.


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;
    protected CommandResult doExecuteWithResult(IProgressMonitor monitor, IAdaptable info)
    throws ExecutionException {

	View view =
	Assert.isNotNull(view, "failed to create a view"); //$NON-NLS-1$
    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 {

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

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

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

	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;
				} 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
		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();
		return bounds;