org.peergreen.vaadin.diagram.client.DiagramConnector.java Source code

Java tutorial

Introduction

Here is the source code for org.peergreen.vaadin.diagram.client.DiagramConnector.java

Source

/**
 * Copyright 2013 Peergreen S.A.S.
 *
 * 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.peergreen.vaadin.diagram.client;

import static com.google.gwt.dom.client.Style.Cursor.AUTO;

import org.peergreen.vaadin.diagram.Diagram;
import org.peergreen.vaadin.diagram.client.coordinates.IScaledPoint;
import org.peergreen.vaadin.diagram.client.handler.DiagramClientClickHandler;
import org.peergreen.vaadin.diagram.client.handler.DiagramClientKeyUpHandler;
import org.peergreen.vaadin.diagram.client.handler.DiagramClientMouseDownHandler;
import org.peergreen.vaadin.diagram.client.handler.DiagramClientMouseMoveHandler;
import org.peergreen.vaadin.diagram.client.handler.DiagramClientMouseOutHandler;
import org.peergreen.vaadin.diagram.client.handler.DiagramClientMouseUpHandler;
import org.peergreen.vaadin.diagram.client.handler.DiagramClientMouseWheelHandler;
import org.peergreen.vaadin.diagram.client.select.ISelectable;
import org.peergreen.vaadin.diagram.client.ui.CompartmentUI;
import org.peergreen.vaadin.diagram.client.ui.ConnectorUI;
import org.peergreen.vaadin.diagram.client.ui.EntityUI;
import org.peergreen.vaadin.diagram.client.ui.MoveComponentUI;
import org.peergreen.vaadin.diagram.client.ui.PortUI;
import org.peergreen.vaadin.diagram.client.ui.ProvideUI;
import org.peergreen.vaadin.diagram.client.ui.RequireUI;
import org.peergreen.vaadin.diagram.client.ui.UI;
import org.peergreen.vaadin.diagram.client.ui.ZoomComponentUI;

import com.google.gwt.dom.client.CanvasElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Style.Cursor;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.Paintable;
import com.vaadin.client.UIDL;
import com.vaadin.client.communication.RpcProxy;
import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.ui.AbstractComponentConnector;
import com.vaadin.client.ui.PostLayoutListener;
import com.vaadin.client.ui.SimpleManagedLayout;
import com.vaadin.client.ui.dd.VDragAndDropManager;
import com.vaadin.shared.ui.Connect;

/**
 * Vaadin Connector linked to the Diagram component
 * @author Florent Benoit
 */
@Connect(Diagram.class)
public class DiagramConnector extends AbstractComponentConnector
        implements SimpleManagedLayout, PostLayoutListener, Paintable {

    /**
     * Serial version UID.
     */
    private static final long serialVersionUID = 2185981138007699576L;

    /**
     * Server side RPC object.
     */
    private final IDiagramServerRpc serverRpc = RpcProxy.create(IDiagramServerRpc.class, this);

    /**
     * State on the client side.
     */
    private final transient ClientStateModel clientStateModel;

    /**
     * Temporary index for switched entity creation
     */
    private int created = 0;

    /**
     * Build a new connector
     */
    public DiagramConnector() {

        this.clientStateModel = new ClientStateModel(getWidget().getContext2d(), serverRpc);

        registerRpc(IDiagramClientRpc.class, new IDiagramClientRpc() {

            /**
             * Serial version UID.
             */
            private static final long serialVersionUID = 1622915989830982425L;

            @Override
            public void init() {
            }

            @Override
            public void createEntity(final String uuid, final String name) {
                created++;
                EntityUI entity = new EntityUI(clientStateModel, uuid, 80 + (created * 40), created * 40);
                entity.setName(name);
                clientStateModel.getRootUIs().add(entity);
                redraw();
            }

            @Override
            public void updateEntity(String uuid, String name) {
                // Search the entity UI
                EntityUI entityUI = clientStateModel.findEntity(uuid);

                // Update the name
                if (entityUI != null) {
                    entityUI.setName(name);
                    redraw();
                }
            }

            @Override
            public void deleteEntity(final String uuid) {
                EntityUI entity = clientStateModel.findEntity(uuid);
                if (entity != null) {
                    cleanDeletedUiReferences(entity);
                    for (CompartmentUI compartment : entity.getCompartments()) {
                        deleteCompartment(compartment.getId());
                    }
                    clientStateModel.getRootUIs().remove(entity);
                }
                redraw();
            }

            @Override
            public void createCompartment(final String entityUuid, final String uuid, final String name,
                    final String iconType) {
                EntityUI entity = clientStateModel.findEntity(entityUuid);
                if (entity != null) {
                    CompartmentUI compartment = new CompartmentUI(clientStateModel, uuid, entity);
                    compartment.setName(name);
                    String resourceUrl = getResourceUrl(iconType);
                    serverRpc.log(iconType + " -> " + resourceUrl);
                    if (resourceUrl != null) {
                        compartment.setIconUrl(resourceUrl);
                    }
                    entity.addCompartment(compartment);
                }
                redraw();
            }

            @Override
            public void deleteCompartment(final String uuid) {
                CompartmentUI compartment = clientStateModel.findCompartment(uuid);
                if (compartment != null) {
                    cleanDeletedUiReferences(compartment);
                    for (ProvideUI provide : compartment.getProvides()) {
                        deleteProvide(provide.getId());
                    }
                    for (RequireUI require : compartment.getRequires()) {
                        deleteRequire(require.getId());
                    }
                    compartment.getEntity().removeCompartment(compartment);
                }
                redraw();
            }

            @Override
            public void createRequire(final String compartmentUuid, final String uuid, final String name) {
                CompartmentUI compartment = clientStateModel.findCompartment(compartmentUuid);
                if (compartment != null) {
                    RequireUI require = new RequireUI(clientStateModel, uuid, compartment);
                    require.setName(name);
                    compartment.addRequire(require);
                }
                redraw();
            }

            @Override
            public void createProvide(final String compartmentUuid, final String uuid, final String name) {
                CompartmentUI compartment = clientStateModel.findCompartment(compartmentUuid);
                if (compartment != null) {
                    ProvideUI provide = new ProvideUI(clientStateModel, uuid, compartment);
                    provide.setName(name);
                    compartment.addProvide(provide);
                }
                redraw();
            }

            @Override
            public void createConnector(final String uuid, final String sourceUuid, final String targetUuid) {
                PortUI source = clientStateModel.findPort(sourceUuid);
                PortUI target = clientStateModel.findPort(targetUuid);
                if ((source != null) && (target != null)) {
                    // Only creates the ConnectorUI when source and target ports are known
                    clientStateModel.addConnector(new ConnectorUI(clientStateModel, uuid, source, target));
                }
                redraw();
            }

            @Override
            public void deleteProvide(final String uuid) {
                PortUI port = clientStateModel.findPort(uuid);
                if (port != null) {
                    cleanDeletedUiReferences(port);
                    for (ConnectorUI connector : clientStateModel.findConnectors(port)) {
                        clientStateModel.removeConnector(connector);
                    }
                    port.getCompartment().removeProvide((ProvideUI) port);
                }
                redraw();
            }

            @Override
            public void deleteRequire(final String uuid) {
                PortUI port = clientStateModel.findPort(uuid);
                if (port != null) {
                    cleanDeletedUiReferences(port);
                    for (ConnectorUI connector : clientStateModel.findConnectors(port)) {
                        clientStateModel.removeConnector(connector);
                    }
                    port.getCompartment().removeRequire((RequireUI) port);
                }
                redraw();
            }

            @Override
            public void deleteConnector(final String uuid) {
                for (ConnectorUI connector : clientStateModel.getConnectors()) {
                    if (uuid.equals(connector.getId())) {
                        cleanDeletedUiReferences(connector);
                        clientStateModel.removeConnector(connector);
                    }
                }
                redraw();
            }

            @Override
            public void handleDrop(final int x, final int y, final String data) {

                IScaledPoint point = clientStateModel.scalePoint(x, y);
                UI selected = null;
                for (UI ui : clientStateModel.getAllUIs()) {

                    // Can we select this UI ?
                    if (!(ui instanceof ISelectable)) {
                        continue;
                    }

                    ISelectable selectableUI = (ISelectable) ui;

                    // Always send back a dropTarget event
                    selected = selectableUI.getSelectedUI(point);
                    if (selected != null) {
                        // Exit loop if we hit something
                        break;
                    }

                }

                String id = null;
                if (selected != null) {
                    id = selected.getId();
                }
                serverRpc.dropTarget(id, data);
            }

            @Override
            public void zoomIn() {
                doZoomIn();
            }

            @Override
            public void zoomOut() {
                doZoomOut();
            }

            @Override
            public void left() {
                moveLeft();
            }

            @Override
            public void right() {
                moveRight();
            }

            @Override
            public void up() {
                moveUp();
            }

            @Override
            public void down() {
                moveDown();
            }

            @Override
            public void reset() {
                clientStateModel.getRootUIs().clear();
                for (ConnectorUI connector : clientStateModel.getConnectors()) {
                    clientStateModel.removeConnector(connector);
                }
                clientStateModel.getRootUIs().add(clientStateModel.getMoveComponentUI());
                clientStateModel.getRootUIs().add(clientStateModel.getZoomComponentUI());

            }

        });
    }

    public void doZoomOut() {
        clientStateModel.setInvertScale(clientStateModel.getInvertScale() * 0.8);
        clientStateModel.setScale(clientStateModel.getScale() * 1.25);

        getWidget().getContext2d().scale(1.25, 1.25);
        // needs to change the shift
        double translateX = clientStateModel.getTranslateX();
        if (translateX != 0) {
            clientStateModel.setTranslateX(translateX * 0.8);
        }
        double translateY = clientStateModel.getTranslateY();
        if (translateY != 0) {
            clientStateModel.setTranslateY(translateY * 0.8);
        }

        redraw();

    }

    public void doZoomIn() {
        clientStateModel.setScale(clientStateModel.getScale() * 0.8);
        clientStateModel.setInvertScale(clientStateModel.getInvertScale() * 1.25);
        getWidget().getContext2d().scale(0.8, 0.8);
        double translateX = clientStateModel.getTranslateX();
        if (translateX != 0) {
            clientStateModel.setTranslateX(translateX * 1.25);
        }
        double translateY = clientStateModel.getTranslateY();
        if (translateY != 0) {
            clientStateModel.setTranslateY(translateY * 1.25);
        }
        redraw();
    }

    public void moveLeft() {
        translate(clientStateModel.getTranslateOffset(), 0);
    }

    public void moveRight() {
        translate(-clientStateModel.getTranslateOffset(), 0);
    }

    public void moveUp() {
        translate(0, clientStateModel.getTranslateOffset());
    }

    public void moveDown() {
        translate(0, -clientStateModel.getTranslateOffset());
    }

    protected void translate(double x, double y) {
        clientStateModel.setTranslateX(clientStateModel.getTranslateX() - x);
        clientStateModel.setTranslateY(clientStateModel.getTranslateY() - y);
        getWidget().getContext2d().translate(x, y);
        redraw();
    }

    /**
     * Clean the selectedUi field if the given ui is the one currently selected
     * @param ui potentially selected Ui
     */
    protected void cleanDeletedUiReferences(UI ui) {
        if (ui.equals(clientStateModel.getSelectedUI())) {
            clientStateModel.setSelectedUI(null);
        }
        if (ui.equals(clientStateModel.getCurrentMouseOverUI())) {
            clientStateModel.setCurrentMouseOverUI(null);
        }
        if (ui.equals(clientStateModel.getCurrentUI())) {
            clientStateModel.setCurrentUI(null);
        }
    }

    @Override
    protected void init() {
        super.init();

        // Register all the mouse events.
        getWidget().addMouseDownHandler(new DiagramClientMouseDownHandler(this, clientStateModel));
        getWidget().addMouseMoveHandler(new DiagramClientMouseMoveHandler(this, clientStateModel));
        getWidget().addMouseOutHandler(new DiagramClientMouseOutHandler(this, clientStateModel));
        getWidget().addMouseUpHandler(new DiagramClientMouseUpHandler(this, clientStateModel));
        getWidget().addClickHandler(new DiagramClientClickHandler(this, clientStateModel));
        getWidget().addMouseWheelHandler(new DiagramClientMouseWheelHandler(this, clientStateModel));

        // For the drop, notify the Drag and Drop manager
        getWidget().addMouseOutHandler(new MouseOutHandler() {

            @Override
            public void onMouseOut(MouseOutEvent event) {
                VDragAndDropManager.get().setCurrentDropHandler(null);
            }
        });
        getWidget().addMouseOverHandler(new MouseOverHandler() {
            @Override
            public void onMouseOver(MouseOverEvent event) {
                VDragAndDropManager.get().setCurrentDropHandler(getWidget().getDropHandler());
            }
        });

        // Key events
        getWidget().addKeyUpHandler(new DiagramClientKeyUpHandler(this, clientStateModel));

        MoveComponentUI moveComponentUI = new MoveComponentUI(this, clientStateModel, "moveComponent");
        clientStateModel.getRootUIs().add(moveComponentUI);
        clientStateModel.setMoveComponentUI(moveComponentUI);

        ZoomComponentUI zoomComponentUI = new ZoomComponentUI(this, clientStateModel, "zoomComponent");
        clientStateModel.getRootUIs().add(zoomComponentUI);
        clientStateModel.setZoomComponentUI(zoomComponentUI);

    }

    /**
     * Draw the component.
     */
    public void redraw() {

        // Perform the shift
        getWidget().getContext2d().clearRect(clientStateModel.getTranslateX() - 100,
                clientStateModel.getTranslateY() - 100, 5000, 5000);

        // Redraw root UIs
        for (UI ui : clientStateModel.getRootUIs()) {
            ui.draw();
        }

        // Notice that we're redrawin a UI that has already been draw (directly or indirectly) from the root UIs.
        // That leads to weird impressions: text bolder than they should be, ...
        // Not super hyper important, but deserves a note :)

        // Redraw the selected UI
        if (clientStateModel.getCurrentMouseOverUI() != null) {
            clientStateModel.getCurrentMouseOverUI().draw();
        }

        // Draw connectors
        for (ConnectorUI connector : clientStateModel.getConnectors()) {
            connector.draw();
        }

        // Draw temp ui if any
        if (clientStateModel.getTempDrawUI() != null) {
            clientStateModel.getTempDrawUI().draw();
        }

        UI moveComponentUI = clientStateModel.getMoveComponentUI();
        if (moveComponentUI != null) {
            moveComponentUI.draw();
        }

        UI zoomComponentUI = clientStateModel.getZoomComponentUI();
        if (zoomComponentUI != null) {
            zoomComponentUI.draw();
        }

    }

    /**
     * Unset the cursor
     */
    public void unsetCursor() {
        getWidget().getElement().getStyle().setCursor(AUTO);
    }

    /**
     * Defines the cursor.
     * @param cursor
     */
    public void setCursor(Cursor cursor) {
        getWidget().getElement().getStyle().setCursor(cursor);
    }

    /**
     * Creates the widget which is the HTML5 canvas.
     */
    @Override
    protected Widget createWidget() {
        CanvasElement element = Document.get().createCanvasElement();
        DiagramWidget diagramWidget = new DiagramWidget(this, element);
        diagramWidget.setStyleName("canvas-widget");
        return diagramWidget;
    }

    /**
     * Gets the widget.
     */
    @Override
    public DiagramWidget getWidget() {
        return (DiagramWidget) super.getWidget();
    }

    /**
     * Gets the state shared between the client and the server.
     */
    @Override
    public DiagramComponentState getState() {
        return (DiagramComponentState) super.getState();
    }

    /**
     * Notification when the state is being changed.
     */
    @Override
    public void onStateChanged(StateChangeEvent stateChangeEvent) {
        super.onStateChanged(stateChangeEvent);
    }

    @Override
    public void layout() {

        // Update the height/width
        int newHeight = getWidget().getElement().getOffsetHeight();
        int newWidth = getWidget().getElement().getOffsetWidth();
        if (getWidget().getCanvasElement().getHeight() != newHeight
                || getWidget().getCanvasElement().getWidth() != newWidth) {
            getWidget().getCanvasElement().setHeight(newHeight);
            getWidget().getCanvasElement().setWidth(newWidth);
            getWidget().getContext2d().scale(clientStateModel.getScale(), clientStateModel.getScale());
            getWidget().getContext2d().translate(-clientStateModel.getTranslateX(),
                    -clientStateModel.getTranslateY());
        }
    }

    @Override
    public void postLayout() {
        redraw();
    }

    @Override
    public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
        getWidget().setCurrentClient(client);

    }

}