/*
* 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.select;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.enterprise.event.Event;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import com.google.gwt.logging.client.LogConfiguration;
import org.kie.workbench.common.stunner.core.client.canvas.AbstractCanvasHandler;
import org.kie.workbench.common.stunner.core.client.canvas.Canvas;
import org.kie.workbench.common.stunner.core.client.canvas.Layer;
import org.kie.workbench.common.stunner.core.client.canvas.controls.AbstractCanvasHandlerRegistrationControl;
import org.kie.workbench.common.stunner.core.client.canvas.event.registration.CanvasShapeRemovedEvent;
import org.kie.workbench.common.stunner.core.client.canvas.event.selection.CanvasClearSelectionEvent;
import org.kie.workbench.common.stunner.core.client.canvas.event.selection.CanvasElementSelectedEvent;
import org.kie.workbench.common.stunner.core.client.shape.Shape;
import org.kie.workbench.common.stunner.core.client.shape.ShapeState;
import org.kie.workbench.common.stunner.core.client.shape.view.event.MouseClickEvent;
import org.kie.workbench.common.stunner.core.client.shape.view.event.MouseClickHandler;
import org.kie.workbench.common.stunner.core.client.shape.view.event.ViewEventType;
import org.kie.workbench.common.stunner.core.client.shape.view.event.ViewHandler;
import org.kie.workbench.common.stunner.core.graph.Element;
import static org.uberfire.commons.validation.PortablePreconditions.checkNotNull;
public abstract class AbstractSelectionControl<H extends AbstractCanvasHandler> extends AbstractCanvasHandlerRegistrationControl<H>
implements SelectionControl<H, Element> {
private static Logger LOGGER = Logger.getLogger(AbstractSelectionControl.class.getName());
Event<CanvasElementSelectedEvent> elementSelectedEventEvent;
Event<CanvasClearSelectionEvent> clearSelectionEventEvent;
private final List<String> selectedElements = new ArrayList<String>();
private ViewHandler<?> layerClickHandler;
@Inject
public AbstractSelectionControl(final Event<CanvasElementSelectedEvent> elementSelectedEventEvent,
final Event<CanvasClearSelectionEvent> clearSelectionEventEvent) {
this.elementSelectedEventEvent = elementSelectedEventEvent;
this.clearSelectionEventEvent = clearSelectionEventEvent;
}
/*
**************************************************************
* CANVAS CONTROL METHODS
***************************************************************
*/
@Override
public void enable(final H canvasHandler) {
super.enable(canvasHandler);
final Layer layer = canvasHandler.getCanvas().getLayer();
// Click event.
final MouseClickHandler clickHandler = new MouseClickHandler() {
@Override
public void handle(final MouseClickEvent event) {
if (event.isButtonLeft()) {
handleLayerClick(!event.isShiftKeyDown());
}
}
};
layer.addHandler(ViewEventType.MOUSE_CLICK,
clickHandler);
this.layerClickHandler = clickHandler;
}
protected abstract void register(final Element element,
final Shape<?> shape);
@Override
public void register(final Element element) {
if (checkNotRegistered(element)) {
final Shape<?> shape = getCanvas().getShape(element.getUUID());
if (null != shape) {
register(element,
shape);
}
}
}
protected void handleElementSelection(final Element element,
final boolean selected,
final boolean clearSelection) {
if (clearSelection) {
clearSelection();
}
if (selected) {
log(Level.FINE,
"Deselect [element=" + element.getUUID() + "]");
deselect(element);
} else {
log(Level.FINE,
"Select [element=" + element.getUUID() + "]");
select(element);
}
}
/**
* When clicking on the layer or on the canvas root element, it's not
* being added into the selected list but it fires the selection event
* so other components can process or present their stuff at this point.
*/
protected void handleLayerClick(final boolean clearSelection) {
if (clearSelection) {
clearSelection();
}
final String canvasRootUUID = getRootUUID();
if (null != canvasRootUUID) {
elementSelectedEventEvent.fire(new CanvasElementSelectedEvent(canvasHandler,
canvasRootUUID));
} else {
clearSelectionEventEvent.fire(new CanvasClearSelectionEvent(canvasHandler));
}
}
@Override
protected void doDisable() {
super.doDisable();
if (null != layerClickHandler
&& null != getCanvas()
&& null != getCanvas().getLayer()) {
getCanvas().getLayer().removeHandler(layerClickHandler);
this.layerClickHandler = null;
}
}
@Override
public void deregisterAll() {
super.deregisterAll();
selectedElements.clear();
}
@Override
public void deregister(final String uuid) {
super.deregister(uuid);
selectedElements.remove(uuid);
}
@SuppressWarnings("unchecked")
protected void updateViewShapesState() {
if (null != getCanvas()) {
final List<Shape> shapes = getCanvas().getShapes();
for (final Shape shape : shapes) {
final boolean isSelected = !selectedElements.isEmpty() && selectedElements.contains(shape.getUUID());
if (isSelected) {
selectShape(shape);
} else {
deselectShape(shape);
}
}
// Batch a show operation.
getCanvas().draw();
}
}
protected void selectShape(final Shape shape) {
shape.applyState(ShapeState.SELECTED);
getCanvas().draw();
}
protected void deselectShape(final Shape shape) {
shape.applyState(ShapeState.NONE);
getCanvas().draw();
}
/*
**************************************************************
* SELECTION CONTROL METHODS
***************************************************************
*/
public SelectionControl<H, Element> select(final String uuid,
final boolean fireEvent) {
selectedElements.add(uuid);
updateViewShapesState();
if (fireEvent) {
elementSelectedEventEvent.fire(new CanvasElementSelectedEvent(canvasHandler,
uuid));
}
return this;
}
@Override
public SelectionControl<H, Element> select(final Element element) {
return select(element,
true);
}
public SelectionControl<H, Element> select(final Element element,
final boolean fireEvent) {
this.select(element.getUUID(),
fireEvent);
return this;
}
public SelectionControl<H, Element> deselect(final String uuid,
final boolean fireEvent) {
// GWT.log("***** DESELECTING " + uuid );
selectedElements.remove(uuid);
updateViewShapesState();
if (fireEvent) {
fireCanvasClear();
}
return this;
}
@Override
public SelectionControl<H, Element> deselect(final Element element) {
return deselect(element,
true);
}
public SelectionControl<H, Element> deselect(final Element element,
final boolean fireEvent) {
return this.deselect(element.getUUID(),
fireEvent);
}
protected boolean isSelected(final String uuid) {
return uuid != null && selectedElements.contains(uuid);
}
@Override
public boolean isSelected(final Element element) {
return null != element && isSelected(element.getUUID());
}
@Override
public Collection<String> getSelectedItems() {
return Collections.unmodifiableCollection(selectedElements);
}
@Override
public SelectionControl<H, Element> clearSelection() {
return clearSelection(true);
}
public SelectionControl<H, Element> clearSelection(final boolean fireEvent) {
// De-select all currently selected shapes.
for (final String uuid : selectedElements) {
final Shape<?> shape = canvasHandler.getCanvas().getShape(uuid);
if (null != shape) {
deselectShape(shape);
}
}
selectedElements.clear();
if (null != getCanvas()) {
// Force batch re-show.
getCanvas().draw();
}
if (fireEvent) {
fireCanvasClear();
}
return this;
}
void onShapeRemovedEvent(final @Observes CanvasShapeRemovedEvent shapeRemovedEvent) {
checkNotNull("shapeRemovedEvent",
shapeRemovedEvent);
if (null != getCanvas() && getCanvas().equals(shapeRemovedEvent.getCanvas())) {
final Shape<?> shape = shapeRemovedEvent.getShape();
if (selectedElements.contains(shape.getUUID())) {
this.deselect(shape.getUUID(),
false);
}
}
}
void onCanvasElementSelectedEvent(final @Observes CanvasElementSelectedEvent event) {
checkNotNull("event",
event);
final String uuid = event.getElementUUID();
if (null != canvasHandler && canvasHandler.equals(event.getCanvasHandler())) {
doSelect(uuid);
}
}
private void doSelect(final String uuid) {
if (!isSelected(uuid) && !uuid.equals(getRootUUID())) {
this.clearSelection(false);
this.select(uuid,
false);
}
}
void CanvasClearSelectionEvent(final @Observes CanvasClearSelectionEvent event) {
checkNotNull("event",
event);
if (null != canvasHandler && canvasHandler.equals(event.getCanvasHandler())) {
this.clearSelection(false);
}
}
protected void fireCanvasClear() {
clearSelectionEventEvent.fire(new CanvasClearSelectionEvent(canvasHandler));
}
protected Canvas getCanvas() {
return null != canvasHandler ? canvasHandler.getCanvas() : null;
}
private String getRootUUID() {
return canvasHandler.getDiagram().getMetadata().getCanvasRootUUID();
}
private void log(final Level level,
final String message) {
if (LogConfiguration.loggingIsEnabled()) {
LOGGER.log(level,
message);
}
}
}