/***************************************************************************** * Copyright (c) 2012 CEA LIST. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation *****************************************************************************/ package org.eclipse.papyrus.infra.gmfdiag.dnd.policy; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; 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.gef.EditPolicy; import org.eclipse.gef.Request; import org.eclipse.gef.commands.Command; import org.eclipse.gmf.runtime.common.core.command.CommandResult; import org.eclipse.gmf.runtime.common.core.command.ICommand; import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy; import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; import org.eclipse.gmf.runtime.diagram.ui.editpolicies.DragDropEditPolicy; import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest; import org.eclipse.gmf.runtime.diagram.ui.requests.DropObjectsRequest; import org.eclipse.gmf.runtime.diagram.ui.requests.RequestConstants; import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand; import org.eclipse.gmf.runtime.notation.Bounds; import org.eclipse.gmf.runtime.notation.LayoutConstraint; import org.eclipse.gmf.runtime.notation.Shape; import org.eclipse.papyrus.infra.emf.utils.EMFHelper; import org.eclipse.papyrus.infra.gmfdiag.common.commands.DefaultActionHandler; import org.eclipse.papyrus.infra.gmfdiag.common.commands.SelectAndExecuteCommand; import org.eclipse.papyrus.infra.gmfdiag.dnd.Activator; import org.eclipse.papyrus.infra.gmfdiag.dnd.strategy.DefaultDropStrategy; import org.eclipse.papyrus.infra.gmfdiag.dnd.strategy.DropStrategy; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.PlatformUI; /** * An EditPolicy to handle Drop in Papyrus diagrams. * This edit policy can be customized from an extension point. If a customization * is not available, it will delegate the behavior to the default Drop edit policy * * @author Camille Letavernier * */ public class CustomizableDropEditPolicy extends DragDropEditPolicy { public static final String EXTENSION_ID = Activator.PLUGIN_ID + ".dropStrategy"; protected EditPolicy defaultDropEditPolicy; protected EditPolicy defaultCreationEditPolicy; protected DropStrategy defaultDropStrategy; //FIXME: This comes from oep.uml.diagram.common.listener.DropTargetListener //This should be merged to oep.infra.gmfdiag.common, as this is not specific to UML public static final String EVENT_DETAIL = "EVENT_DETAIL"; @Override public void activate() { //Nothing } /** * Instantiates a new CustomizableDropEditPolicy * * @param defaultEditPolicy * The default editPolicy, to be called when no custom Drop strategy is available */ public CustomizableDropEditPolicy(EditPolicy defaultDropEditPolicy, EditPolicy defaultCreationEditPolicy) { this.defaultDropEditPolicy = defaultDropEditPolicy; this.defaultCreationEditPolicy = defaultCreationEditPolicy; this.defaultDropStrategy = new DefaultDropStrategy(defaultDropEditPolicy, defaultCreationEditPolicy); } @Override public Command getCommand(final Request request) { Command command; if(super.understandsRequest(request)) { //Drag & Drop request try { command = super.getCommand(request); //Will call this.getDropObjectsCommand() eventually } catch (Exception ex) { command = getCustomCommand(request); } } else if(this.understands(request)) { //Add request command = getCreationCommand(request); } else if(defaultCreationEditPolicy != null) { //Creation request if(defaultCreationEditPolicy.getTargetEditPart(request) != getTargetEditPart(request)) { command = defaultCreationEditPolicy.getTargetEditPart(request).getCommand(request); } else { command = defaultCreationEditPolicy.getCommand(request); } } else { command = null; } if(command == null) { return null; } return command; } @Override public boolean understandsRequest(Request request) { return this.understands(request) || (defaultCreationEditPolicy != null && defaultCreationEditPolicy.understandsRequest(request)) || (defaultDropEditPolicy != null && defaultDropEditPolicy.understandsRequest(request)); } protected boolean understands(Request request) { return RequestConstants.REQ_ADD.equals(request.getType()); } protected Command getCreationCommand(Request request) { return getCustomCommand(request); } /** * {@inheritDoc} */ @Override protected Command getDropObjectsCommand(DropObjectsRequest request) { Command dropCommand = getCustomCommand(request); if(dropCommand != null && dropCommand.canExecute() && request.getObjects().size() > 1) { return layoutDroppedObjects(dropCommand); } return dropCommand; } protected Command layoutDroppedObjects(final Command dropCommand) { AbstractTransactionalCommand spacingCommand = new AbstractTransactionalCommand((TransactionalEditingDomain)EMFHelper.resolveEditingDomain(getHost()), "Spacing elements", Collections.EMPTY_LIST) { @Override protected CommandResult doExecuteWithResult(IProgressMonitor progressMonitor, IAdaptable info) throws ExecutionException { if(dropCommand instanceof ICommandProxy) { ICommand gmfCommand = ((ICommandProxy)dropCommand).getICommand(); CommandResult previousCommandResult = gmfCommand.getCommandResult(); if(previousCommandResult != null) { Object returnValue = previousCommandResult.getReturnValue(); if(returnValue instanceof List<?>) { List<?> returnedElements = (List<?>)returnValue; int i = 0; for(Object returnedElement : returnedElements) { if(returnedElement instanceof CreateViewRequest.ViewDescriptor) { CreateViewRequest.ViewDescriptor newViewDescriptor = (CreateViewRequest.ViewDescriptor)returnedElement; Shape newShape = (Shape)newViewDescriptor.getAdapter(Shape.class); if(newShape != null) { LayoutConstraint constraint = newShape.getLayoutConstraint(); if(constraint instanceof Bounds) { Bounds bounds = (Bounds)constraint; updateBounds(bounds, i); i++; } } } } } } } return CommandResult.newOKCommandResult(); } }; return dropCommand.chain(new ICommandProxy(spacingCommand)); } protected void updateBounds(Bounds bounds, int position) { int x = bounds.getX(); int y = bounds.getY(); bounds.setX(x + 15 * position); bounds.setY(y + 15 * position); } /** * Returns the command from a Custom DropStrategy * * @param request * @return */ protected Command getCustomCommand(Request request) { final Map<DropStrategy, Command> matchingStrategies = findStrategies(request); //Only one strategy: return the associated command if(matchingStrategies.size() == 1) { return matchingStrategies.values().iterator().next(); } //More than one strategy if(matchingStrategies.size() > 1) { boolean useDefault = true; //FIXME: What's the exact semantic of EVENT_DETAIL=DND_COPY in Papyrus? //Currently, DND_COPY corresponds to Ctrl + Drag/Drop if(request.getExtendedData().containsKey(EVENT_DETAIL)) { int eventDetailValue = (Integer)request.getExtendedData().get(EVENT_DETAIL); if((eventDetailValue & DND.DROP_COPY) != 0) { useDefault = false; } } //Search for a default strategy if(useDefault) { DropStrategy defaultStrategy = DropStrategyManager.instance.getDefaultDropStrategy(matchingStrategies.keySet()); if(defaultStrategy != null) { return matchingStrategies.get(defaultStrategy); } } //If there is no default choice, ask user (Open a choice List) Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); DefaultActionHandler handler = new DefaultActionHandler() { public void defaultActionSelected(Command defaultCommand) { DropStrategy defaultStrategy = findStrategy(matchingStrategies, defaultCommand); if(defaultStrategy != null) { DropStrategyManager.instance.setDefaultDropStrategy(matchingStrategies.keySet(), defaultStrategy); } } public String getLabel() { return "Change the default strategy"; } }; SelectAndExecuteCommand command = new SelectAndExecuteCommand("Select drop", shell, new LinkedList<Command>(matchingStrategies.values()), handler); return new ICommandProxy(command); } //No matching strategy return null; } private static DropStrategy findStrategy(Map<DropStrategy, Command> matchingStrategies, Command command) { for(Map.Entry<DropStrategy, Command> entry : matchingStrategies.entrySet()) { if(entry.getValue() == command) { return entry.getKey(); } } return null; } /** * Returns a map of DropStrategy / Command, for each Strategy which can handle * the given request. All the returned commands are executable. The map may be empty. * * @param request * @return */ protected Map<DropStrategy, Command> findStrategies(Request request) { Map<DropStrategy, Command> matchingStrategies = new LinkedHashMap<DropStrategy, Command>(); for(DropStrategy strategy : DropStrategyManager.instance.getActiveStrategies()) { Command command = strategy.getCommand(request, getHost()); if(command != null && command.canExecute()) { matchingStrategies.put(strategy, command); } } Command command = defaultDropStrategy.getCommand(request, getHost()); if(command != null && command.canExecute()) { matchingStrategies.put(defaultDropStrategy, command); } return matchingStrategies; } /** * @see org.eclipse.gef.EditPolicy#showTargetFeedback(org.eclipse.gef.Request) */ @Override public void showTargetFeedback(Request request) { if(!(getHost() instanceof DiagramEditPart)) { super.showTargetFeedback(request); } } }