com.puzzlebazar.client.ui.SquareGridManipulatorImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.puzzlebazar.client.ui.SquareGridManipulatorImpl.java

Source

/**
 * Copyright 2010 Philippe Beaudoin
 *
 * 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 com.puzzlebazar.client.ui;

import com.google.gwt.event.dom.client.HasMouseDownHandlers;
import com.google.gwt.event.dom.client.HasMouseMoveHandlers;
import com.google.gwt.event.dom.client.HasMouseOutHandlers;
import com.google.gwt.event.dom.client.HasMouseUpHandlers;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseMoveHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;
import com.puzzlebazar.client.util.EdgeHitInfo;
import com.puzzlebazar.client.util.SquareGridConverter;
import com.puzzlebazar.client.util.SquareGridConverterImpl;
import com.puzzlebazar.client.util.VertexHitInfo;
import com.puzzlebazar.shared.util.SquareGridValidator;
import com.puzzlebazar.shared.util.SquareGridValidatorImpl;
import com.puzzlebazar.shared.util.Vec2i;

/**
 * @author Philippe Beaudoin
 */
public class SquareGridManipulatorImpl
        implements MouseDownHandler, MouseUpHandler, MouseMoveHandler, MouseOutHandler, SquareGridManipulator {

    /**
     * The manipulator factory implementation.
     */
    public static class FactoryImpl implements SquareGridManipulator.Factory {

        @Inject
        FactoryImpl() {
        }

        @Override
        public SquareGridManipulator create(SquareGridLayoutPanel gridPanel, Widget uiWidget) {
            return new SquareGridManipulatorImpl(new SquareGridValidatorImpl(gridPanel),
                    new SquareGridConverterImpl(gridPanel, uiWidget), gridPanel, uiWidget);
        }
    }

    private final SquareGridValidator squareGridValidator;
    private final SquareGridConverter squareGridConverter;
    private final Widget uiWidget;

    private HandlerManager handlerManager;

    /**
     * Indicates which object the mouse is currently over.
     */
    private static final int OVER_NOTHING = -1;
    private static final int OVER_CELL = 0;
    private static final int OVER_EDGE = 1;
    private static final int OVER_VERTEX = 2;
    private int current = OVER_NOTHING;
    private boolean currentVertical;
    private Vec2i currentLoc;

    private HandlerRegistration mouseDownHandlerRegistration;
    private HandlerRegistration mouseUpHandlerRegistration;
    private HandlerRegistration mouseMoveHandlerRegistration;
    private HandlerRegistration mouseOutHandlerRegistration;

    private int vertexClickDistance = -1;
    private int vertexMoveDistance = -1;
    private int edgeClickDistance = -1;
    private int edgeMoveDistance = -1;

    /**
     * Create a square grid manipulator. This constructor is called
     * from the {@link SquareGridManipulatorFactoryImpl}.
     */
    private SquareGridManipulatorImpl(SquareGridValidator squareGridValidator,
            SquareGridConverter squareGridConverter, SquareGridLayoutPanel gridPanel, Widget uiWidget) {
        this.squareGridValidator = squareGridValidator;
        this.squareGridConverter = squareGridConverter;
        this.uiWidget = uiWidget;

        mouseDownHandlerRegistration = ((HasMouseDownHandlers) uiWidget).addMouseDownHandler(this);
        mouseUpHandlerRegistration = ((HasMouseUpHandlers) uiWidget).addMouseUpHandler(this);
        mouseMoveHandlerRegistration = ((HasMouseMoveHandlers) uiWidget).addMouseMoveHandler(this);
        mouseOutHandlerRegistration = ((HasMouseOutHandlers) uiWidget).addMouseOutHandler(this);
    }

    @Override
    public void unbind() {

        if (mouseDownHandlerRegistration != null) {
            mouseDownHandlerRegistration.removeHandler();
        }
        if (mouseUpHandlerRegistration != null) {
            mouseUpHandlerRegistration.removeHandler();
        }
        if (mouseMoveHandlerRegistration != null) {
            mouseMoveHandlerRegistration.removeHandler();
        }
        if (mouseOutHandlerRegistration != null) {
            mouseOutHandlerRegistration.removeHandler();
        }

        mouseDownHandlerRegistration = null;
        mouseUpHandlerRegistration = null;
        mouseMoveHandlerRegistration = null;
        mouseOutHandlerRegistration = null;
    }

    @Override
    public void onMouseDown(MouseDownEvent event) {
        event.preventDefault(); // Prevents undesired element selection
        DOM.setCapture(uiWidget.getElement());

        int x = event.getX();
        int y = event.getY();

        VertexHitInfo vertexHitInfo = vertexHit(x, y, vertexClickDistance);
        if (vertexHitInfo != null) {
            fireEvent(new VertexMouseDownEvent(vertexHitInfo.getVertex()));
            return;
        }

        EdgeHitInfo edgeHitInfo = edgeHit(x, y, edgeClickDistance);
        if (edgeHitInfo != null) {
            fireEvent(new EdgeMouseDownEvent(edgeHitInfo.isVertical(), edgeHitInfo.getEdge()));
            return;
        }

        Vec2i cell = cellHit(x, y);
        if (cell != null) {
            fireEvent(new CellMouseDownEvent(cell));
        }
    }

    @Override
    public void onMouseUp(MouseUpEvent event) {
        event.preventDefault(); // Prevents undesired element selection
        DOM.releaseCapture(uiWidget.getElement());

        int x = event.getX();
        int y = event.getY();

        VertexHitInfo vertexHitInfo = vertexHit(x, y, vertexClickDistance);
        if (vertexHitInfo != null) {
            fireEvent(new VertexMouseUpEvent(vertexHitInfo.getVertex()));
            return;
        }

        EdgeHitInfo edgeHitInfo = edgeHit(x, y, edgeClickDistance);
        if (edgeHitInfo != null) {
            fireEvent(new EdgeMouseUpEvent(edgeHitInfo.isVertical(), edgeHitInfo.getEdge()));
            return;
        }

        Vec2i cell = cellHit(x, y);
        if (cell != null) {
            fireEvent(new CellMouseUpEvent(cell));
        }
    }

    @Override
    public void onMouseMove(MouseMoveEvent event) {
        event.preventDefault(); // Prevents undesired element selection
        mouseMovedTo(event.getX(), event.getY());
    }

    @Override
    public void onMouseOut(MouseOutEvent event) {
        event.preventDefault(); // Prevents undesired element selection
        mouseMovedTo(event.getX(), event.getY());
    }

    /**
     * Call whenever the mouse move, wether because of a
     * {@link MouseMoveEvent} or a {@link MouseOutEvent}.
     *
     * @param x The x pixel coordinate within {@link #uiWidget}.
     * @param y The y pixel coordinate within {@link #uiWidget}.
     */
    private void mouseMovedTo(int x, int y) {

        VertexHitInfo vertexHitInfo = vertexHit(x, y, vertexMoveDistance);
        if (vertexHitInfo != null) {
            if (current != OVER_VERTEX || !currentLoc.equals(vertexHitInfo.getVertex())) {
                fireOutEvent();
                current = OVER_VERTEX;
                currentLoc = vertexHitInfo.getVertex();
                fireEvent(new VertexMouseOverEvent(vertexHitInfo.getVertex()));
            }
            fireEvent(new VertexMouseMoveEvent(vertexHitInfo.getVertex()));
            return;
        }

        EdgeHitInfo edgeHitInfo = edgeHit(x, y, edgeMoveDistance);
        if (edgeHitInfo != null) {
            if (current != OVER_EDGE || currentVertical != edgeHitInfo.isVertical()
                    || !currentLoc.equals(edgeHitInfo.getEdge())) {
                fireOutEvent();
                current = OVER_EDGE;
                currentVertical = edgeHitInfo.isVertical();
                currentLoc = edgeHitInfo.getEdge();
                fireEvent(new EdgeMouseOverEvent(edgeHitInfo.isVertical(), edgeHitInfo.getEdge()));
            }
            fireEvent(new EdgeMouseMoveEvent(edgeHitInfo.isVertical(), edgeHitInfo.getEdge()));
            return;
        }

        Vec2i cell = cellHit(x, y);
        if (cell != null) {
            if (current != OVER_CELL || !currentLoc.equals(cell)) {
                fireOutEvent();
                current = OVER_CELL;
                currentLoc = cell;
                fireEvent(new CellMouseOverEvent(cell));
            }
            fireEvent(new CellMouseMoveEvent(cell));
            return;
        }

        fireOutEvent();
        current = OVER_NOTHING;
        currentLoc = null;
    }

    /**
     * Fires the event indicating the the currently over
     * element has been left.
     */
    private void fireOutEvent() {
        if (current == OVER_NOTHING) {
            return;
        } else if (current == OVER_CELL) {
            fireEvent(new CellMouseOutEvent(currentLoc));
        } else if (current == OVER_EDGE) {
            fireEvent(new EdgeMouseOutEvent(currentVertical, currentLoc));
        } else if (current == OVER_VERTEX) {
            fireEvent(new VertexMouseOutEvent(currentLoc));
        }
    }

    @Override
    public void setVertexClickDistance(int vertexClickDistance) {
        this.vertexClickDistance = vertexClickDistance;
    }

    @Override
    public void setVertexMoveDistance(int vertexMoveDistance) {
        this.vertexMoveDistance = vertexMoveDistance;
    }

    @Override
    public void setVertexDistance(int vertexDistance) {
        setVertexClickDistance(vertexDistance);
        setVertexMoveDistance(vertexDistance);
    }

    @Override
    public void setEdgeClickDistance(int edgeClickDistance) {
        this.edgeClickDistance = edgeClickDistance;
    }

    @Override
    public void setEdgeMoveDistance(int edgeMoveDistance) {
        this.edgeMoveDistance = edgeMoveDistance;
    }

    @Override
    public void setEdgeDistance(int edgeDistance) {
        setEdgeClickDistance(edgeDistance);
        setEdgeMoveDistance(edgeDistance);
    }

    @Override
    public HandlerRegistration addCellMouseDownHandler(CellMouseDownHandler handler) {
        return ensureHandlers().addHandler(CellMouseDownEvent.getType(), handler);
    }

    @Override
    public HandlerRegistration addCellMouseUpHandler(CellMouseUpHandler handler) {
        return ensureHandlers().addHandler(CellMouseUpEvent.getType(), handler);
    }

    @Override
    public HandlerRegistration addCellMouseOverHandler(CellMouseOverHandler handler) {
        return ensureHandlers().addHandler(CellMouseOverEvent.getType(), handler);
    }

    @Override
    public HandlerRegistration addCellMouseOutHandler(CellMouseOutHandler handler) {
        return ensureHandlers().addHandler(CellMouseOutEvent.getType(), handler);
    }

    @Override
    public HandlerRegistration addCellMouseMoveHandler(CellMouseMoveHandler handler) {
        return ensureHandlers().addHandler(CellMouseMoveEvent.getType(), handler);
    }

    @Override
    public HandlerRegistration addEdgeMouseDownHandler(EdgeMouseDownHandler handler) {
        return ensureHandlers().addHandler(EdgeMouseDownEvent.getType(), handler);
    }

    @Override
    public HandlerRegistration addEdgeMouseUpHandler(EdgeMouseUpHandler handler) {
        return ensureHandlers().addHandler(EdgeMouseUpEvent.getType(), handler);
    }

    @Override
    public HandlerRegistration addEdgeMouseOverHandler(EdgeMouseOverHandler handler) {
        return ensureHandlers().addHandler(EdgeMouseOverEvent.getType(), handler);
    }

    @Override
    public HandlerRegistration addEdgeMouseOutHandler(EdgeMouseOutHandler handler) {
        return ensureHandlers().addHandler(EdgeMouseOutEvent.getType(), handler);
    }

    @Override
    public HandlerRegistration addEdgeMouseMoveHandler(EdgeMouseMoveHandler handler) {
        return ensureHandlers().addHandler(EdgeMouseMoveEvent.getType(), handler);
    }

    @Override
    public HandlerRegistration addVertexMouseDownHandler(VertexMouseDownHandler handler) {
        return ensureHandlers().addHandler(VertexMouseDownEvent.getType(), handler);
    }

    @Override
    public HandlerRegistration addVertexMouseUpHandler(VertexMouseUpHandler handler) {
        return ensureHandlers().addHandler(VertexMouseUpEvent.getType(), handler);
    }

    @Override
    public HandlerRegistration addVertexMouseOverHandler(VertexMouseOverHandler handler) {
        return ensureHandlers().addHandler(VertexMouseOverEvent.getType(), handler);
    }

    @Override
    public HandlerRegistration addVertexMouseOutHandler(VertexMouseOutHandler handler) {
        return ensureHandlers().addHandler(VertexMouseOutEvent.getType(), handler);
    }

    @Override
    public HandlerRegistration addVertexMouseMoveHandler(VertexMouseMoveHandler handler) {
        return ensureHandlers().addHandler(VertexMouseMoveEvent.getType(), handler);
    }

    /**
     * Fires an event that could be handled by the handlers registered
     * towards this object.
     *
     * @param event The {@link GwtEvent} to fire.
     */
    private void fireEvent(GwtEvent<?> event) {
        if (handlerManager != null) {
            handlerManager.fireEvent(event);
        }
    }

    /**
     * Ensures the existence of the handler manager.
     *
     * @return the handler manager
     * */
    HandlerManager ensureHandlers() {
        return handlerManager == null ? handlerManager = new HandlerManager(this) : handlerManager;
    }

    /**
     * Checks if the passed coordinate hits a vertex within the specified
     * threshold distance.
     *
     * @param x The x coordinate to test, in pixels.
     * @param y The y coordinate to test, in pixels.
     * @param distance The threshold distance.
     * @return The {@link VertexHitInfo} of the hit vertex, or {@code null} if no vertex is hit.
     */
    private VertexHitInfo vertexHit(int x, int y, int distance) {
        if (distance < 0 || squareGridConverter == null || squareGridValidator == null) {
            return null;
        }
        VertexHitInfo vertexHitInfo = squareGridConverter.pixelToVertex(x, y);
        if (vertexHitInfo != null && squareGridValidator.isValidVertex(vertexHitInfo.getVertex())
                && vertexHitInfo.getDist().max() <= distance) {
            return vertexHitInfo;
        }
        return null;
    }

    /**
     * Checks if the passed coordinate hits an edge within the specified
     * threshold distance.
     *
     * @param x The x coordinate to test, in pixels.
     * @param y The y coordinate to test, in pixels.
     * @param distance The threshold distance.
     * @return The {@link EdgeHitInfo} of the hit edge, or {@code null} if no edge is hit.
     */
    private EdgeHitInfo edgeHit(int x, int y, int distance) {
        if (distance < 0 || squareGridConverter == null || squareGridValidator == null) {
            return null;
        }
        EdgeHitInfo edgeHitInfo = squareGridConverter.pixelToEdge(x, y);
        if (edgeHitInfo != null && edgeHitInfo.isVertical()
                && squareGridValidator.isValidVerticalEdge(edgeHitInfo.getEdge())
                && edgeHitInfo.getDist() <= distance) {
            return edgeHitInfo;
        }
        if (edgeHitInfo != null && !edgeHitInfo.isVertical()
                && squareGridValidator.isValidHorizontalEdge(edgeHitInfo.getEdge()) && edgeHitInfo.getDist() <= 4) {
            return edgeHitInfo;
        }
        return null;
    }

    /**
     * Checks if the passed coordinate hits a cell.
     *
     * @param x The x coordinate to test, in pixels.
     * @param y The y coordinate to test, in pixels.
     * @return The {@link Vec2i} containing the coordinates of the hit cell, or {@code null} if no cell is hit.
     */
    private Vec2i cellHit(int x, int y) {
        if (squareGridConverter == null || squareGridValidator == null) {
            return null;
        }
        Vec2i cell = squareGridConverter.pixelToCell(x, y);
        if (squareGridValidator.isValidCell(cell)) {
            return cell;
        }
        return null;
    }

}