Java tutorial
/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2012-2014 The OpenNMS Group, Inc. * OpenNMS(R) is Copyright (C) 1999-2014 The OpenNMS Group, Inc. * * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc. * * OpenNMS(R) is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * OpenNMS(R) is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with OpenNMS(R). If not, see: * http://www.gnu.org/licenses/ * * For more information contact: * OpenNMS(R) Licensing <license@opennms.org> * http://www.opennms.org/ * http://www.opennms.com/ *******************************************************************************/ package org.opennms.features.topology.app.internal.gwt.client; import static org.opennms.features.topology.app.internal.gwt.client.d3.TransitionBuilder.fadeIn; import static org.opennms.features.topology.app.internal.gwt.client.d3.TransitionBuilder.fadeOut; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.opennms.features.topology.app.internal.gwt.client.d3.D3; import org.opennms.features.topology.app.internal.gwt.client.d3.D3Behavior; import org.opennms.features.topology.app.internal.gwt.client.d3.D3Drag; import org.opennms.features.topology.app.internal.gwt.client.d3.D3Events; import org.opennms.features.topology.app.internal.gwt.client.d3.D3Events.Handler; import org.opennms.features.topology.app.internal.gwt.client.d3.D3Transform; import org.opennms.features.topology.app.internal.gwt.client.d3.Func; import org.opennms.features.topology.app.internal.gwt.client.handler.DragHandlerManager; import org.opennms.features.topology.app.internal.gwt.client.handler.DragObject; import org.opennms.features.topology.app.internal.gwt.client.handler.MarqueeSelectHandler; import org.opennms.features.topology.app.internal.gwt.client.handler.PanHandler; import org.opennms.features.topology.app.internal.gwt.client.map.SVGTopologyMap; import org.opennms.features.topology.app.internal.gwt.client.service.ServiceRegistry; import org.opennms.features.topology.app.internal.gwt.client.service.support.DefaultServiceRegistry; import org.opennms.features.topology.app.internal.gwt.client.svg.BoundingRect; import org.opennms.features.topology.app.internal.gwt.client.svg.SVGGElement; import org.opennms.features.topology.app.internal.gwt.client.svg.SVGMatrix; import org.opennms.features.topology.app.internal.gwt.client.svg.SVGPoint; import org.opennms.features.topology.app.internal.gwt.client.view.TopologyView; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.JsArray; import com.google.gwt.core.client.JsArrayInteger; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.EventTarget; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.Style.Cursor; import com.google.gwt.touch.client.Point; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.Window.Navigator; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ApplicationConnection; import com.vaadin.client.MouseEventDetailsBuilder; import com.vaadin.shared.MouseEventDetails; public class VTopologyComponent extends Composite implements SVGTopologyMap, TopologyView.Presenter<VTopologyComponent.TopologyViewRenderer> { private static final int UPDATE_PHYSICAL_DIMENSIONS_TIMER_PERIOD_MILLIS = 250; public interface TopologyViewRenderer { void draw(GWTGraph graph, TopologyView<TopologyViewRenderer> topologyView, GWTBoundingBox oldBBox); } public interface GraphUpdateListener { void onGraphUpdated(GWTGraph graph, GWTBoundingBox oldBBox); } public class SVGGraphDrawerNoTransition extends SVGGraphDrawer { public SVGGraphDrawerNoTransition(D3Behavior dragBehavior, ServiceRegistry serviceRegistry) { super(dragBehavior, serviceRegistry); } @Override protected D3Behavior enterTransition() { return new D3Behavior() { @Override public D3 run(D3 selection) { return selection; } }; } @Override protected D3Behavior exitTransition() { return new D3Behavior() { @Override public D3 run(D3 selection) { return selection; } }; } @Override protected D3Behavior updateTransition() { return new D3Behavior() { @Override public D3 run(D3 selection) { return selection; } }; } } public class SVGGraphDrawer implements TopologyViewRenderer { GWTGraph m_graph; Element m_edgeGroup; D3Behavior m_dragBehavior; Handler<GWTVertex> m_clickHandler; private Handler<GWTVertex> m_dblClickHandler; Handler<GWTEdge> m_edgeClickHandler; Handler<GWTVertex> m_contextMenuHandler; private Handler<GWTEdge> m_edgeContextHandler; @SuppressWarnings("unchecked") public SVGGraphDrawer(D3Behavior dragBehavior, ServiceRegistry serviceRegistry) { m_dragBehavior = dragBehavior; m_clickHandler = serviceRegistry.findProvider(Handler.class, "(handlerType=vertexClick)"); m_dblClickHandler = serviceRegistry.findProvider(Handler.class, "(handlerType=vertexDblClick)"); m_edgeClickHandler = serviceRegistry.findProvider(Handler.class, "(handlerType=edgeClick)"); m_contextMenuHandler = serviceRegistry.findProvider(Handler.class, "(handlerType=vertexContextMenu)"); m_edgeContextHandler = serviceRegistry.findProvider(Handler.class, "(handlerType=edgeContextMenu)"); } public Handler<GWTVertex> getClickHandler() { return m_clickHandler; } public Handler<GWTVertex> getDblClickHandler() { return m_dblClickHandler; } public Handler<GWTEdge> getEdgeClickHandler() { return m_edgeClickHandler; } public void setEdgeClickHandler(Handler<GWTEdge> edgeClickHandler) { m_edgeClickHandler = edgeClickHandler; } public void setClickHandler(Handler<GWTVertex> clickHandler) { m_clickHandler = clickHandler; } public Handler<GWTVertex> getContextMenuHandler() { return m_contextMenuHandler; } public void setContextMenuHandler(Handler<GWTVertex> contextMenuHandler) { m_contextMenuHandler = contextMenuHandler; } public GWTGraph getGraph() { return m_graph; } public D3Behavior getDragBehavior() { return m_dragBehavior; } @Override public void draw(GWTGraph graph, final TopologyView<TopologyViewRenderer> topologyView, GWTBoundingBox oldBBox) { D3 edgeSelection = getEdgeSelection(graph, topologyView); D3 vertexSelection = getVertexSelection(graph, topologyView); vertexSelection.enter().create(GWTVertex.create()).call(setupEventHandlers()) .attr("transform", new Func<String, GWTVertex>() { @Override public String call(GWTVertex vertex, int index) { return "translate(" + vertex.getInitialX() + "," + vertex.getInitialY() + ")"; } }).attr("opacity", 1); //Exits edgeSelection.exit().remove(); vertexSelection.exit().with(new D3Behavior() { @Override public D3 run(D3 selection) { return selection.transition().delay(0).duration(500); } }).attr("transform", new Func<String, GWTVertex>() { @Override public String call(GWTVertex vertex, int index) { return "translate(" + vertex.getInitialX() + "," + vertex.getInitialY() + ")"; } }).attr("opacity", 0).remove(); //Updates edgeSelection.call(GWTEdge.draw()).attr("opacity", 1); vertexSelection.with(updateTransition()).call(GWTVertex.draw()).attr("opacity", 1); //Enters edgeSelection.enter().create(GWTEdge.create()).call(setupEdgeEventHandlers()); //Scaling and Fit to Zoom transitions SVGMatrix transform = topologyView.calculateNewTransform(graph.getBoundingBox()); int width = topologyView.getPhysicalWidth(); int height = topologyView.getPhysicalHeight(); D3 selection = D3.d3().select(topologyView.getSVGViewPort()); String attr = selection.attr("transform"); // Ugly hack; what is going on here? Can't figure out how the viewport is ending up with NaN if (attr.contains("NaN")) { attr = "translate(0,0)"; } D3Transform tform = D3.getTransform(attr); JsArrayInteger p0 = (JsArrayInteger) JsArrayInteger.createArray(); int x = tform.getX(); int oldCenterX = (int) Math.round(((width / 2 - x) / tform.getScaleX())); int y = tform.getY(); int oldCenterY = (int) Math.round(((height / 2 - y) / tform.getScaleY())); p0.push(oldCenterX); p0.push(oldCenterY); p0.push((int) (width / tform.getScaleX())); p0.push((int) (height / tform.getScaleY())); JsArrayInteger p1 = (JsArrayInteger) JsArrayInteger.createArray(); int newCenterX = graph.getBoundingBox().getX() + graph.getBoundingBox().getWidth() / 2; int newCenterY = graph.getBoundingBox().getY() + graph.getBoundingBox().getHeight() / 2; p1.push(newCenterX); p1.push(newCenterY); p1.push(graph.getBoundingBox().getWidth()); p1.push(graph.getBoundingBox().getHeight()); D3.d3().zoomTransition(selection, width, height, p0, p1); D3.d3().selectAll(GWTEdge.SVG_EDGE_ELEMENT) .style("stroke-width", GWTEdge.EDGE_WIDTH / transform.getA() + "px").transition().delay(750) .duration(500).attr("opacity", "1").transition(); } protected D3Behavior enterTransition() { return fadeIn(500, 1000); } protected D3Behavior exitTransition() { return fadeOut(500, 0); } protected D3Behavior updateTransition() { return new D3Behavior() { @Override public D3 run(D3 selection) { return selection.transition().delay(500).duration(500); } }; } private D3Behavior setupEdgeEventHandlers() { return new D3Behavior() { @Override public D3 run(D3 selection) { return selection.on(D3Events.CLICK.event(), getEdgeClickHandler()) .on(D3Events.CONTEXT_MENU.event(), getEdgeContextHandler()); } }; } private D3Behavior setupEventHandlers() { return new D3Behavior() { @Override public D3 run(D3 selection) { return selection.on(D3Events.CLICK.event(), getClickHandler()) .on(D3Events.CONTEXT_MENU.event(), getContextMenuHandler()) .on(D3Events.DOUBLE_CLICK.event(), getDblClickHandler()).call(getDragBehavior()); } }; } private D3 getVertexSelection(GWTGraph graph, TopologyView<TopologyViewRenderer> topologyView) { D3 vertexGroup = D3.d3().select(topologyView.getVertexGroup()); Func<String, GWTVertex> vertexIdentifierFunction = new Func<String, GWTVertex>() { @Override public String call(GWTVertex param, int index) { return param.getId() + "_" + param.getSVGIconId(); } }; return vertexGroup.selectAll(GWTVertex.VERTEX_CLASS_NAME).data(graph.getVertices(), vertexIdentifierFunction); } private D3 getEdgeSelection(GWTGraph graph, TopologyView<TopologyViewRenderer> topologyView) { D3 edgeGroup = D3.d3().select(topologyView.getEdgeGroup()); Func<String, GWTEdge> edgeIdentifierFunction = new Func<String, GWTEdge>() { @Override public String call(GWTEdge edge, int index) { return edge.getId(); } }; return edgeGroup.selectAll(GWTEdge.SVG_EDGE_ELEMENT).data(graph.getEdges(), edgeIdentifierFunction); } public Handler<GWTEdge> getEdgeContextHandler() { return m_edgeContextHandler; } public void setEdgeContextHandler(Handler<GWTEdge> edgeClickHandler) { m_edgeContextHandler = edgeClickHandler; } } private static VTopologyComponentUiBinder uiBinder = GWT.create(VTopologyComponentUiBinder.class); interface VTopologyComponentUiBinder extends UiBinder<Widget, VTopologyComponent> { } private ApplicationConnection m_client; private GWTGraph m_graph; private DragObject m_dragObject; @UiField FlowPanel m_componentHolder; private D3Drag m_d3PanDrag; private SVGGraphDrawer m_graphDrawer; private SVGGraphDrawerNoTransition m_graphDrawerNoTransition; private List<Element> m_selectedElements = new ArrayList<Element>(); private DragHandlerManager m_svgDragHandlerManager; private TopologyViewRenderer m_currentViewRender; private TopologyView<TopologyViewRenderer> m_topologyView; private List<GraphUpdateListener> m_graphListenerList = new ArrayList<GraphUpdateListener>(); private TopologyComponentServerRpc m_serverRpc; private int m_width; private int m_height; private String m_activeTool; // Used to periodically poll the size of the map container // and notify the server if/when the size changes. final Timer m_updatePhysicalDimensionTimer = new Timer() { @Override public void run() { sendPhysicalDimensions(); } }; public VTopologyComponent() { initWidget(uiBinder.createAndBindUi(this)); m_graph = GWTGraph.create(); } @SuppressWarnings("serial") @Override protected void onLoad() { super.onLoad(); consoleLog("onLoad"); ServiceRegistry serviceRegistry = new DefaultServiceRegistry(); serviceRegistry.register(vertexClickHandler(), new HashMap<String, String>() { { put("handlerType", "vertexClick"); } }, Handler.class); serviceRegistry.register(vertexDblClickHandler(), new HashMap<String, String>() { { put("handlerType", "vertexDblClick"); } }, Handler.class); serviceRegistry.register(vertexContextMenuHandler(), new HashMap<String, String>() { { put("handlerType", "vertexContextMenu"); } }, Handler.class); serviceRegistry.register(edgeContextHandler(), new HashMap<String, String>() { { put("handlerType", "edgeContextMenu"); } }, Handler.class); serviceRegistry.register(edgeClickHandler(), new HashMap<String, String>() { { put("handlerType", "edgeClick"); } }, Handler.class); m_topologyView = new TopologyViewImpl(); m_topologyView.setPresenter(this); m_componentHolder.setSize("100%", "100%"); m_componentHolder.add(m_topologyView.asWidget()); m_svgDragHandlerManager = new DragHandlerManager(); m_svgDragHandlerManager.addDragBehaviorHandler(PanHandler.DRAG_BEHAVIOR_KEY, new PanHandler(this)); m_svgDragHandlerManager.addDragBehaviorHandler(MarqueeSelectHandler.DRAG_BEHAVIOR_KEY, new MarqueeSelectHandler(this, m_topologyView)); m_svgDragHandlerManager.setCurrentDragHandler(PanHandler.DRAG_BEHAVIOR_KEY); setupDragBehavior(m_topologyView.getSVGElement(), m_svgDragHandlerManager); D3 svgElement = D3.d3().select(m_topologyView.getSVGElement()); // svgElement.on("dblclick", new Handler<Void>() { // // @Override // public void call(Void t, int index) { // JsArrayInteger pos = D3.getMouse(m_topologyView.getSVGElement()); // onBackgroundDoubleClick(m_topologyView.getPoint(pos.get(0), pos.get(1))); // } // // }) svgElement.on(D3Events.CONTEXT_MENU.event(), new Handler<Void>() { @Override public void call(Void aVoid, int index) { NativeEvent event = D3.getEvent(); if (D3.eventDefaultPrevented()) { return; } if (!isMarqueeSelected()) { EventTarget target = event.getEventTarget(); if (target.equals(m_topologyView.getSVGElement())) { onContextMenu(null, event.getClientX(), event.getClientY(), "map"); } } event.preventDefault(); event.stopPropagation(); } }); svgElement.on(D3Events.CLICK.event(), new Handler<Void>() { @Override public void call(Void aVoid, int index) { NativeEvent event = D3.getEvent(); if (D3.eventDefaultPrevented()) { return; } if (!isMarqueeSelected() && event.getButton() == NativeEvent.BUTTON_LEFT && event.getEventTarget().equals(m_topologyView.getSVGElement())) { onBackgroundClick(); } event.preventDefault(); event.stopPropagation(); } }); svgElement.on(D3Events.MOUSE_WHEEL.event(), new Handler<Void>() { @Override public void call(Void t, int index) { double scrollVal = (double) D3.getEvent().getMouseWheelVelocityY() / 30.0; SVGPoint centerPos = m_topologyView.getCenterPos(m_graph.getBoundingBox()); onMouseWheel(scrollVal, (int) centerPos.getX(), (int) centerPos.getY()); } }); D3Behavior dragBehavior = new D3Behavior() { @Override public D3 run(D3 selection) { D3Drag drag = D3.getDragBehavior(); drag.on(D3Events.DRAG_START.event(), vertexDragStartHandler()); drag.on(D3Events.DRAG.event(), vertexDragHandler()); drag.on(D3Events.DRAG_END.event(), vertexDragEndHandler()); selection.call(drag); return selection; } }; m_graphDrawer = new SVGGraphDrawer(dragBehavior, serviceRegistry); m_graphDrawerNoTransition = new SVGGraphDrawerNoTransition(dragBehavior, serviceRegistry); setTopologyViewRenderer(m_graphDrawer); m_updatePhysicalDimensionTimer.scheduleRepeating(UPDATE_PHYSICAL_DIMENSIONS_TIMER_PERIOD_MILLIS); } public TopologyView<TopologyViewRenderer> getTopologyView() { return m_topologyView; } private void setupDragBehavior(final Element panElem, final DragHandlerManager handlerManager) { D3Drag d3Pan = D3.getDragBehavior(); d3Pan.on(D3Events.DRAG_START.event(), new Handler<Element>() { @Override public void call(Element elem, int index) { handlerManager.onDragStart(elem); } }); d3Pan.on(D3Events.DRAG.event(), new Handler<Element>() { @Override public void call(Element elem, int index) { handlerManager.onDrag(elem); } }); d3Pan.on(D3Events.DRAG_END.event(), new Handler<Element>() { @Override public void call(Element elem, int index) { handlerManager.onDragEnd(elem); } }); D3 select = D3.d3().select(panElem); select.call(d3Pan); } private void deselectAllItems() { m_serverRpc.deselectAllItems(); } private Handler<GWTVertex> vertexContextMenuHandler() { return new D3Events.Handler<GWTVertex>() { @Override public void call(final GWTVertex vertex, int index) { showContextMenu(vertex.getId(), D3.getEvent().getClientX(), D3.getEvent().getClientY(), "vertex"); D3.getEvent().preventDefault(); D3.getEvent().stopPropagation(); } }; } private Handler<GWTEdge> edgeContextHandler() { return new D3Events.Handler<GWTEdge>() { @Override public void call(final GWTEdge edge, int index) { showContextMenu(edge.getId(), D3.getEvent().getClientX(), D3.getEvent().getClientY(), "edge"); D3.getEvent().preventDefault(); D3.getEvent().stopPropagation(); } }; } private Handler<GWTEdge> edgeClickHandler() { return new Handler<GWTEdge>() { @Override public void call(GWTEdge edge, int index) { m_serverRpc.edgeClicked(edge.getId()); D3.getEvent().preventDefault(); D3.getEvent().stopPropagation(); } }; } private Handler<GWTVertex> vertexClickHandler() { return new D3Events.Handler<GWTVertex>() { @Override public void call(GWTVertex vertex, int index) { if (D3.eventDefaultPrevented()) { return; } NativeEvent event = D3.getEvent(); SVGGElement vertexElement = event.getCurrentEventTarget().cast(); vertexElement.getParentElement().appendChild(vertexElement); event.preventDefault(); event.stopPropagation(); final MouseEventDetails mouseDetails = MouseEventDetailsBuilder.buildMouseEventDetails(event, getElement()); m_serverRpc.vertexClicked(vertex.getId(), mouseDetails, Navigator.getPlatform()); } }; } private Handler<GWTVertex> vertexDblClickHandler() { return new D3Events.Handler<GWTVertex>() { @Override public void call(GWTVertex vert, int index) { } }; } private Handler<GWTVertex> vertexDragEndHandler() { return new Handler<GWTVertex>() { @Override public void call(GWTVertex vertex, int index) { if (D3.getEvent().getButton() != NativeEvent.BUTTON_RIGHT) { final List<String> values = new ArrayList<String>(); final String[] vertexIds = m_dragObject.getDraggedVertices(); D3.d3().selectAll(GWTVertex.VERTEX_CLASS_NAME).each(new Handler<GWTVertex>() { @Override public void call(GWTVertex vertex, int index) { for (String id : vertexIds) { if (vertex.getId().equals(id)) { values.add("id," + vertex.getId() + "|x," + vertex.getX() + "|y," + vertex.getY() + "|selected," + vertex.isSelected()); } } } }); if (m_dragObject.getDraggableElement().getAttribute("class").equals("vertex")) { //if(!D3.getEvent().getShiftKey()) { // deselectAllItems(false); //} } // m_client.updateVariable(getPaintableId(), "updateVertices", values.toArray(new String[] {}), false); // m_client.sendPendingVariableChanges(); m_serverRpc.updateVertices(values); D3.getEvent().preventDefault(); D3.getEvent().stopPropagation(); } } }; } private Handler<GWTVertex> vertexDragStartHandler() { return new Handler<GWTVertex>() { @Override public void call(GWTVertex vertex, int index) { NativeEvent event = D3.getEvent(); Element draggableElement = Element.as(event.getEventTarget()).getParentElement(); D3 selection = null; boolean isSelected = draggableElement.getAttribute("class").equals("vertex selected"); if (isSelected) { selection = D3.d3().selectAll(GWTVertex.SELECTED_VERTEX_CLASS_NAME); } else { selection = D3.d3().select(Element.as(event.getEventTarget()).getParentElement()); } m_dragObject = new DragObject(VTopologyComponent.this.m_topologyView, draggableElement, m_topologyView.getSVGViewPort(), selection); D3.getEvent().preventDefault(); D3.getEvent().stopPropagation(); } }; } private Handler<GWTVertex> vertexDragHandler() { return new Handler<GWTVertex>() { @Override public void call(GWTVertex vertex, int index) { m_dragObject.move(); if (getViewRenderer() == m_graphDrawer) { m_currentViewRender = m_graphDrawerNoTransition; } for (GraphUpdateListener listener : m_graphListenerList) { listener.onGraphUpdated(m_graph, m_graph.getBoundingBox()); } D3.getEvent().preventDefault(); D3.getEvent().stopPropagation(); } }; } public void injectSVGDefs(ApplicationConnection appConnection, List<String> svgDefFiles) { for (String file : svgDefFiles) { D3.d3().injectSVGDef(appConnection.translateVaadinUri(file)); } } public void updateGraph(ApplicationConnection applicationConnection, TopologyComponentState componentState) { GWTGraph graph = GWTGraph.create(); m_client = applicationConnection; for (SharedVertex sharedVertex : componentState.getVertices()) { GWTVertex vertex = GWTVertex.create(sharedVertex.getKey(), sharedVertex.getX(), sharedVertex.getY()); vertex.setInitialX(sharedVertex.getInitialX()); vertex.setInitialY(sharedVertex.getInitialY()); boolean selected = sharedVertex.getSelected(); vertex.setSelected(selected); vertex.setSVGIconId(sharedVertex.getSVGIconId()); vertex.setLabel(sharedVertex.getLabel()); vertex.setStatus(sharedVertex.getStatus()); vertex.setStatusCount(sharedVertex.getStatusCount()); vertex.setTooltipText(sharedVertex.getTooltipText()); vertex.setStyleName(sharedVertex.getStyleName()); graph.addVertex(vertex); } for (SharedEdge sharedEdge : componentState.getEdges()) { String edgeKey = sharedEdge.getKey(); String sourceKey = sharedEdge.getSourceKey(); String targetKey = sharedEdge.getTargetKey(); GWTVertex source = graph.findVertexById(sourceKey); GWTVertex target = graph.findVertexById(targetKey); GWTEdge edge = GWTEdge.create(edgeKey, source, target); boolean selected = sharedEdge.getSelected(); String cssClass = sharedEdge.getCssClass(); edge.setSelected(selected); edge.setCssClass(cssClass); String ttText = sharedEdge.getTooltipText(); edge.setTooltipText(ttText); edge.setStatus(sharedEdge.getStatus()); graph.addEdge(edge); } JsArray<GWTEdge> edges = graph.getEdges(); sortEdges(edges); for (int i = 0; i < edges.length(); i++) { if (i != 0) { GWTEdge edge1 = edges.get(i - 1); GWTEdge edge2 = edges.get(i); String edge1Source = minEndPoint(edge1); String edge2Source = minEndPoint(edge2); String edge1Target = maxEndPoint(edge1); String edge2Target = maxEndPoint(edge2); if ((edge1Source.equals(edge2Source) && edge1Target.equals(edge2Target))) { edge2.setLinkNum(edge1.getLinkNum() + 1); } else { edge2.setLinkNum(1); } } } int x = componentState.getBoundX(); int y = componentState.getBoundY(); int width = componentState.getBoundWidth(); int height = componentState.getBoundHeight(); GWTBoundingBox oldBBox = m_graph.getBoundingBox(); graph.setBoundingBox(GWTBoundingBox.create(x, y, width, height)); setGraph(graph, oldBBox); } private void sendPhysicalDimensions() { // only send physicalDimensions if they actually have changed if (m_width != m_topologyView.getPhysicalWidth() || m_height != m_topologyView.getPhysicalHeight()) { consoleLog("Updating physical bounds from width: " + m_width + " height: " + m_height + " to width: " + m_topologyView.getPhysicalWidth() + " height: " + m_topologyView.getPhysicalHeight()); m_width = m_topologyView.getPhysicalWidth(); m_height = m_topologyView.getPhysicalHeight(); m_serverRpc.mapPhysicalBounds(m_width, m_height); } } private String minEndPoint(GWTEdge edge1) { return edge1.getSource().getId().compareTo(edge1.getTarget().getId()) < 0 ? edge1.getSource().getId() : edge1.getTarget().getId(); } private String maxEndPoint(GWTEdge edge1) { return edge1.getSource().getId().compareTo(edge1.getTarget().getId()) < 0 ? edge1.getTarget().getId() : edge1.getSource().getId(); } private native void sortEdges(JsArray<GWTEdge> list)/*-{ list.sort(function(a,b){ var sourceA = a.source.id < a.target.id ? a.source.id : a.target.id; var targetA = a.source.id < a.target.id ? a.target.id : a.source.id; var sourceB = b.source.id < b.target.id ? b.source.id : b.target.id; var targetB = b.source.id < b.target.id ? b.target.id : b.source.id; if(sourceA > sourceB){ return 1; } else if(sourceA < sourceB){ return -1; }else{ if(targetA > targetB){ return 1; } if(targetA < targetB){ return -1; } else {return 0;} } }); }-*/; public void setActiveTool(String toolname) { if (toolname.equals("pan")) { m_svgDragHandlerManager.setCurrentDragHandler(PanHandler.DRAG_BEHAVIOR_KEY); m_topologyView.getSVGElement().getStyle().setCursor(Cursor.MOVE); } else if (toolname.equals("select")) { m_svgDragHandlerManager.setCurrentDragHandler(MarqueeSelectHandler.DRAG_BEHAVIOR_KEY); m_topologyView.getSVGElement().getStyle().setCursor(Cursor.CROSSHAIR); } m_activeTool = toolname; } private boolean isMarqueeSelected() { return "select".equals(m_activeTool); } /** * Sets the graph, updates the ViewRenderer if need be and * updates all graphUpdateListeners * @param graph * @param oldBBox */ private void setGraph(GWTGraph graph, GWTBoundingBox oldBBox) { m_graph = graph; //Set the ViewRenderer to the Animated one if it isn't already if (getViewRenderer() != m_graphDrawer) { setTopologyViewRenderer(m_graphDrawer); } if (this.isAttached()) { updateGraphUpdateListeners(oldBBox); } } public ApplicationConnection getClient() { return m_client; } public void showContextMenu(String target, int x, int y, String type) { m_serverRpc.contextMenu(target, type, x, y); } @Override public void setVertexSelection(List<String> vertIds) { final MouseEventDetails mouseDetails = MouseEventDetailsBuilder.buildMouseEventDetails(D3.getEvent(), getElement()); m_serverRpc.marqueeSelection(vertIds.toArray(new String[] {}), mouseDetails); } /** * Returns the D3 selection for all Vertex svg elements */ @Override public D3 selectAllVertexElements() { return D3.d3().selectAll(GWTVertex.VERTEX_CLASS_NAME); } private BoundingRect createBoundingRect(JsArray<GWTVertex> vertices, boolean fitToView) { final BoundingRect rect = new BoundingRect(); for (int i = 0; i < vertices.length(); i++) { GWTVertex vertex = vertices.get(i); if (fitToView || vertex.isSelected()) { double vertexX = vertex.getX(); double vertexY = vertex.getY(); rect.addPoint(new Point(vertexX, vertexY)); } } return rect; } public void updateMapPosition() { SVGPoint pos = m_topologyView.getCenterPos(m_graph.getBoundingBox()); Map<String, Object> point = new HashMap<String, Object>(); point.put("x", (int) Math.round(pos.getX())); point.put("y", (int) Math.round(pos.getY())); m_serverRpc.clientCenterPoint((int) Math.round(pos.getX()), (int) Math.round(pos.getY())); } @Override public TopologyViewRenderer getViewRenderer() { return m_currentViewRender; } private void setTopologyViewRenderer(TopologyViewRenderer viewRenderer) { m_currentViewRender = viewRenderer; } @Override public void onBackgroundClick() { m_serverRpc.backgroundClicked(); } @Override public void onContextMenu(Object target, int x, int y, String type) { showContextMenu((String) target, x, y, type); } @Override public void addGraphUpdateListener(GraphUpdateListener listener) { m_graphListenerList.add(listener); } private void updateGraphUpdateListeners(GWTBoundingBox oldBBox) { for (GraphUpdateListener listener : m_graphListenerList) { listener.onGraphUpdated(m_graph, oldBBox); } } @Override public void onMouseWheel(double scrollVal, int x, int y) { m_serverRpc.scrollWheel(scrollVal, x, y); } public static final native void eval(JavaScriptObject elem) /*-{ $wnd.console.log($wnd.eval(elem)); }-*/; public static final native void typeof(Element elem) /*-{ $wnd.console.log("typeof: " + typeof(elem)); }-*/; private static final native void consoleLog(Object message) /*-{ $wnd.console.log(message); }-*/; public void setPhysicalWidth(int physicalWidth) { m_width = physicalWidth; } public void setPhysicalHeight(int physicalHeight) { m_height = physicalHeight; } @Override public void onBackgroundDoubleClick(SVGPoint center) { m_serverRpc.backgroundDoubleClick(center.getX(), center.getY()); } public void setComponentServerRpc(TopologyComponentServerRpc rpc) { m_serverRpc = rpc; } }