/*
* 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.core.client.canvas.controls.toolbox.command.builder;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.enterprise.context.Dependent;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import com.google.gwt.logging.client.LogConfiguration;
import org.kie.workbench.common.stunner.core.client.api.ShapeManager;
import org.kie.workbench.common.stunner.core.client.canvas.AbstractCanvasHandler;
import org.kie.workbench.common.stunner.core.client.canvas.Layer;
import org.kie.workbench.common.stunner.core.client.canvas.controls.builder.BuildRequest;
import org.kie.workbench.common.stunner.core.client.canvas.controls.builder.BuilderControl;
import org.kie.workbench.common.stunner.core.client.canvas.controls.builder.NodeBuilderControl;
import org.kie.workbench.common.stunner.core.client.canvas.controls.builder.request.NodeBuildRequest;
import org.kie.workbench.common.stunner.core.client.canvas.controls.builder.request.NodeBuildRequestImpl;
import org.kie.workbench.common.stunner.core.client.canvas.controls.toolbox.command.Context;
import org.kie.workbench.common.stunner.core.client.canvas.event.selection.CanvasElementSelectedEvent;
import org.kie.workbench.common.stunner.core.client.canvas.util.CanvasLayoutUtils;
import org.kie.workbench.common.stunner.core.client.components.drag.DragProxy;
import org.kie.workbench.common.stunner.core.client.components.drag.DragProxyCallback;
import org.kie.workbench.common.stunner.core.client.components.drag.NodeDragProxy;
import org.kie.workbench.common.stunner.core.client.components.drag.NodeDragProxyCallback;
import org.kie.workbench.common.stunner.core.client.components.glyph.DefinitionGlyphTooltip;
import org.kie.workbench.common.stunner.core.client.service.ClientFactoryService;
import org.kie.workbench.common.stunner.core.client.service.ClientRuntimeError;
import org.kie.workbench.common.stunner.core.client.service.ServiceCallback;
import org.kie.workbench.common.stunner.core.client.shape.factory.ShapeFactory;
import org.kie.workbench.common.stunner.core.client.shape.view.HasEventHandlers;
import org.kie.workbench.common.stunner.core.graph.Edge;
import org.kie.workbench.common.stunner.core.graph.Element;
import org.kie.workbench.common.stunner.core.graph.Node;
import org.kie.workbench.common.stunner.core.graph.content.view.View;
import org.kie.workbench.common.stunner.core.graph.processing.index.bounds.GraphBoundsIndexer;
import org.kie.workbench.common.stunner.core.util.DefinitionUtils;
import org.kie.workbench.common.stunner.core.util.UUID;
import org.uberfire.mvp.Command;
@Dependent
public class NewNodeCommand<I> extends AbstractElementBuilderCommand<I> {
private static Logger LOGGER = Logger.getLogger(NewNodeCommand.class.getName());
private final NodeDragProxy<AbstractCanvasHandler> nodeDragProxyFactory;
private final NodeBuilderControl<AbstractCanvasHandler> nodeBuilderControl;
private final Event<CanvasElementSelectedEvent> elementSelectedEvent;
private final DefinitionUtils definitionUtils;
private final CanvasLayoutUtils canvasLayoutUtils;
private String definitionId;
private int sourceMagnet;
private int targetMagnet;
private HasEventHandlers<?, ?> layerEventHandlers;
protected NewNodeCommand() {
this(null,
null,
null,
null,
null,
null,
null,
null,
null);
}
@Inject
public NewNodeCommand(final ClientFactoryService clientFactoryServices,
final ShapeManager shapeManager,
final DefinitionGlyphTooltip<?> glyphTooltip,
final GraphBoundsIndexer graphBoundsIndexer,
final NodeDragProxy<AbstractCanvasHandler> nodeDragProxyFactory,
final NodeBuilderControl<AbstractCanvasHandler> nodeBuilderControl,
final DefinitionUtils definitionUtils,
final CanvasLayoutUtils canvasLayoutUtils,
final Event<CanvasElementSelectedEvent> elementSelectedEvent) {
super(clientFactoryServices,
shapeManager,
glyphTooltip,
graphBoundsIndexer);
this.nodeDragProxyFactory = nodeDragProxyFactory;
this.nodeBuilderControl = nodeBuilderControl;
this.definitionUtils = definitionUtils;
this.canvasLayoutUtils = canvasLayoutUtils;
this.elementSelectedEvent = elementSelectedEvent;
this.layerEventHandlers = null;
}
// TODO: i18n.
@PostConstruct
public void init() {
getGlyphTooltip().setPrefix("Click to create a ");
}
public void setDefinitionIdentifier(final String definitionId) {
this.definitionId = definitionId;
}
private String getEdgeIdentifier(final Context<AbstractCanvasHandler> context) {
final String defSetId = context.getCanvasHandler().getDiagram().getMetadata().getDefinitionSetId();
return definitionUtils.getDefaultConnectorId(defSetId);
}
@Override
protected String getDefinitionIdentifier(final Context<AbstractCanvasHandler> context) {
return getEdgeIdentifier(context);
}
@Override
protected String getGlyphDefinitionId() {
return this.definitionId;
}
// TODO: I18n.
@Override
public String getTitle() {
return "Creates a new node";
}
@Override
public void click(final Context<AbstractCanvasHandler> context,
final Element element) {
super.click(context,
element);
log(Level.INFO,
"Click - Start adding a new node...");
addOnNextLayoutPosition(context,
element);
}
// TODO: Move all these stuff to canvas builder controls?
@SuppressWarnings("unchecked")
private void addOnNextLayoutPosition(final Context<AbstractCanvasHandler> context,
final Element element) {
fireLoadingStarted(context);
final AbstractCanvasHandler canvasHandler = context.getCanvasHandler();
getGraphBoundsIndexer().setRootUUID(canvasHandler.getDiagram().getMetadata().getCanvasRootUUID());
getGraphBoundsIndexer().build(canvasHandler.getDiagram().getGraph());
getClientFactoryServices().newElement(UUID.uuid(),
getDefinitionIdentifier(context),
new ServiceCallback<Element>() {
@Override
public void onSuccess(final Element newEdgeElement) {
onDefinitionInstanceBuilt(context,
element,
newEdgeElement,
new Command() {
@Override
public void execute() {
getBuilderControl().enable(canvasHandler);
getBuilderControl().setCommandManagerProvider(context::getCommandManager);
getGraphBoundsIndexer().build(canvasHandler.getDiagram().getGraph());
// TODO: Use right magnets.
NewNodeCommand.this.sourceMagnet = 3;
NewNodeCommand.this.targetMagnet = 7;
final double[] next = canvasLayoutUtils.getNext(canvasHandler,
(Node<View<?>, Edge>) element);
log(Level.INFO,
"New edge request complete - [UUID=" + newEdgeElement.getUUID()
+ ", x=" + next[0] + ", y=" + next[1] + "]");
NewNodeCommand.this.onComplete(context,
element,
newEdgeElement,
(int) next[0],
(int) next[1]);
}
});
}
@Override
public void onError(final ClientRuntimeError error) {
NewNodeCommand.this.onError(context,
error);
}
});
}
@Override
protected DragProxy getDragProxyFactory() {
return nodeDragProxyFactory;
}
@Override
protected BuilderControl getBuilderControl() {
return nodeBuilderControl;
}
@Override
protected DragProxyCallback getDragProxyCallback(final Context<AbstractCanvasHandler> context,
final Element element,
final Element item) {
return new NodeDragProxyCallback() {
@Override
public void onStart(final int x,
final int y) {
NewNodeCommand.this.onStart(context,
element,
item,
x,
y);
}
@Override
public void onMove(final int x,
final int y) {
NewNodeCommand.this.onMove(context,
element,
item,
x,
y);
}
@Override
public void onComplete(final int x,
final int y) {
}
@Override
public void onComplete(final int x,
final int y,
final int sourceMagnet,
final int targetMagnet) {
NewNodeCommand.this.sourceMagnet = sourceMagnet;
NewNodeCommand.this.targetMagnet = targetMagnet;
NewNodeCommand.this.onComplete(context,
element,
item,
x,
y);
}
};
}
@Override
protected void onStart(final Context<AbstractCanvasHandler> context,
final Element element,
final Element item,
final int x1,
final int y1) {
super.onStart(context,
element,
item,
x1,
y1);
// Disable layer events handlers in order to avoid layer events while using the drag def.
this.layerEventHandlers = getLayer(context);
disableHandlers();
}
@Override
protected void onItemBuilt(final Context<AbstractCanvasHandler> context,
final String uuid) {
super.onItemBuilt(context,
uuid);
fireElementSelectedEvent(elementSelectedEvent,
context.getCanvasHandler(),
uuid);
}
@Override
protected void onError(final Context<AbstractCanvasHandler> context,
final ClientRuntimeError error) {
super.onError(context,
error);
// Enable layer events handlers again.
enableHandlers();
}
protected Layer getLayer(final Context<AbstractCanvasHandler> context) {
return context.getCanvasHandler().getCanvas().getLayer();
}
@Override
@SuppressWarnings("unchecked")
protected void onDefinitionInstanceBuilt(final Context<AbstractCanvasHandler> context,
final Element source,
final Element newElement,
final Command callback) {
final Node<View<?>, Edge> sourceNode = (Node<View<?>, Edge>) source;
final Edge<View<?>, Node> edge = (Edge<View<?>, Node>) newElement;
// Create the new node.
getClientFactoryServices().newElement(UUID.uuid(),
definitionId,
new ServiceCallback<Element>() {
@Override
public void onSuccess(final Element item) {
final Node<View<?>, Edge> node = (Node<View<?>, Edge>) item.asNode();
// Perform the temporal def connections.
edge.setSourceNode(sourceNode);
edge.setTargetNode(node);
NewNodeCommand.super.onDefinitionInstanceBuilt(context,
source,
newElement,
callback);
}
@Override
public void onError(final ClientRuntimeError error) {
NewNodeCommand.this.onError(context,
error);
}
});
}
@Override
@SuppressWarnings("unchecked")
protected Object createtBuilderControlItem(final Context<AbstractCanvasHandler> context,
final Element source,
final Element newElement) {
final Node<View<?>, Edge> sourceNode = (Node<View<?>, Edge>) source;
final Edge<View<?>, Node> edge = (Edge<View<?>, Node>) newElement;
final ShapeFactory<?, AbstractCanvasHandler, ?> shapeFactory = getFactory(context.getCanvasHandler());
return new NodeDragProxy.Item<AbstractCanvasHandler>() {
@Override
public Node<View<?>, Edge> getNode() {
return edge.getTargetNode();
}
@Override
public ShapeFactory<?, AbstractCanvasHandler, ?> getNodeShapeFactory() {
return shapeFactory;
}
@Override
public Edge<View<?>, Node> getInEdge() {
return edge;
}
@Override
public Node<View<?>, Edge> getInEdgeSourceNode() {
return sourceNode;
}
@Override
public ShapeFactory<?, AbstractCanvasHandler, ?> getInEdgeShapeFactory() {
return shapeFactory;
}
};
}
@Override
@SuppressWarnings("unchecked")
protected boolean onDragProxyMove(final int x,
final int y,
final Element source,
final Element newElement,
final Node parent) {
final Edge<View<?>, Node> edge = (Edge<View<?>, Node>) newElement;
final Node<View<?>, Edge> node = (Node<View<?>, Edge>) edge.getTargetNode();
final NodeBuildRequest request = new NodeBuildRequestImpl(x,
y,
node,
edge);
final boolean accepts = nodeBuilderControl.allows(request);
if (accepts) {
if (null != parent) {
return true;
}
}
return false;
}
@Override
@SuppressWarnings("unchecked")
protected BuildRequest createBuildRequest(final int x,
final int y,
final Element source,
final Element newElement,
final Node targetNode) {
final Edge<View<?>, Node> edge = (Edge<View<?>, Node>) newElement;
final Node<View<?>, Edge> node = (Node<View<?>, Edge>) edge.getTargetNode();
return new NodeBuildRequestImpl(x,
y,
node,
edge,
this.sourceMagnet,
this.targetMagnet);
}
@Override
protected void clearDragProxy() {
super.clearDragProxy();
// Enable layers' events handlers again.
enableHandlers();
}
private void disableHandlers() {
if (null != this.layerEventHandlers) {
this.layerEventHandlers.disableHandlers();
}
}
private void enableHandlers() {
if (null != this.layerEventHandlers) {
this.layerEventHandlers.enableHandlers();
}
}
@Override
public void destroy() {
super.destroy();
this.layerEventHandlers = null;
}
protected String getDefinitionId(final Object def) {
return definitionUtils.getDefinitionManager().adapters().forDefinition().getId(def);
}
private void log(final Level level,
final String message) {
if (LogConfiguration.loggingIsEnabled()) {
LOGGER.log(level,
message);
}
}
}