/*
* Copyright 2014 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.drools.workbench.screens.guided.dtree.client.widget.shapes;
import java.util.ArrayList;
import java.util.List;
import com.ait.lienzo.client.core.event.NodeMouseClickEvent;
import com.ait.lienzo.client.core.event.NodeMouseClickHandler;
import com.ait.lienzo.client.core.image.PictureLoadedHandler;
import com.ait.lienzo.client.core.shape.Circle;
import com.ait.lienzo.client.core.shape.Group;
import com.ait.lienzo.client.core.shape.Picture;
import com.ait.lienzo.client.core.shape.Rectangle;
import com.ait.lienzo.client.core.shape.Text;
import com.ait.lienzo.client.core.types.Point2D;
import com.ait.lienzo.shared.core.types.Color;
import com.ait.lienzo.shared.core.types.TextAlign;
import com.ait.lienzo.shared.core.types.TextBaseLine;
import com.google.gwt.resources.client.ImageResource;
import org.drools.workbench.models.guided.dtree.shared.model.nodes.Node;
import org.drools.workbench.screens.guided.dtree.client.editor.GuidedDecisionTreeEditorPresenter;
import org.drools.workbench.screens.guided.dtree.client.resources.GuidedDecisionTreeResources;
import org.uberfire.ext.wires.core.trees.client.shapes.WiresBaseTreeNode;
import org.uberfire.mvp.Command;
public abstract class BaseGuidedDecisionTreeShape<T extends Node> extends WiresBaseTreeNode {
private static final int BOUNDARY_SIZE = 10;
private final Circle circle;
private final Circle bounding;
protected final Text plus = new Text( "+",
"normal",
50 );
protected NodeLabel nodeLabel = new NodeLabel();
private Group ctrlGroupDeleteIcon;
private Group ctrlGroupEditIcon;
private Group ctrlGroupCollapseIcon;
private Group ctrlGroupExpandIcon;
protected final T node;
protected boolean isReadOnly;
private GuidedDecisionTreeEditorPresenter presenter;
public BaseGuidedDecisionTreeShape( final Circle shape,
final T node,
final boolean isReadOnly ) {
this.circle = shape;
this.node = node;
this.isReadOnly = isReadOnly;
bounding = new Circle( circle.getRadius() + ( BOUNDARY_SIZE / 2 ) );
bounding.setStrokeWidth( BOUNDARY_SIZE );
bounding.setAlpha( 0.1 );
plus.setTextAlign( TextAlign.CENTER );
plus.setTextBaseLine( TextBaseLine.MIDDLE );
plus.setStrokeWidth( 2 );
add( circle );
add( nodeLabel );
nodeLabel.addNodeMouseClickHandler( new NodeMouseClickHandler() {
@Override
public void onNodeMouseClick( final NodeMouseClickEvent nodeMouseClickEvent ) {
selectionManager.selectShape( BaseGuidedDecisionTreeShape.this );
}
} );
if ( !isReadOnly ) {
setupControls();
}
}
public void setPresenter( final GuidedDecisionTreeEditorPresenter presenter ) {
this.presenter = presenter;
}
protected void setupControls() {
ctrlGroupDeleteIcon = setupControl( GuidedDecisionTreeResources.INSTANCE.images().ctrlDelete(),
new Command() {
@Override
public void execute() {
shapesManager.deleteShape( BaseGuidedDecisionTreeShape.this );
}
} );
ctrlGroupEditIcon = setupControl( GuidedDecisionTreeResources.INSTANCE.images().ctrlEdit(),
new Command() {
@Override
public void execute() {
presenter.editModelNode( BaseGuidedDecisionTreeShape.this.getModelNode(),
new Command() {
@Override
public void execute() {
updateLabels( BaseGuidedDecisionTreeShape.this );
BaseGuidedDecisionTreeShape.this.getLayer().batch();
}
private void updateLabels( final BaseGuidedDecisionTreeShape parent ) {
parent.setNodeLabel( parent.getNodeLabel() );
for ( WiresBaseTreeNode child : parent.getChildren() ) {
if ( child instanceof BaseGuidedDecisionTreeShape ) {
final BaseGuidedDecisionTreeShape cs = (BaseGuidedDecisionTreeShape) child;
cs.setNodeLabel( cs.getNodeLabel() );
updateLabels( cs );
}
}
}
} );
}
} );
ctrlGroupCollapseIcon = setupControl( GuidedDecisionTreeResources.INSTANCE.images().ctrlCollapse(),
new Command() {
@Override
public void execute() {
BaseGuidedDecisionTreeShape.this.collapse( new Command() {
@Override
public void execute() {
//Nothing to do when the animation completes
}
} );
final List<Group> controls = new ArrayList<Group>() {{
add( ctrlGroupDeleteIcon );
add( ctrlGroupExpandIcon );
}};
BaseGuidedDecisionTreeShape.this.setControls( controls );
}
} );
ctrlGroupExpandIcon = setupControl( GuidedDecisionTreeResources.INSTANCE.images().ctrlExpand(),
new Command() {
@Override
public void execute() {
BaseGuidedDecisionTreeShape.this.expand( new Command() {
@Override
public void execute() {
//Nothing to do when the animation completes
}
} );
final List<Group> controls = new ArrayList<Group>() {{
add( ctrlGroupDeleteIcon );
add( ctrlGroupEditIcon );
add( ctrlGroupCollapseIcon );
}};
BaseGuidedDecisionTreeShape.this.setControls( controls );
}
} );
controls.add( ctrlGroupDeleteIcon );
controls.add( ctrlGroupEditIcon );
}
protected Group setupControl( final ImageResource resource,
final Command command ) {
final Group controlGroup = new Group();
new Picture( resource, picture1 -> {
//This is a hack for Lienzo 1.2 (and possibly 2.x?). There is a bug in Picture when
//we want to add it to Lienzo's SelectionLayer. We work around it here by adding
//the Picture to a Group containing a "near invisible" Rectangle that we use to
//capture the NodeMouseClickEvents.
final double offsetX = -picture1.getImageData().getWidth() / 2;
final double offsetY = -picture1.getImageData().getHeight() / 2;
final Rectangle selector = new Rectangle( picture1.getImageData().getWidth(),
picture1.getImageData().getHeight() );
selector.setFillColor( Color.rgbToBrowserHexColor( 255,
255,
255 ) );
selector.setAlpha( 0.01 );
selector.setLocation( new Point2D( offsetX,
offsetY ) );
picture1.setLocation( new Point2D( offsetX,
offsetY ) );
controlGroup.add( picture1 );
controlGroup.add( selector );
},
false );
controlGroup.addNodeMouseClickHandler( new NodeMouseClickHandler() {
@Override
public void onNodeMouseClick( final NodeMouseClickEvent nodeMouseClickEvent ) {
command.execute();
}
} );
return controlGroup;
}
/**
* Set the label shown for this Node.
* @param label
*/
protected void setNodeLabel( final String label ) {
nodeLabel.setLabel( label );
}
/**
* Get a String for the Node
* @return
*/
protected abstract String getNodeLabel();
@Override
public void setSelected( final boolean isSelected ) {
if ( isSelected ) {
add( bounding );
if ( !isReadOnly ) {
showControls();
}
} else {
remove( bounding );
if ( !isReadOnly ) {
hideControls();
}
}
}
@Override
public void addChildNode( final WiresBaseTreeNode child ) {
final boolean hasChildren = hasChildren();
super.addChildNode( child );
if ( !hasChildren && hasChildren() ) {
addControl( ctrlGroupCollapseIcon );
}
}
@Override
protected Point2D getControlTarget( final Group ctrl ) {
final Point2D target = super.getControlTarget( ctrl );
target.setX( circle.getRadius() + 25 );
return target;
}
@Override
public void onCollapseStart() {
add( plus );
nodeLabel.moveToTop();
plus.setAlpha( 0.0 );
}
@Override
public void onCollapseProgress( final double pct ) {
plus.setAlpha( pct );
}
@Override
public void onExpandProgress( double pct ) {
plus.setAlpha( 1.0 - pct );
}
@Override
public void onExpandEnd() {
remove( plus );
}
@Override
public double getWidth() {
return circle.getRadius() * 2;
}
@Override
public double getHeight() {
return circle.getRadius() * 2;
}
public T getModelNode() {
return node;
}
}