1 /* 2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab 3 * Copyright (C) 2010-2010 - DIGITEO - Clement DAVID <clement.david@scilab.org> 4 * Copyright (C) 2011-2015 - Scilab Enterprises - Clement DAVID 5 * 6 * Copyright (C) 2012 - 2016 - Scilab Enterprises 7 * 8 * This file is hereby licensed under the terms of the GNU GPL v2.0, 9 * pursuant to article 5.3.4 of the CeCILL v.2.1. 10 * This file was originally licensed under the terms of the CeCILL v2.1, 11 * and continues to be available under such terms. 12 * For more information, see the COPYING file which you should have received 13 * along with this program. 14 * 15 */ 16 17 package org.scilab.modules.xcos.graph.swing.handler; 18 19 import java.awt.Color; 20 import java.awt.event.MouseEvent; 21 import java.util.ArrayList; 22 import java.util.List; 23 24 import org.scilab.modules.xcos.block.BasicBlock; 25 import org.scilab.modules.xcos.graph.swing.GraphComponent; 26 27 import com.mxgraph.model.mxICell; 28 import com.mxgraph.model.mxIGraphModel; 29 import com.mxgraph.swing.handler.mxCellMarker; 30 import com.mxgraph.swing.handler.mxConnectPreview; 31 import com.mxgraph.swing.handler.mxConnectionHandler; 32 import com.mxgraph.util.mxEvent; 33 import com.mxgraph.util.mxPoint; 34 import com.mxgraph.view.mxCellState; 35 import com.mxgraph.view.mxGraph; 36 37 /** 38 * Connection handler used to handle multi point links. 39 */ 40 @SuppressWarnings(value = { "serial" }) 41 public class ConnectionHandler extends mxConnectionHandler { 42 private boolean multiPointLinkStarted; 43 44 /** 45 * Default constructor. 46 * 47 * @param graphComponent 48 * the component 49 */ ConnectionHandler(GraphComponent graphComponent)50 public ConnectionHandler(GraphComponent graphComponent) { 51 super(graphComponent); 52 53 /* 54 * Same default settings as mxConnectionHandler plus get first free 55 * input port in case of a simple flow connection. 56 */ 57 58 marker = new mxCellMarker(graphComponent) { 59 // Overrides to return cell at location only if valid (so that 60 // there is no highlight for invalid cells that have no error 61 // message when the mouse is released) 62 @Override 63 protected Object getCell(MouseEvent e) { 64 Object cell = super.getCell(e); 65 66 if (isConnecting()) { 67 if (source != null) { 68 cell = getFirstValidPort(cell); 69 error = validateConnection(source.getCell(), cell); 70 71 if (error != null && error.length() == 0) { 72 cell = null; 73 74 // Enables create target inside groups 75 if (createTarget) { 76 error = null; 77 } 78 } 79 } 80 } else if (!isValidSource(cell)) { 81 cell = null; 82 } 83 84 return cell; 85 } 86 87 // Sets the highlight color according to isValidConnection 88 @Override 89 protected boolean isValidState(mxCellState state) { 90 if (isConnecting()) { 91 return error == null; 92 } else { 93 return super.isValidState(state); 94 } 95 } 96 97 // Overrides to use marker color only in highlight mode or for 98 // target selection 99 @Override 100 protected Color getMarkerColor(MouseEvent e, mxCellState state, boolean isValid) { 101 return (isHighlighting() || isConnecting()) ? super.getMarkerColor(e, state, isValid) : null; 102 } 103 104 // Overrides to use hotspot only for source selection otherwise 105 // intersects always returns true when over a cell 106 @Override 107 protected boolean intersects(mxCellState state, MouseEvent e) { 108 if (!isHighlighting() || isConnecting()) { 109 return true; 110 } 111 112 return super.intersects(state, e); 113 } 114 115 private Object getFirstValidPort(Object o) { 116 if (!(o instanceof BasicBlock)) { 117 return o; 118 } 119 final BasicBlock block = (BasicBlock) o; 120 121 for (int i = 0; i < block.getChildCount(); i++) { 122 final Object cell = block.getChildAt(i); 123 124 final String err = validateConnection(source.getCell(), cell); 125 if (err == null) { 126 return cell; 127 } 128 } 129 130 return o; 131 } 132 133 @Override 134 public void reset() { 135 if (markedState != null) { 136 this.graphComponent.getGraph().addSelectionCell(markedState.getCell()); 137 } 138 139 super.reset(); 140 } 141 }; 142 } 143 144 /** 145 * @return {@link ConnectPreview} instance 146 * @see com.mxgraph.swing.handler.mxConnectionHandler#createConnectPreview() 147 */ 148 @Override createConnectPreview()149 protected mxConnectPreview createConnectPreview() { 150 return new ConnectPreview((GraphComponent) graphComponent); 151 } 152 153 /** 154 * Enable or disable the reset handler which reset any action on graph 155 * modification. 156 * 157 * @param status 158 * the enable status 159 */ setResetEnable(boolean status)160 protected void setResetEnable(boolean status) { 161 final mxIGraphModel model = graphComponent.getGraph().getModel(); 162 163 if (status) { 164 model.addListener(mxEvent.CHANGE, resetHandler); 165 } else { 166 model.removeListener(resetHandler, mxEvent.CHANGE); 167 } 168 } 169 170 /* 171 * mxMouseAdapter specific reimplementation 172 */ 173 174 /** 175 * Handle first release and click on the empty background during connection. 176 * 177 * @param e 178 * the event 179 * @see com.mxgraph.swing.handler.mxConnectionHandler#mouseReleased(java.awt.event.MouseEvent) 180 */ 181 @Override mouseReleased(MouseEvent e)182 public void mouseReleased(MouseEvent e) { 183 final boolean isEventValid = error != null && error.isEmpty() && !e.isConsumed(); 184 final boolean hasValidState = first != null && connectPreview.isActive() && !marker.hasValidState(); 185 186 if (isEventValid && hasValidState) { 187 final mxGraph graph = graphComponent.getGraph(); 188 final double x = graph.snap(e.getX()); 189 final double y = graph.snap(e.getY()); 190 191 // we are during a link creation on an invalid area 192 mxICell cell = (mxICell) connectPreview.getPreviewState().getCell(); 193 194 // allocate points if applicable 195 List<mxPoint> points = cell.getGeometry().getPoints(); 196 if (points == null) { 197 points = new ArrayList<mxPoint>(); 198 cell.getGeometry().setPoints(points); 199 } 200 201 // scale and set the point 202 // extracted from mxConnectPreview#transformScreenPoint 203 final mxPoint tr = graph.getView().getTranslate(); 204 final double scale = graph.getView().getScale(); 205 points.add(new mxPoint(graph.snap(x / scale - tr.getX()), graph.snap(y / scale - tr.getY()))); 206 207 // update the preview and set the flag 208 connectPreview.update(e, null, x, y); 209 multiPointLinkStarted = true; 210 211 e.consume(); 212 } else { 213 if (marker.hasValidState() && connectPreview.getPreviewState() != null) { 214 final mxGraph graph = graphComponent.getGraph(); 215 final double x = graph.snap(e.getX()); 216 final double y = graph.snap(e.getY()); 217 218 // We are ending a link creation on an valid port, 219 // so sync the points coordinates with the model 220 mxICell cell = (mxICell) connectPreview.getPreviewState().getCell(); 221 cell.setGeometry(cell.getGeometry()); 222 } 223 224 multiPointLinkStarted = false; 225 super.mouseReleased(e); 226 } 227 } 228 229 /** 230 * Only chain up when the multi point link feature is disable, drag 231 * otherwise. 232 * 233 * @param e 234 * the event 235 * @see com.mxgraph.swing.handler.mxConnectionHandler#mouseMoved(java.awt.event.MouseEvent) 236 */ 237 @Override mouseMoved(MouseEvent e)238 public void mouseMoved(MouseEvent e) { 239 if (multiPointLinkStarted) { 240 mouseDragged(e); 241 } else { 242 super.mouseMoved(e); 243 } 244 } 245 246 /** 247 * Only chain up when multi point link feature is disable. 248 * 249 * This will not update the first point on multi point link creation. 250 * 251 * @param e 252 * the mouse event 253 * @param state 254 * the marker valid state 255 * @see com.mxgraph.swing.handler.mxConnectionHandler#start(java.awt.event.MouseEvent, 256 * com.mxgraph.view.mxCellState) 257 */ 258 @Override start(MouseEvent e, mxCellState state)259 public void start(MouseEvent e, mxCellState state) { 260 if (!multiPointLinkStarted) { 261 super.start(e, state); 262 } 263 } 264 } 265