/* * Copyright 2016 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.client.lienzo.shape.view; import java.util.HashMap; import java.util.Map; import com.ait.lienzo.client.core.event.AbstractNodeGestureEvent; import com.ait.lienzo.client.core.event.AbstractNodeTouchEvent; import com.ait.lienzo.client.core.event.NodeGestureChangeEvent; import com.ait.lienzo.client.core.event.NodeGestureChangeHandler; import com.ait.lienzo.client.core.event.NodeGestureEndEvent; import com.ait.lienzo.client.core.event.NodeGestureEndHandler; import com.ait.lienzo.client.core.event.NodeGestureStartEvent; import com.ait.lienzo.client.core.event.NodeGestureStartHandler; import com.ait.lienzo.client.core.event.TouchPoint; import com.ait.lienzo.client.core.shape.Node; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.Timer; import org.kie.workbench.common.stunner.core.client.shape.view.event.GestureEvent; import org.kie.workbench.common.stunner.core.client.shape.view.event.GestureEventImpl; import org.kie.workbench.common.stunner.core.client.shape.view.event.GestureHandler; import org.kie.workbench.common.stunner.core.client.shape.view.event.HandlerRegistrationImpl; import org.kie.workbench.common.stunner.core.client.shape.view.event.MouseClickEvent; import org.kie.workbench.common.stunner.core.client.shape.view.event.MouseDoubleClickEvent; import org.kie.workbench.common.stunner.core.client.shape.view.event.MouseEnterEvent; import org.kie.workbench.common.stunner.core.client.shape.view.event.MouseExitEvent; import org.kie.workbench.common.stunner.core.client.shape.view.event.TouchEventImpl; import org.kie.workbench.common.stunner.core.client.shape.view.event.TouchHandler; import org.kie.workbench.common.stunner.core.client.shape.view.event.ViewEvent; import org.kie.workbench.common.stunner.core.client.shape.view.event.ViewEventType; import org.kie.workbench.common.stunner.core.client.shape.view.event.ViewHandler; public class ViewEventHandlerManager { private static final int CLICK_HANDLER_TIMER_DURATION = 350; protected final HandlerRegistrationImpl registrationManager = new HandlerRegistrationImpl(); protected final Map<ViewEventType, HandlerRegistration[]> registrationMap = new HashMap<>(); private final Node<?> node; private final Node<?> path; private final ViewEventType[] supportedTypes; private boolean enabled; /** * This is a flag used to distinguish between click / double click events fired for same node. * When doing mouse click on the node, this implementation schedules a timer to trigger the click handler/s, if any. * If just another click is done, which produces the double click event to fire, the double click handler added * by this implementation set the <code>fireClickHandler</code> to false, to when the previously scheduled timer * tries to fire the click event, it'll be fired depending on this boolean's value. */ private boolean fireClickHandler; public ViewEventHandlerManager(final Node<?> node, final Node<?> path, final ViewEventType... supportedTypes) { this.node = node; this.path = path; this.supportedTypes = supportedTypes; this.fireClickHandler = true; enable(); } public void enable() { this.enabled = true; } public void disable() { this.enabled = false; } private boolean isEnabled() { return this.enabled; } public boolean supports(final ViewEventType type) { if (null != supportedTypes) { for (final ViewEventType type1 : supportedTypes) { if (type.equals(type1)) { return true; } } } return false; } @SuppressWarnings("unchecked") public void addHandler(final ViewEventType type, final ViewHandler<? extends ViewEvent> eventHandler) { if (supports(type)) { final HandlerRegistration[] registrations = doAddHandler(type, eventHandler); addHandlersRegistration(type, registrations); } } @SuppressWarnings("unchecked") public void addHandlersRegistration(final ViewEventType type, final HandlerRegistration... registrations) { if (null != registrations && registrations.length > 0) { registrationMap.put(type, registrations); for (final HandlerRegistration registration : registrations) { registrationManager.register(registration); } } } @SuppressWarnings("unchecked") protected HandlerRegistration[] doAddHandler(final ViewEventType type, final ViewHandler<? extends ViewEvent> eventHandler) { if (ViewEventType.MOUSE_CLICK.equals(type)) { return registerClickHandler((ViewHandler<ViewEvent>) eventHandler); } if (ViewEventType.MOUSE_DBL_CLICK.equals(type)) { return registerDoubleClickHandler((ViewHandler<ViewEvent>) eventHandler); } if (ViewEventType.MOUSE_ENTER.equals(type)) { return registerEnterHandler((ViewHandler<ViewEvent>) eventHandler); } if (ViewEventType.MOUSE_EXIT.equals(type)) { return registerExitHandler((ViewHandler<ViewEvent>) eventHandler); } if (ViewEventType.TOUCH.equals(type)) { return registerTouchHandler((TouchHandler) eventHandler); } if (ViewEventType.GESTURE.equals(type)) { return registerGestureHandler((GestureHandler) eventHandler); } return null; } @SuppressWarnings("unchecked") public void removeHandler(final ViewHandler<? extends ViewEvent> eventHandler) { final ViewEventType type = eventHandler.getType(); if (registrationMap.containsKey(type)) { final HandlerRegistration[] registrations = registrationMap.get(type); if (null != registrations && registrations.length > 0) { for (final HandlerRegistration registration : registrations) { registrationManager.deregister(registration); } } } } @SuppressWarnings("unchecked") public void destroy() { restoreClickHandler(); registrationManager.removeHandler(); registrationMap.clear(); } protected HandlerRegistration[] registerGestureHandler(final GestureHandler gestureHandler) { HandlerRegistration gestureStartReg = node.addNodeGestureStartHandler(new NodeGestureStartHandler() { @Override public void onNodeGestureStart(final NodeGestureStartEvent event) { if (isEnabled()) { final GestureEvent event1 = buildGestureEvent(event); if (null != event1) { gestureHandler.start(event1); } } } }); HandlerRegistration gestureChangeReg = node.addNodeGestureChangeHandler(new NodeGestureChangeHandler() { @Override public void onNodeGestureChange(final NodeGestureChangeEvent event) { if (isEnabled()) { final GestureEvent event1 = buildGestureEvent(event); if (null != event1) { gestureHandler.change(event1); } } } }); HandlerRegistration gestureEndReg = node.addNodeGestureEndHandler(new NodeGestureEndHandler() { @Override public void onNodeGestureEnd(final NodeGestureEndEvent event) { if (isEnabled()) { final GestureEvent event1 = buildGestureEvent(event); if (null != event1) { gestureHandler.end(event1); } } } }); return new HandlerRegistration[]{ gestureStartReg, gestureChangeReg, gestureEndReg }; } protected GestureEventImpl buildGestureEvent(final AbstractNodeGestureEvent event) { return new GestureEventImpl(event.getScale(), event.getRotation()); } protected HandlerRegistration[] registerEnterHandler(final ViewHandler<ViewEvent> eventHandler) { return new HandlerRegistration[]{ path.addNodeMouseEnterHandler(e -> { if (isEnabled()) { final MouseEnterEvent event = new MouseEnterEvent(e.getX(), e.getY(), e.getMouseEvent().getClientX(), e.getMouseEvent().getClientY()); event.setShiftKeyDown(e.isShiftKeyDown()); event.setAltKeyDown(e.isAltKeyDown()); event.setMetaKeyDown(e.isMetaKeyDown()); eventHandler.handle(event); } }) }; } protected HandlerRegistration[] registerExitHandler(final ViewHandler<ViewEvent> eventHandler) { return new HandlerRegistration[]{ path.addNodeMouseExitHandler(e -> { if (isEnabled()) { final MouseExitEvent event = new MouseExitEvent(e.getX(), e.getY(), e.getMouseEvent().getClientX(), e.getMouseEvent().getClientY()); event.setShiftKeyDown(e.isShiftKeyDown()); event.setAltKeyDown(e.isAltKeyDown()); event.setMetaKeyDown(e.isMetaKeyDown()); eventHandler.handle(event); } }) }; } protected HandlerRegistration[] registerClickHandler(final ViewHandler<ViewEvent> eventHandler) { return new HandlerRegistration[]{ node.addNodeMouseClickHandler(nodeMouseClickEvent -> { if (ViewEventHandlerManager.this.isEnabled()) { restoreClickHandler(); final int x = nodeMouseClickEvent.getX(); final int y = nodeMouseClickEvent.getY(); final int clientX = nodeMouseClickEvent.getMouseEvent().getClientX(); final int clientY = nodeMouseClickEvent.getMouseEvent().getClientY(); final boolean isShiftKeyDown = nodeMouseClickEvent.isShiftKeyDown(); final boolean isAltKeyDown = nodeMouseClickEvent.isAltKeyDown(); final boolean isMetaKeyDown = nodeMouseClickEvent.isMetaKeyDown(); final boolean isButtonLeft = nodeMouseClickEvent.isButtonLeft(); final boolean isButtonMiddle = nodeMouseClickEvent.isButtonMiddle(); final boolean isButtonRight = nodeMouseClickEvent.isButtonRight(); new Timer() { @Override public void run() { if (fireClickHandler) { ViewEventHandlerManager.this.onMouseClick(eventHandler, x, y, clientX, clientY, isShiftKeyDown, isAltKeyDown, isMetaKeyDown, isButtonLeft, isButtonMiddle, isButtonRight); } } }.schedule(CLICK_HANDLER_TIMER_DURATION); } }) }; } protected HandlerRegistration[] registerDoubleClickHandler(final ViewHandler<ViewEvent> eventHandler) { return new HandlerRegistration[]{ node.addNodeMouseDoubleClickHandler(nodeMouseDoubleClickEvent -> { if (isEnabled()) { skipClickHandler(); final MouseDoubleClickEvent event = new MouseDoubleClickEvent(nodeMouseDoubleClickEvent.getX(), nodeMouseDoubleClickEvent.getY(), nodeMouseDoubleClickEvent.getMouseEvent().getClientX(), nodeMouseDoubleClickEvent.getMouseEvent().getClientY()); event.setShiftKeyDown(nodeMouseDoubleClickEvent.isShiftKeyDown()); event.setAltKeyDown(nodeMouseDoubleClickEvent.isAltKeyDown()); event.setMetaKeyDown(nodeMouseDoubleClickEvent.isMetaKeyDown()); event.setButtonLeft(nodeMouseDoubleClickEvent.isButtonLeft()); event.setButtonMiddle(nodeMouseDoubleClickEvent.isButtonMiddle()); event.setButtonRight(nodeMouseDoubleClickEvent.isButtonRight()); eventHandler.handle(event); restoreClickHandler(); } }) }; } public void skipClickHandler() { this.fireClickHandler = false; } public void restoreClickHandler() { this.fireClickHandler = true; } private void onMouseClick(final ViewHandler<ViewEvent> eventHandler, final int x, final int y, final int clientX, final int clientY, final boolean isShiftKeyDown, final boolean isAltKeyDown, final boolean isMetaKeyDown, final boolean isButtonLeft, final boolean isButtonMiddle, final boolean isButtonRight) { final MouseClickEvent event = new MouseClickEvent(x, y, clientX, clientY); event.setShiftKeyDown(isShiftKeyDown); event.setAltKeyDown(isAltKeyDown); event.setMetaKeyDown(isMetaKeyDown); event.setButtonLeft(isButtonLeft); event.setButtonMiddle(isButtonMiddle); event.setButtonRight(isButtonRight); eventHandler.handle(event); } protected HandlerRegistration[] registerTouchHandler(final TouchHandler touchHandler) { HandlerRegistration touchStartReg = node.addNodeTouchStartHandler(event -> { if (isEnabled()) { final TouchEventImpl event1 = buildTouchEvent(event); if (null != event1) { touchHandler.start(event1); } } }); HandlerRegistration touchMoveReg = node.addNodeTouchMoveHandler(event -> { if (isEnabled()) { final TouchEventImpl event1 = buildTouchEvent(event); if (null != event1) { touchHandler.move(event1); } } }); HandlerRegistration touchEndReg = node.addNodeTouchEndHandler(event -> { if (isEnabled()) { final TouchEventImpl event1 = buildTouchEvent(event); if (null != event1) { touchHandler.end(event1); } } }); HandlerRegistration touchCancelReg = node.addNodeTouchCancelHandler(event -> { if (isEnabled()) { final TouchEventImpl event1 = buildTouchEvent(event); if (null != event1) { touchHandler.cancel(event1); } } }); return new HandlerRegistration[]{ touchStartReg, touchMoveReg, touchEndReg, touchCancelReg }; } private TouchEventImpl buildTouchEvent(final AbstractNodeTouchEvent event) { final TouchPoint touchPoint = null != event.getTouches() && !event.getTouches().isEmpty() ? (TouchPoint) event.getTouches().get(0) : null; if (null != touchPoint) { final int tx = touchPoint.getX(); final int ty = touchPoint.getY(); return new TouchEventImpl(event.getX(), event.getY(), tx, ty); } return null; } }