1   /****************************************************************************
2    **
3    ** This file is part of yFiles-2.6. 
4    ** 
5    ** yWorks proprietary/confidential. Use is subject to license terms.
6    **
7    ** Redistribution of this file or of an unauthorized byte-code version
8    ** of this file is strictly forbidden.
9    **
10   ** Copyright (c) 2000-2008 by yWorks GmbH, Vor dem Kreuzberg 28, 
11   ** 72070 Tuebingen, Germany. All rights reserved.
12   **
13   ***************************************************************************/
14  package demo.view.ports;
15  
16  import java.awt.Color;
17  import java.awt.Graphics2D;
18  import java.awt.geom.Ellipse2D;
19  import java.awt.geom.Point2D;
20  import java.io.IOException;
21  import java.io.ObjectInputStream;
22  import java.io.ObjectOutputStream;
23  
24  import y.base.ListCell;
25  import y.base.YCursor;
26  import y.base.YList;
27  import y.geom.YPoint;
28  
29  import y.view.EdgeRealizer;
30  import y.view.Graph2D;
31  import y.view.ImageNodeRealizer;
32  import y.view.NodeRealizer;
33  import y.view.Port;
34  
35  /**
36   * Modified version of ImageNodeRealizer, that maintains a list of possible
37   * port coordinates, suppresses resizing of the node and modifies the behavior
38   * 
39   */
40  public class FixedPortsNodeRealizer extends ImageNodeRealizer
41  {
42    /** Holds value of property paintingPorts. */
43    private boolean paintingPorts = true;
44    
45    /** Holds value of property portCandidates. */
46    private YList portCandidates;
47    
48    /** Creates a new instance of FixedPortsNodeRealizer */
49    public FixedPortsNodeRealizer()
50    {
51      init();
52    }
53  
54    /** Creates a new instance of FixedPortsNodeRealizer */
55    public FixedPortsNodeRealizer(NodeRealizer arg)
56    {
57      super(arg);
58      if (arg instanceof FixedPortsNodeRealizer){
59        FixedPortsNodeRealizer fpnr = (FixedPortsNodeRealizer)arg;
60        this.portCandidates = 
61          new YList(fpnr.portCandidates);
62        this.paintingPorts = fpnr.paintingPorts;
63      } 
64      init();
65    }
66    
67    /**
68     * assures the list of port candidates is non null and not empty
69     */
70    private void init(){
71      if (portCandidates == null){
72        portCandidates = new YList();
73        portCandidates.add(new Port(0,0));
74      }
75    }
76    
77    /** Getter for property portCandidates.
78     * @return Value of property portCandidates.
79     *
80     */
81    public YList getPortCandidates()
82    {
83      return this.portCandidates;
84    }
85    
86    /**
87     * overwritten in order to make MovePortMode use the port candidates
88     */
89    public YList getPortCandidates(double grid){
90      YList candidates = new YList();
91      for (YCursor c = getPortCandidates().cursor(); c.ok(); c.next()){
92        Port p = (Port) c.current();
93        candidates.add(new YPoint(p.getX(this), p.getY(this)));
94      }
95      return candidates;
96    }
97    
98    /**
99     * this method can be modified in order to present a subset of port candidates
100    * depending on the edge to be created.
101    * @param graph the graph that contains everything
102    * @param edge the edge which has been created
103    * @param source a flag indicating whether to create a list of possible source ports
104    * respectively target ports
105    */
106   public YList createCandidateList(Graph2D graph, EdgeRealizer edge, boolean source){
107     return getPortCandidates();
108   }
109   
110   /**
111    * This method chooses from a list of given ports for an edge
112    * a suitable port, given an initial placement.
113    * @param edge the affected edge
114    * @param source whether we look at the source node
115    * @param x the initial x offset
116    * @param y the initial y offset
117    */  
118   public Port snapCandidate(Graph2D graph, EdgeRealizer edge, boolean source, double x, double y){
119     
120     YList ports = createCandidateList(graph, edge, source);
121     
122     if (ports == null || ports.size() < 1) return null; // do nothing
123 
124     // find the closest port with regards to the getDistance function
125     Port closest = (Port) ports.first();
126     double dist = getDistance(x,y,closest);
127     
128     for (YCursor cursor = ports.cursor(); cursor.ok(); cursor.next()){
129       Port p = (Port) cursor.current();
130       double d2 = getDistance(x,y, p);
131       if (d2 < dist){
132         dist = d2;
133         closest = p;
134       }
135     }
136     return new Port(closest);
137   }
138   
139   /**
140    * This method calculates a metric for ports and points
141    * @param x the initial x offset
142    * @param y the initial y offset
143    * @param port the port
144    * @return the distance between the point (x,y) and the port
145    */  
146   public static double getDistance(double x, double y, Port port){
147     return Math.sqrt((x-port.getOffsetX())*(x-port.getOffsetX()) 
148       + (y-port.getOffsetY()) * (y-port.getOffsetY()));
149   }
150   
151   /**
152    * important deep copy method
153    */
154   public NodeRealizer createCopy(NodeRealizer arg){
155     return new FixedPortsNodeRealizer(arg);
156   }
157   
158   /**
159    * suppress resizing of node by not offering any hotspots (and hits)
160    */
161   public byte hotSpotHit(double x, double y){
162     return HOTSPOT_NONE;
163   }
164   
165   private static final Ellipse2D ellipse = new Ellipse2D.Double();
166   
167   public void paintNode(Graphics2D g2d){
168     super.paintNode(g2d);
169     if (isSelected()){
170       g2d.setColor(getHotSpotColor());
171       g2d.draw(getBoundingBox());
172     }
173     if (paintingPorts){
174       g2d.setColor(getHotSpotColor());
175       for (ListCell cell = getPortCandidates().firstCell(); cell != null; cell = cell.succ()){
176         Port p = (Port) cell.getInfo();
177         ellipse.setFrame(p.getX(this)-3.0,p.getY(this)-3.0,6.0,6.0);
178         g2d.setColor(Color.red);
179         g2d.fill(ellipse);
180         g2d.setColor(Color.black);
181         g2d.draw(ellipse);
182       }
183     }
184   }
185   
186   /** 
187    * overwritten to do nothing, since this realizer does not support resizing
188    */
189   public void paintHotSpots(Graphics2D g2d){
190     return;
191   }
192   
193   /** Getter for property paintingPorts.
194    * @return Value of property paintingPorts.
195    *
196    */
197   public boolean isPaintingPorts()
198   {
199     return this.paintingPorts;
200   }
201   
202   /** Setter for property paintingPorts.
203    * @param paintingPorts New value of property paintingPorts.
204    *
205    */
206   public void setPaintingPorts(boolean paintingPorts)
207   {
208     this.paintingPorts = paintingPorts;
209   }
210   
211   /**
212    * overwritten to suppress the clipping of edges on the boundary of the node
213    */
214   public boolean findIntersection(double ix, double iy, double ox, double oy, Point2D res){
215     res.setLocation(ix, iy);
216     return true;
217   }
218 
219   /**
220    * serializes this node to an ObjectOutputStream by appending the port 
221    * candidates and additional properties of this realizer to the serialization
222    * data of the super class.
223    */
224   public void write(ObjectOutputStream oos) throws IOException{
225     super.write(oos);
226     YList ports = getPortCandidates();
227     oos.writeInt(ports.size());
228     for (YCursor yc = ports.cursor(); yc.ok(); yc.next()){
229       Port p = (Port)yc.current();
230       p.write(oos);
231     }
232     oos.writeBoolean(isPaintingPorts());
233   }
234 
235   /**
236    * deserializes this node from an ObjectInputStream by reading the port
237    * candidates and additional properties of this realizer
238    */
239   public void read(ObjectInputStream ois) throws IOException, ClassNotFoundException{
240     super.read(ois);
241     YList ports = getPortCandidates();
242     ports.clear();
243     int count = ois.readInt();
244     while (count-- > 0){
245       Port p = new Port();
246       p.read(ois);
247       ports.add(p);
248     }
249     setPaintingPorts(ois.readBoolean());
250   }
251 }
252