/*******************************************************************************
* Copyright (c) 2015 - 2017
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*******************************************************************************/
package go.graphics.event.interpreter;
import java.util.LinkedList;
import go.graphics.UIPoint;
import go.graphics.event.GOEvent;
import go.graphics.event.GOEventHandlerProvider;
import go.graphics.event.GOKeyEvent;
import go.graphics.event.SingleHandlerGoEvent;
import go.graphics.event.command.GOCommandEvent;
/**
* This class interprets events. It provides helper functions for e.g. Swing converter to send the events.
*
* @author michael
*/
public class AbstractEventConverter {
private static final int PAN_PER_KEY_PRESS = 20;
private final GOEventHandlerProvider provider;
private final LinkedList<EventReplacementRule> replace = new LinkedList<>();
private GOKeyEvent ongoingKeyEvent = null;
private ConvertedDrawEvent ongoingDrawEvent = null;
private ConvertedPanEvent ongoingPanEvent = null;
private ConvertedHoverEvent ongoingHoverEvent;
private ConvertedZoomEvent ongoingZoomEvent;
protected AbstractEventConverter(GOEventHandlerProvider provider) {
this.provider = provider;
}
/**
* trys to cancel the first found ongoing event.
*/
protected void tryCancelCurrentEvent() {
if (ongoingDrawEvent != null) {
ongoingDrawEvent.aborted();
ongoingDrawEvent = null;
} else if (ongoingPanEvent != null) {
ongoingPanEvent.aborted();
ongoingPanEvent = null;
}
}
/**
* Starts a draw event, if none is started yet.
*
* @param start
*/
protected void startDraw(UIPoint start) {
if (ongoingDrawEvent == null) {
ongoingDrawEvent = new ConvertedDrawEvent(start);
handleEvent(ongoingDrawEvent);
ongoingDrawEvent.initialized();
}
}
protected void updateDrawPosition(UIPoint current) {
if (ongoingDrawEvent != null) {
ongoingDrawEvent.setMousePosition(current);
}
}
protected void endDraw(UIPoint position) {
if (ongoingDrawEvent != null) {
if (tryReplaceEvent(position, ReplacableEvent.DRAW,
ongoingDrawEvent.getTime(),
ongoingDrawEvent.getMouseMoved())) {
abortDraw();
} else {
ongoingDrawEvent.released();
ongoingDrawEvent = null;
}
}
}
protected void abortDraw() {
if (ongoingDrawEvent != null) {
ongoingDrawEvent.aborted();
ongoingDrawEvent = null;
}
}
protected void startPan(UIPoint start) {
if (ongoingPanEvent == null) {
ongoingPanEvent = new ConvertedPanEvent(start);
handleEvent(ongoingPanEvent);
ongoingPanEvent.initialized();
}
}
protected void updatePanPosition(UIPoint current) {
if (ongoingPanEvent != null) {
ongoingPanEvent.setMousePosition(current);
}
}
protected void endPan(UIPoint position) {
if (ongoingPanEvent != null) {
if (tryReplaceEvent(position, ReplacableEvent.PAN,
ongoingPanEvent.getTime(), ongoingPanEvent.getMouseMoved())) {
abortPan();
} else {
ongoingPanEvent.released();
ongoingPanEvent = null;
}
}
}
protected void startZoom() {
if (ongoingZoomEvent == null) {
ongoingZoomEvent = new ConvertedZoomEvent();
handleEvent(ongoingZoomEvent);
ongoingZoomEvent.initialized();
}
}
protected void updateZoomFactor(float factor) {
if (ongoingZoomEvent != null) {
ongoingZoomEvent.setZoomFactor(factor, null);
}
}
protected void endZoomEvent(float factor, UIPoint pointingPosition) {
if (ongoingZoomEvent != null) {
ongoingZoomEvent.setZoomFactor(factor, pointingPosition);
ongoingZoomEvent.released();
ongoingZoomEvent = null;
}
}
protected void abortPan() {
if (ongoingPanEvent != null) {
ongoingPanEvent.aborted();
ongoingPanEvent = null;
}
}
protected void startHover(UIPoint start) {
if (ongoingHoverEvent == null) {
ongoingHoverEvent = new ConvertedHoverEvent(start);
handleEvent(ongoingHoverEvent);
ongoingHoverEvent.initialized();
}
}
protected void updateHoverPosition(UIPoint current) {
if (ongoingHoverEvent != null) {
ongoingHoverEvent.setMousePosition(current);
}
}
protected void endHover(UIPoint position) {
if (ongoingHoverEvent != null) {
if (tryReplaceEvent(position, ReplacableEvent.HOVER,
ongoingHoverEvent.getTime(),
ongoingHoverEvent.getMouseMoved())) {
abortHover();
} else {
ongoingHoverEvent.released();
ongoingHoverEvent = null;
}
}
}
protected void abortHover() {
if (ongoingHoverEvent != null) {
ongoingHoverEvent.aborted();
ongoingHoverEvent = null;
}
}
protected boolean fireCommandEvent(UIPoint point, boolean isSelect) {
ConvertedCommandEvent commandEvent = new ConvertedCommandEvent(point, isSelect);
handleEvent(commandEvent);
commandEvent.initialized();
return commandEvent.getHandler() != null;
}
protected synchronized void startKeyEvent(String string) {
if (ongoingKeyEvent == null) {
ongoingKeyEvent = new GOKeyEvent(string);
replaceKeyEvent(ongoingKeyEvent);
handleEvent(ongoingKeyEvent);
ongoingKeyEvent.started();
}
}
protected synchronized void endKeyEvent(String string) {
if (ongoingKeyEvent != null) {
ongoingKeyEvent.released();
ongoingKeyEvent = null;
}
}
protected synchronized boolean replaceKeyEvent(GOKeyEvent event) {
if ("DOWN".equalsIgnoreCase(event.getKeyCode())) {
doPan(event, 0, PAN_PER_KEY_PRESS);
return true;
} else if ("UP".equalsIgnoreCase(event.getKeyCode())) {
doPan(event, 0, -PAN_PER_KEY_PRESS);
return true;
} else if ("LEFT".equalsIgnoreCase(event.getKeyCode())) {
doPan(event, PAN_PER_KEY_PRESS, 0);
return true;
} else if ("RIGHT".equalsIgnoreCase(event.getKeyCode())) {
doPan(event, -PAN_PER_KEY_PRESS, 0);
return true;
} else {
return false;
}
}
private void doPan(GOKeyEvent event, int x, int y) {
PseudoPanEvent pan = new PseudoPanEvent(event, x, y);
this.handleEvent(pan);
}
/**
* Lets the provider handle a event.
*/
protected void handleEvent(GOEvent e) {
provider.handleEvent(e);
}
private class ConvertedCommandEvent extends SingleHandlerGoEvent implements
GOCommandEvent {
private final UIPoint position;
private final boolean selecting;
public ConvertedCommandEvent(UIPoint position, boolean selecting) {
this.position = position;
this.selecting = selecting;
}
public void initialized() {
setPhase(PHASE_STARTED);
setPhase(PHASE_MODAL);
setPhase(PHASE_FINISHED);
}
@Override
public UIPoint getCommandPosition() {
return position;
}
@Override
public boolean isSelecting() {
return selecting;
}
}
protected void addReplaceRule(EventReplacementRule r) {
replace.add(r);
}
private boolean tryReplaceEvent(UIPoint p, ReplacableEvent e, double time,
double distance) {
for (EventReplacementRule r : replace) {
if (r.matches(e, time, distance)) {
boolean success = fireCommandEvent(p,
r.replaced == Replacement.COMMAND_SELECT);
if (success) {
return true;
}
}
}
return false;
}
protected boolean drawStarted() {
return ongoingDrawEvent != null;
}
protected boolean panStarted() {
return ongoingPanEvent != null;
}
/**
* a rule on when to replace short draw, hover or pan events with other stuff.
*
* @author michael
*/
public class EventReplacementRule {
private final ReplacableEvent search;
private final Replacement replaced;
private final double maxTime;
private final double maxDistance;
/**
* @param search
* @param replaced
* @param maxTime
* Maximum time the event took. -1: ignore
* @param maxDistance
* Maximum distance the cursor moved. -1: ignore
*/
public EventReplacementRule(ReplacableEvent search,
Replacement replaced, double maxTime, double maxDistance) {
this.search = search;
this.replaced = replaced;
this.maxTime = maxTime;
this.maxDistance = maxDistance;
}
private boolean matches(ReplacableEvent e, double time, double distance) {
return e == search && (maxTime < 0 || time < maxTime)
&& (maxDistance < 0 || distance < maxDistance);
}
}
public enum ReplacableEvent {
DRAW,
PAN,
HOVER;
}
public enum Replacement {
COMMAND_SELECT,
COMMAND_ACTION;
}
}