View Javadoc
1 package com.bonevich.erj.diagram; 2 3 import com.bonevich.erj.model.*; 4 import com.bonevich.erj.ui.*; 5 import com.bonevich.erj.ErjConstants; 6 import com.bonevich.util.dependency.VisualDependent; 7 import com.bonevich.util.dependency.UpdateAction; 8 9 import org.tigris.gef.presentation.*; 10 import org.tigris.gef.graph.*; 11 import org.tigris.gef.base.PathConv; 12 import org.tigris.gef.base.PathConvPercent; 13 14 import java.util.Vector; 15 import javax.swing.Action; 16 import javax.swing.JMenu; 17 import java.awt.event.MouseEvent; 18 import java.awt.event.MouseListener; 19 import java.awt.*; 20 21 /*** A Fig that paints edges between ports. This version 22 * automatically routes a rectilinear edge. The routing is not very 23 * good. It avoids the source and sink nodes and no other nodes. It is 24 * basically case-analysis, and some of the cases are wrong or 25 * missing. Anyway, the user can edit the edge by dragging 26 * handles. The 0th and last handles are fixed in position so that 27 * they stay connected to ports. If the user drags a handle next to a 28 * fixed handle, a new vertex is automatically inserted. 29 * 30 * @see FigPoly */ 31 32 public class FigForeignKey extends FigEdge 33 implements MouseListener, ErjConstants 34 { 35 //////////////////////////////////////////////////////////////// 36 // Attributes 37 /*** True if the edge has been laid out automatically once. It will 38 * not be done automatically again since the user may have edited the 39 * edge and I dont want to undo that work. */ 40 private boolean _initiallyLaidOut = false; 41 private boolean _menuInitialized = false; 42 43 private Action _editFK; 44 private Action _dispose; 45 private JMenu _cardinalityMenu; 46 47 private FigText _idTextFig; 48 private PathConv _idTextPath; 49 50 private static final int BORDER = 18; 51 52 ////////////////////////////////////////////////////////// 53 // Dependency sentries 54 private VisualDependent _dep_arrowHeads = new VisualDependent( 55 new UpdateAction() 56 { 57 public void onUpdate() 58 { 59 setArrowHeads(); 60 setIdentifierText(); 61 } 62 } 63 ); 64 65 //////////////////////////////////////////////////////////////// 66 // Constructors 67 public FigForeignKey() 68 { 69 super(); 70 init(); 71 } 72 73 public FigForeignKey(Fig srcPort, Fig dstPort, FigNode srcNode, FigNode dstNode, Object edge) 74 { 75 super(srcPort, dstPort, srcNode, dstNode, edge); 76 init(); 77 } 78 79 private void init() 80 { 81 setBetweenNearestPoints(true); 82 83 _idTextFig = new FigText(1, 1, 1, 1); 84 _idTextFig.setAllowsTab(false); 85 _idTextFig.setEditable(false); 86 _idTextFig.setFilled(true); 87 _idTextFig.setLineWidth(0); 88 _idTextFig.setMultiLine(false); 89 _idTextFig.setFont(LABEL_FONT); 90 _idTextFig.setJustification(FigText.JUSTIFY_CENTER); 91 _idTextFig.setTextColor(LABEL_COLOR); 92 _idTextFig.setFillColor(null); 93 94 //FIXME: make the positioning more dynamic (shift from 95 //side-to-side as necessary, particularly for self-loops) 96 _idTextPath = new PathConvPercent(this, 50, -10); 97 98 addPathItem(_idTextFig, _idTextPath); 99 } 100 101 //////////////////////////////////////////////////////////////// 102 // FigEdge API 103 /*** Instanciate a FigRectilinear. */ 104 protected Fig makeEdgeFig() 105 { 106 FigRectiline res = new FigRectiline(Color.black); 107 res.setFixedHandles(-1); 108 res.setFilled(false); 109 res.addPropertyChangeListener(ErjFrame.getInstance().getApplication().getCurrentProject()); 110 return res; 111 } 112 113 //////////////////////////////////////////////////////////////// 114 // routing methods 115 116 /*** Find the route that the edge should follow. Basically case 117 * analysis to route around source and destination nodes. 118 * Needs-More-Work: A better algorithm would really be useful. 119 * Needs-More-Work: Sometimes the edge can get non-rectilinear. */ 120 public void computeRoute() 121 { 122 if (true) { //!_initiallyLaidOut) { 123 layoutEdge(); 124 _initiallyLaidOut = true; 125 } 126 FigRectiline p = ((FigRectiline) _fig); 127 128 Point srcPt = p.getPoints(0); 129 Point dstPt = p.getPoints(p.getNumPoints()-1); 130 131 // use the second and second-to-last points on the rectiline 132 // to set the end points 133 //FIXME: needs to test that these are actually directly under or next to fig 134 // otherwise we get non-rectilinear lines 135 //srcPt = _sourcePortFig.connectionPoint(p.getPoints(1)); 136 //dstPt = _destPortFig.connectionPoint(p.getPoints(p.getNumPoints()-2)); 137 138 p.setEndPoints(srcPt, dstPt); 139 calcBounds(); 140 } /* end computeRoute */ 141 142 /*** Internal function to actually compute the layout of the line if 143 * it has never been done on that line before. */ 144 protected void layoutEdge() 145 { 146 int npoints = 0; 147 int xpoints[] = new int[16]; 148 int ypoints[] = new int[16]; 149 Point srcCntr = _sourcePortFig.center(); 150 Point dstCntr = _destPortFig.center(); 151 152 Point srcPt = intersection(_sourcePortFig, srcCntr, dstCntr); 153 Point dstPt = intersection(_destPortFig, srcCntr, dstCntr); 154 155 int srcSector = ((FigRelation)_sourceFigNode).getSector(srcPt); 156 int dstSector = ((FigRelation)_destFigNode).getSector(dstPt); 157 158 // now start adding points 159 xpoints[npoints] = srcPt.x; ypoints[npoints++] = srcPt.y; 160 161 if (!isSelfLoop() && !srcPt.equals(dstPt)) { 162 if (srcSector == 1 || srcSector == -1) { 163 if (dstSector == 1 || dstSector == -1) { 164 int y = (dstPt.y + srcPt.y) / 2; 165 xpoints[npoints] = srcPt.x; ypoints[npoints++] = y; 166 xpoints[npoints] = dstPt.x; ypoints[npoints++] = y; 167 } else { 168 xpoints[npoints] = dstPt.x; ypoints[npoints++] = srcPt.y; 169 } 170 } else { // srcSector == 2 || srcSector == -2 171 if (dstSector == 2 || dstSector == -2) { 172 int x = (dstPt.x + srcPt.x) / 2; 173 xpoints[npoints] = x; ypoints[npoints++] = srcPt.y; 174 xpoints[npoints] = x; ypoints[npoints++] = dstPt.y; 175 } else { 176 xpoints[npoints] = srcPt.x; ypoints[npoints++] = dstPt.y; 177 } 178 } 179 } else { 180 Rectangle r = _sourcePortFig.getBounds(); 181 Rectangle rr = new Rectangle(r.x - BORDER, r.y - BORDER, r.width + BORDER * 2, r.height + BORDER * 2); 182 if (srcSector == 1 || srcSector == -1) { 183 dstPt.x = srcSector == 1 ? r.x : r.x + r.width; dstPt.y = r.y + r.height / 2; 184 dstSector = ((FigRelation)_destFigNode).getSector(dstPt); 185 Point srcRRPt = routingRectPoint(srcPt,rr,srcSector); 186 Point dstRRPt = routingRectPoint(dstPt,rr,dstSector); 187 xpoints[npoints] = srcRRPt.x; ypoints[npoints++] = srcRRPt.y; 188 xpoints[npoints] = dstRRPt.x; ypoints[npoints++] = srcRRPt.y; 189 xpoints[npoints] = dstRRPt.x; ypoints[npoints++] = dstRRPt.y; 190 } else { // srcSector == 2 || srcSector == -2 191 dstPt.x = r.x + r.width / 2; dstPt.y = srcSector == 2 ? r.y : r.y + r.height; 192 dstSector = ((FigRelation)_destFigNode).getSector(dstPt); 193 Point srcRRPt = routingRectPoint(srcPt,rr,srcSector); 194 Point dstRRPt = routingRectPoint(dstPt,rr,dstSector); 195 xpoints[npoints] = srcRRPt.x; ypoints[npoints++] = srcRRPt.y; 196 xpoints[npoints] = srcRRPt.x; ypoints[npoints++] = dstRRPt.y; 197 xpoints[npoints] = dstRRPt.x; ypoints[npoints++] = dstRRPt.y; 198 } 199 } 200 201 xpoints[npoints] = dstPt.x; ypoints[npoints++] = dstPt.y; 202 203 Polygon routePoly = new Polygon(xpoints, ypoints, npoints); 204 ((FigRectiline)_fig).setPolygon(routePoly); 205 } 206 207 /*** Find the intersection of the Fig and the line connecting two points. */ 208 private Point intersection(Fig fig, Point p1, Point p2) 209 { 210 Rectangle r = fig.getBounds(); 211 int x0 = r.x, y0 = r.y; 212 int x1 = r.x + r.width, y1 = r.y + r.height; 213 214 //y = ax + b 215 double a; 216 if (p2.x != p1.x) { 217 a = (double)(p2.y - p1.y) / (double)(p2.x - p1.x); 218 } else { 219 a = Double.MAX_VALUE; 220 } 221 if (a == 0) a = Double.MIN_VALUE; 222 double b = (double)(p1.y - (p1.x * a)); 223 224 225 if (x0 <= Math.max(p1.x,p2.x) && x0 >= Math.min(p1.x,p2.x)) 226 { 227 int y = (int)(a * x0 + b); 228 if (y <= Math.max(y0,y1) && y >= Math.min(y0,y1)) return new Point(x0,y); 229 } 230 if (x1 <= Math.max(p1.x,p2.x) && x1 >= Math.min(p1.x,p2.x)) 231 { 232 int y = (int)(a * x1 + b); 233 if (y <= Math.max(y0,y1) && y >= Math.min(y0,y1)) return new Point(x1,y); 234 } 235 if (y0 <= Math.max(p1.y,p2.y) && y0 >= Math.min(p1.y,p2.y)) 236 { 237 int x = (int)((y0 - b) / a); 238 if (x <= Math.max(x0,x1) && x >= Math.min(x0,x1)) return new Point(x,y0); 239 } 240 if (y1 <= Math.max(p1.y,p2.y) && y1 >= Math.min(p1.y,p2.y)) 241 { 242 int x = (int)((y1 - b) / a); 243 if (x <= Math.max(x0,x1) && x >= Math.min(x0,x1)) return new Point(x,y1); 244 } 245 return new Point(p1.x, p1.y < y0 ? y0 : y1); 246 } 247 248 /*** Reply a point on the given routing rect that is "straight out" 249 * from the connection point in the proper direction. */ 250 protected Point routingRectPoint(Point p, Rectangle r, int sector) 251 { 252 switch (sector) 253 { 254 case -1: return new Point(p.x, r.y); 255 case 2: return new Point(r.x, p.y); 256 case 1: return new Point(p.x, r.y + r.height); 257 case -2: return new Point(r.x + r.width, p.y); 258 default: System.out.println("error, undefined sector!"); 259 return p; 260 } 261 } 262 263 public void setSelfLoop(boolean self) { ((FigRectiline)_fig).setSelfLoop(self); } 264 public boolean isSelfLoop() { return ((FigRectiline)_fig).isSelfLoop(); } 265 266 public Vector getPopUpActions(MouseEvent e) 267 { 268 Vector actions = super.getPopUpActions(e); 269 if (!_menuInitialized) 270 { 271 ForeignKey fk = (ForeignKey) getOwner(); 272 _editFK = new CmdEditForeignKey(fk); 273 _dispose = new CmdDeleteForeignKey(fk); 274 275 _cardinalityMenu = new JMenu("Cardinality",false); 276 _cardinalityMenu.add(CmdAlterCardinality.ZeroToZeroInstance); 277 _cardinalityMenu.add(CmdAlterCardinality.OneToZeroInstance); 278 _cardinalityMenu.add(CmdAlterCardinality.OneToOneInstance); 279 _cardinalityMenu.add(CmdAlterCardinality.ZeroToManyInstance); 280 _cardinalityMenu.add(CmdAlterCardinality.OneToManyInstance); 281 _cardinalityMenu.add(CmdAlterCardinality.ZeroToManyMandatoryInstance); 282 _cardinalityMenu.add(CmdAlterCardinality.OneToManyMandatoryInstance); 283 _cardinalityMenu.add(CmdAlterCardinality.ManyToManyInstance); 284 285 _menuInitialized = true; 286 } 287 actions.addElement(_editFK); 288 actions.addElement(_cardinalityMenu); 289 actions.addElement(_dispose); 290 return actions; 291 } 292 293 private void setArrowHeads() 294 { 295 ForeignKey fk = (ForeignKey) getOwner(); 296 297 if (fk.getCardinality() == ForeignKey.ONE_TO_ZERO || 298 fk.getCardinality() == ForeignKey.ONE_TO_ONE || 299 fk.getCardinality() == ForeignKey.ONE_TO_MANY || 300 fk.getCardinality() == ForeignKey.ONE_TO_MANY_MANDATORY) 301 { 302 setDestArrowHead(ArrowHeadBar.getInstance()); 303 } 304 else if (fk.getCardinality() == ForeignKey.MANY_TO_MANY) 305 { 306 setDestArrowHead(ArrowHeadLesser.getInstance()); 307 } 308 else 309 { 310 setDestArrowHead(ArrowHeadNone.TheInstance); 311 } 312 313 if (fk.getCardinality() == ForeignKey.ZERO_TO_MANY || 314 fk.getCardinality() == ForeignKey.ONE_TO_MANY || 315 fk.getCardinality() == ForeignKey.MANY_TO_MANY) 316 { 317 setSourceArrowHead(ArrowHeadLesser.getInstance()); 318 } 319 else if (fk.getCardinality() == ForeignKey.ZERO_TO_MANY_MANDATORY || 320 fk.getCardinality() == ForeignKey.ONE_TO_MANY_MANDATORY) 321 { 322 setSourceArrowHead(ArrowHeadBarAndLesser.getInstance()); 323 } 324 else if (fk.getCardinality() == ForeignKey.ONE_TO_ONE) 325 { 326 setSourceArrowHead(ArrowHeadBar.getInstance()); 327 } 328 else 329 { 330 setSourceArrowHead(ArrowHeadNone.TheInstance); 331 } 332 startTrans(); 333 } 334 335 private void setIdentifierText() 336 { 337 ForeignKey fk = (ForeignKey) getOwner(); 338 _idTextFig.setText(fk.getIdentifier()); 339 startTrans(); 340 } 341 342 ////////////////////////////////////////////////////////// 343 // Event handling 344 /*** Do nothing when mouse enters. */ 345 public void mouseEntered(MouseEvent e) { } 346 347 /*** Do nothing when mouse exits. */ 348 public void mouseExited(MouseEvent e) { } 349 350 /*** Do nothing when mouse is pressed in FigNode. */ 351 public void mousePressed(MouseEvent e) { } 352 353 /*** Do nothing when mouse is released in FigNode. */ 354 public void mouseReleased(MouseEvent e) { } 355 356 /*** Open the default editor for the foreign key. */ 357 public void mouseClicked(MouseEvent e) 358 { 359 if (e.isConsumed()) return; 360 if (e.getClickCount() >= 2) 361 { 362 ForeignKey edge = (ForeignKey) getOwner(); 363 CmdEditForeignKey editor = new CmdEditForeignKey(edge); 364 editor.invoke(); 365 e.consume(); 366 } 367 } 368 369 } /* end class FigForeignKey */

This page was automatically generated by Maven