/* * Copyright 2017 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kie.workbench.common.stunner.cm.client.canvas.controls.containment; import java.util.Optional; import javax.enterprise.context.Dependent; import javax.inject.Inject; import com.ait.lienzo.client.core.shape.wires.IContainmentAcceptor; import com.ait.lienzo.client.core.shape.wires.ILayoutHandler; import com.ait.lienzo.client.core.shape.wires.WiresContainer; import com.ait.lienzo.client.core.shape.wires.WiresShape; import com.ait.lienzo.client.core.types.Point2D; import org.kie.workbench.common.stunner.client.lienzo.canvas.controls.AbstractContainmentBasedControl; import org.kie.workbench.common.stunner.client.lienzo.canvas.wires.WiresCanvas; import org.kie.workbench.common.stunner.client.lienzo.canvas.wires.WiresUtils; import org.kie.workbench.common.stunner.cm.client.command.CaseManagementCanvasCommandFactory; import org.kie.workbench.common.stunner.cm.client.wires.CaseManagementContainmentStateHolder; import org.kie.workbench.common.stunner.cm.qualifiers.CaseManagementEditor; import org.kie.workbench.common.stunner.core.client.canvas.AbstractCanvasHandler; import org.kie.workbench.common.stunner.core.client.canvas.controls.containment.ContainmentAcceptorControl; import org.kie.workbench.common.stunner.core.client.command.CanvasViolation; import org.kie.workbench.common.stunner.core.command.Command; import org.kie.workbench.common.stunner.core.graph.Edge; import org.kie.workbench.common.stunner.core.graph.Node; import org.kie.workbench.common.stunner.core.graph.content.relationship.Child; @Dependent @CaseManagementEditor public class CaseManagementContainmentAcceptorControlImpl extends AbstractContainmentBasedControl implements ContainmentAcceptorControl<AbstractCanvasHandler> { private final IContainmentAcceptor CONTAINMENT_ACCEPTOR = new CanvasManagementContainmentAcceptor(); private final CaseManagementCanvasCommandFactory canvasCommandFactory; private final CaseManagementContainmentStateHolder state; @Inject public CaseManagementContainmentAcceptorControlImpl(final @CaseManagementEditor CaseManagementCanvasCommandFactory canvasCommandFactory, final CaseManagementContainmentStateHolder state) { this.canvasCommandFactory = canvasCommandFactory; this.state = state; } @Override protected void doEnable(final WiresCanvas.View view) { view.setContainmentAcceptor(CONTAINMENT_ACCEPTOR); } @Override protected void doDisable(final WiresCanvas.View view) { view.setContainmentAcceptor(IContainmentAcceptor.NONE); } @Override protected boolean isEdgeAccepted(final Edge edge) { return edge.getContent() instanceof Child; } @Override protected Command<AbstractCanvasHandler, CanvasViolation> getAddEdgeCommand(final Node parent, final Node child) { return canvasCommandFactory.setChildNode(parent, child); } protected Command<AbstractCanvasHandler, CanvasViolation> getSetEdgeCommand(final Node parent, final Node child, final Optional<Integer> index, final Optional<Node> originalParent, final Optional<Integer> originalIndex) { return canvasCommandFactory.setChildNode(parent, child, index, originalParent, originalIndex); } @Override protected Command<AbstractCanvasHandler, CanvasViolation> getDeleteEdgeCommand(final Node parent, final Node child) { return canvasCommandFactory.removeChild(parent, child); } class CanvasManagementContainmentAcceptor implements IContainmentAcceptor { @Override public boolean containmentAllowed(final WiresContainer wiresContainer, final WiresShape wiresShape) { if (!isAccept(wiresContainer, wiresShape)) { return false; } final Node childNode = WiresUtils.getNode(getCanvasHandler(), wiresShape); final Node parentNode = WiresUtils.getNode(getCanvasHandler(), wiresContainer); return allow(parentNode, childNode); } @Override public boolean acceptContainment(final WiresContainer wiresContainer, final WiresShape wiresShape) { // Check containment is allowed. This (almost) replicates AbstractContainmentBasedControl.accept() // that has the additional check whether a Child can be removed from a Container; but for Case Management // that is not a concern. final boolean isAccept = containmentAllowed(wiresContainer, wiresShape); // We have some interesting fun here; "accept" is called before the child WiresShape has been // added to the parent WiresShape or in the correct position (index) as determined by the parent's // ILayoutHandler. We therefore delay execution of the Command to mutate the Graph until the // WiresContainer has completed positioning the child. if (isAccept) { wiresContainer.setLayoutHandler(new InterceptingLayoutHandler(wiresContainer)); } return isAccept; } } class InterceptingLayoutHandler implements ILayoutHandler { private final WiresContainer container; private final ILayoutHandler layout; private InterceptingLayoutHandler(final WiresContainer container) { this.container = container; this.layout = container.getLayoutHandler(); } @Override public void add(final WiresShape shape, final WiresContainer container, final Point2D mouseRelativeLoc) { try { this.layout.add(shape, container, mouseRelativeLoc); final WiresContainer newContainer = shape.getParent(); final Optional<Integer> newIndex = Optional.of(newContainer.getChildShapes().toList().indexOf(shape)); final Optional<WiresContainer> originalContainer = state.getOriginalParent(); final Optional<Integer> originalIndex = state.getOriginalIndex(); getCommandManager().execute(getCanvasHandler(), makeAddMutationCommand(shape, newContainer, newIndex, originalContainer, originalIndex)); } finally { this.container.setLayoutHandler(layout); } } @Override public void remove(final WiresShape shape, final WiresContainer container) { try { this.layout.remove(shape, container); } finally { this.container.setLayoutHandler(layout); } } protected Command<AbstractCanvasHandler, CanvasViolation> makeAddMutationCommand(final WiresShape shape, final WiresContainer container, final Optional<Integer> index, final Optional<WiresContainer> originalContainer, final Optional<Integer> originalIndex) { final Node parent = WiresUtils.getNode(getCanvasHandler(), container); final Node child = WiresUtils.getNode(getCanvasHandler(), shape); final Optional<Node> originalParent = originalContainer.flatMap((c) -> Optional.ofNullable(WiresUtils.getNode(getCanvasHandler(), c))); // Set relationship. return getSetEdgeCommand(parent, child, index, originalParent, originalIndex); } @Override public void requestLayout(final WiresContainer container) { this.layout.requestLayout(container); } @Override public void layout(final WiresContainer container) { this.layout.layout(container); } } }