View Javadoc

1   /*
2   * The contents of this file are subject to the BT "ZEUS" Open Source 
3   * Licence (L77741), Version 1.0 (the "Licence"); you may not use this file 
4   * except in compliance with the Licence. You may obtain a copy of the Licence
5   * from $ZEUS_INSTALL/licence.html or alternatively from
6   * http://www.labs.bt.com/projects/agents/zeus/licence.htm
7   * 
8   * Except as stated in Clause 7 of the Licence, software distributed under the
9   * Licence is distributed WITHOUT WARRANTY OF ANY KIND, either express or 
10  * implied. See the Licence for the specific language governing rights and 
11  * limitations under the Licence.
12  * 
13  * The Original Code is within the package zeus.*.
14  * The Initial Developer of the Original Code is British Telecommunications
15  * public limited company, whose registered office is at 81 Newgate Street, 
16  * London, EC1A 7AJ, England. Portions created by British Telecommunications 
17  * public limited company are Copyright 1996-9. All Rights Reserved.
18  * 
19  * THIS NOTICE MUST BE INCLUDED ON ANY COPY OF THIS FILE
20  */
21  
22  
23  
24  package zeus.gui.graph;
25  
26  import java.awt.*;
27  import java.awt.event.*;
28  import java.util.*;
29  import javax.swing.*;
30  import javax.swing.event.*;
31  
32  import zeus.util.*;
33  
34  public class Graph extends MovePanel
35               implements GraphIconListener,
36                          GraphModelListener,
37                          GraphNodeEditorListener {
38  
39    public static final int VERTICAL_PARENT_CHILD   = 0;
40    public static final int VERTICAL_CHILD_PARENT   = 1;
41    public static final int HORIZONTAL_PARENT_CHILD = 2;
42    public static final int HORIZONTAL_CHILD_PARENT = 3;
43    public static final int CENTRED                 = 4;
44    public static final int CIRCLES                 = 5;
45  
46    protected static final int ORG_DX      = 20;
47    protected static final int ORG_DY      = 20;
48  
49    protected static final double HEIGHT_FACTOR = 2.0;
50  
51    protected static final int BETA	 = 30;
52    protected static final int GAMMA	 = 20;
53  
54    protected static final int BOTTOM_EDGE = 1;
55    protected static final int TOP_EDGE	 = 2;
56    protected static final int LEFT_EDGE	 = 3;
57    protected static final int RIGHT_EDGE	 = 4;
58  
59    protected Hashtable         ViewList = new Hashtable();
60    protected GraphModel        model;
61    protected GraphNodeRenderer nodeRenderer = null;
62    protected GraphNodeEditor   nodeEditor = null;
63    protected Component         nodeEditorComponent = null;
64    protected JLayeredPane      pane = null;
65    protected boolean           isNodeEditable;
66    protected boolean           isLinkEditable;
67    protected int               viewMode;
68  
69    private Point     startPoint = null;
70    private Point     lastPoint = null;
71    private Point     stretchedPoint= null;
72    private GraphIcon sourceIcon = null;
73    private GraphIcon destIcon = null;
74  
75    public Graph(int viewMode, GraphModel model, boolean isNodeEditable,
76                 boolean isLinkEditable) {
77        super(2000,2000);
78        this.setLayout(new BulletinLayout());
79        this.model = model;
80        this.isNodeEditable = isNodeEditable;
81        this.isLinkEditable = isLinkEditable;
82        this.viewMode = viewMode;
83        model.addGraphModelListener(this);
84    }
85    
86    
87    public Graph(GraphModel model, boolean isNodeEditable,
88                 boolean isLinkEditable) {
89       this(HORIZONTAL_CHILD_PARENT,model,isNodeEditable,isLinkEditable);
90    }
91    
92    
93    public Graph(GraphModel model) {
94      this(HORIZONTAL_CHILD_PARENT,model,true,true);
95    }
96    
97    
98  
99  
100   public void setModel(GraphModel model) {
101     this.model.removeGraphModelListener(this);
102     this.model = model;
103     this.model.addGraphModelListener(this);
104     reset();
105   }
106 
107 
108   public GraphModel getModel() {  return model; }
109 
110   public void setViewMode(int mode) {
111      switch( mode ) {
112         case HORIZONTAL_PARENT_CHILD:
113         case HORIZONTAL_CHILD_PARENT:
114         case VERTICAL_PARENT_CHILD:
115         case VERTICAL_CHILD_PARENT:
116         case CIRCLES:
117         case CENTRED:
118              viewMode = mode;
119              break;
120         default:
121              Core.USER_ERROR("Attempt to set an illegal view mode: " + mode);
122              break;
123      }
124   }
125 
126   public GraphNodeRenderer getNodeRenderer() {
127      return nodeRenderer;
128   }
129   
130   
131   public void setNodeRenderer(GraphNodeRenderer nodeRenderer) {
132      this.nodeRenderer = nodeRenderer;
133   }
134   
135   
136   public GraphNodeEditor getNodeEditor() {
137      return nodeEditor;
138   }
139   
140   
141   public void setNodeEditor(GraphNodeEditor nodeEditor) {
142      if ( this.nodeEditor != null )
143         this.nodeEditor.removeGraphNodeEditorListener(this);
144 
145      this.nodeEditor = nodeEditor;
146 
147      if ( this.nodeEditor != null )
148         this.nodeEditor.addGraphNodeEditorListener(this);
149   }
150   
151 
152   public void addNotify() {
153      super.addNotify();
154      reset();
155   }
156   
157 
158   public boolean isVisible(GraphNode node) {
159      GraphIcon icon = (GraphIcon)ViewList.get(node);
160      return icon != null && icon.isVisible();
161   }
162 
163 
164   public Rectangle getBounds(GraphNode node) {
165      GraphIcon icon = (GraphIcon)ViewList.get(node);
166      if ( icon != null )
167         return icon.getBounds();
168      else {
169         Core.USER_ERROR("getBounds called on a null node");
170         return new Rectangle(0,0,0,0);
171      }
172   }
173 
174   protected void reset() {
175     // first remove graph from icon listenerList
176     GraphIcon icon;
177     Enumeration enum = ViewList.elements();
178     while( enum.hasMoreElements() ) {
179       icon = (GraphIcon)enum.nextElement();
180       icon.removeGraphIconListener(this);
181     }
182     // next clear ViewList
183     ViewList.clear();
184     this.removeAll();
185     // recreate graph
186     enum = model.nodes();
187     while( enum.hasMoreElements() )
188        addNode((GraphNode)enum.nextElement());
189     recompute();
190   }
191 
192 
193   protected void addNode(GraphNode node) {
194     GraphIcon icon;
195     Rectangle rect = new Rectangle(0,0,0,0);
196     Enumeration enum = ViewList.elements();
197     while( enum.hasMoreElements() ) {
198       icon = (GraphIcon)enum.nextElement();
199       rect = rect.union(icon.getBounds());
200     }
201     icon = new GraphIcon(node,this);
202     ViewList.put(node,icon);
203     this.add(icon);
204     icon.setLocation(new Point(rect.x+rect.width,rect.y));
205     icon.addGraphIconListener(this);
206   }
207   
208   
209   protected void removeNode(GraphNode node) {
210     GraphIcon icon = (GraphIcon)ViewList.remove(node);
211     if ( icon != null ) {
212        this.remove(icon);
213        icon.removeGraphIconListener(this);
214     }
215   }
216   protected void updateNode(GraphNode node) {
217     GraphIcon icon = (GraphIcon)ViewList.get(node);
218     if ( icon != null )
219        icon.reset();
220   }
221 
222   public void hide() {
223     GraphIcon icon;
224     Enumeration enum = ViewList.elements();
225     while( enum.hasMoreElements() ) {
226       icon = (GraphIcon)enum.nextElement();
227       if ( icon.isSelected() ) icon.setVisible(false);
228     }
229     repaint();
230   }
231 
232   public void show() {
233     GraphIcon icon;
234     Enumeration enum = ViewList.elements();
235     while( enum.hasMoreElements() ) {
236       icon = (GraphIcon)enum.nextElement();
237       if ( !icon.isVisible() ) icon.setVisible(true);
238     }
239     repaint();
240   }
241 
242   public void collapse() {
243     GraphIcon icon;
244     Enumeration enum = ViewList.elements();
245     while( enum.hasMoreElements() ) {
246       icon = (GraphIcon)enum.nextElement();
247       if ( icon.isSelected() )
248          collapseNode(icon.getGraphNode(), new Vector());
249     }
250     repaint();
251   }
252 
253   public void expand() {
254     GraphIcon icon;
255     Enumeration enum = ViewList.elements();
256     while( enum.hasMoreElements() ) {
257       icon = (GraphIcon)enum.nextElement();
258       if ( icon.isSelected() )
259          expandNode(icon.getGraphNode(), new Vector());
260     }
261     repaint();
262   }
263 
264   public GraphNode[] getSelectedNodes() {
265     GraphIcon icon;
266     Vector items = new Vector();
267     Enumeration enum = ViewList.elements();
268     while( enum.hasMoreElements() ) {
269       icon = (GraphIcon)enum.nextElement();
270       if ( icon.isSelected() )
271          items.addElement(icon);
272     }
273 
274     GraphNode[] out = new GraphNode[items.size()];
275     for(int i = 0; i < out.length; i++ )
276        out[i] = ((GraphIcon)items.elementAt(i)).getGraphNode();
277     return out;
278   }
279 
280   protected void collapseNode(GraphNode node, Vector doneList) {
281      GraphNode[] children = node.getChildren();
282      doneList.addElement(node);
283      for(int i = 0; i < children.length; i++ ) {
284         GraphIcon icon = (GraphIcon)ViewList.get(children[i]);
285         if ( icon != null ) {
286            icon.setVisible(false);
287            icon.setSelected(false);
288            if ( !doneList.contains(children[i]) )
289               collapseNode(children[i],doneList);
290         }
291      }
292   }
293 
294   protected void expandNode(GraphNode node, Vector doneList) {
295      GraphNode[] children = node.getChildren();
296      doneList.addElement(node);
297      for(int i = 0; i < children.length; i++ ) {
298         GraphIcon icon = (GraphIcon)ViewList.get(children[i]);
299         if ( icon != null ) {
300            icon.setVisible(true);
301            if ( !doneList.contains(children[i]) )
302               expandNode(children[i],doneList);
303         }
304      }
305   }
306 
307   public void select() {
308     Vector items;
309     GraphIcon icon;
310     if ( (items = this.boundedItems()) != null ) {
311       boolean all_selected = true;
312       for( int i = 0; i < items.size(); i++ ) {
313 	icon = (GraphIcon)items.elementAt(i);
314 	all_selected = all_selected && icon.isSelected();
315 	if ( !all_selected) break;
316       }
317 
318       for( int i = 0; i < items.size(); i++ ) {
319 	icon = (GraphIcon)items.elementAt(i);
320 	if ( all_selected )
321 	  icon.setSelected(false);
322 	else
323 	  icon.setSelected(true);
324       }
325     }
326   }
327 
328   public void selectAll() {
329     GraphIcon icon;
330     boolean all_selected = true;
331     Enumeration enum = ViewList.elements();
332     while( enum.hasMoreElements() ) {
333       icon = (GraphIcon)enum.nextElement();
334       all_selected = all_selected && icon.isSelected();
335       if ( !all_selected) break;
336     }
337 
338     enum = ViewList.elements();
339     while( enum.hasMoreElements() ) {
340       icon = (GraphIcon)enum.nextElement();
341       if ( all_selected )
342 	icon.setSelected(false);
343       else
344 	icon.setSelected(true);
345     }
346   }
347 
348   public void recompute() {
349     Point p = new Point(0,0);
350     GraphNode node;
351     GraphIcon icon;
352     Enumeration enum;
353 
354     Rectangle bb;
355     Point pt;
356     Rectangle rect;
357 
358     Vector doneList = new Vector();
359     switch( viewMode ) {
360        case VERTICAL_PARENT_CHILD:
361        case VERTICAL_CHILD_PARENT:
362             p.x = ORG_DX; p.y = ORG_DY;
363             enum = ViewList.elements();
364             while( enum.hasMoreElements() ) {
365                icon = (GraphIcon)enum.nextElement();
366                node = icon.getGraphNode();
367                if ( icon.isVisible() && node.getNodeType() == GraphNode.PARENT )
368   	          computeVerticalPosition(node,doneList,p);
369             }
370 
371             enum = ViewList.elements();
372             while( enum.hasMoreElements() ) {
373                icon = (GraphIcon)enum.nextElement();
374                node = icon.getGraphNode();
375                if ( icon.isVisible() && node.getNodeType() == GraphNode.CHILD &&
376 	            !doneList.contains(node) )
377 	          computeVerticalPosition(node,doneList,p);
378             }
379 
380             if ( viewMode == VERTICAL_PARENT_CHILD )
381                break;
382 
383             rect = getDrawArea(doneList);
384             int ym = rect.y + rect.height/2;
385             enum = doneList.elements();
386             while( enum.hasMoreElements() ) {
387                node = (GraphNode)enum.nextElement();
388                icon = (GraphIcon)ViewList.get(node);
389                bb = icon.getBounds();
390                pt = new Point(bb.x,bb.y+bb.height);
391                // transform 
392                pt = new Point(pt.x, 2*ym - pt.y);
393                // set new Location
394                icon.setLocation(pt);
395             }
396             break;
397 
398        case HORIZONTAL_PARENT_CHILD:
399        case HORIZONTAL_CHILD_PARENT:
400             enum = ViewList.elements();
401             while( enum.hasMoreElements() ) {
402                icon = (GraphIcon)enum.nextElement();
403                node = icon.getGraphNode();
404                if ( icon.isVisible() && node.getNodeType() == GraphNode.PARENT )
405   	          computeHorizontalPosition(node,doneList,p);
406             }
407 
408             enum = ViewList.elements();
409             while( enum.hasMoreElements() ) {
410                icon = (GraphIcon)enum.nextElement();
411                node = icon.getGraphNode();
412                if ( icon.isVisible() && node.getNodeType() == GraphNode.CHILD &&
413 	            !doneList.contains(node) )
414 	          computeHorizontalPosition(node,doneList,p);
415             }
416 
417             rect = getDrawArea(doneList);
418 
419             enum = doneList.elements();
420             while( enum.hasMoreElements() ) {
421                node = (GraphNode)enum.nextElement();
422                icon = (GraphIcon)ViewList.get(node);
423                pt = icon.getLocation();
424                icon.setLocation(pt.x-rect.x+ORG_DX,pt.y-rect.y+ORG_DY);
425             }
426 
427             if ( viewMode == HORIZONTAL_CHILD_PARENT )
428                break;
429 
430             rect = getDrawArea(doneList);
431             int xm = rect.x + rect.width/2;
432             enum = doneList.elements();
433             while( enum.hasMoreElements() ) {
434                node = (GraphNode)enum.nextElement();
435                icon = (GraphIcon)ViewList.get(node);
436                bb = icon.getBounds();
437                pt = new Point(bb.x + bb.width, bb.y);
438                // transform 
439                pt = new Point(2*xm - pt.x + ORG_DX, pt.y - ORG_DY);
440                icon.setLocation(pt);
441             }
442             break;
443 
444 
445        case CENTRED:
446             GraphNode[] nodes = getSelectedNodes();
447             node = null;
448             boolean found = false;
449             if ( nodes.length > 0 ) {
450                for(int i = 0; !found && i < nodes.length; i++ ) {
451                   node = nodes[i];
452                   icon = (GraphIcon)ViewList.get(node);
453                   found = icon.isVisible();
454                }
455             }
456 
457             if ( !found && !ViewList.isEmpty() ) {
458                enum = ViewList.keys();
459                while( !found && enum.hasMoreElements() ) {
460                   node = (GraphNode)enum.nextElement();
461                   icon = (GraphIcon)ViewList.get(node);
462                   found = icon.isVisible();
463                }
464             }
465             if ( !found ) break;
466 
467             GraphNode middleNode = node;
468 
469             enum = ViewList.keys();
470             while( enum.hasMoreElements() ) {
471                node = (GraphNode)enum.nextElement();
472                icon = (GraphIcon)ViewList.get(node);
473                if ( icon.isVisible() )
474                   doneList.addElement(node);
475             }
476             centerView(middleNode,doneList);
477             break;
478 
479        case CIRCLES:
480             p.x = ORG_DX; p.y = ORG_DY;
481             enum = ViewList.elements();
482             while( enum.hasMoreElements() ) {
483                icon = (GraphIcon)enum.nextElement();
484                node = icon.getGraphNode();
485                if ( icon.isVisible() )
486   	          computeCircularPosition(node,doneList,p);
487             }
488             break;
489     }
490     redraw();
491   }
492 
493   protected Rectangle getDrawArea(Vector doneList) {
494     Rectangle rect = new Rectangle(0,0,0,0);
495     Enumeration enum = ViewList.elements();
496     GraphIcon icon;
497     GraphNode node;
498     while( enum.hasMoreElements() ) {
499       icon = (GraphIcon)enum.nextElement();
500       node = icon.getGraphNode();
501       if ( doneList.contains(node) )
502          rect = rect.union(icon.getBounds());
503     }
504     return rect;
505   }
506 
507   public void redraw() {
508     invalidate();
509     repaint();
510   }
511 
512   public void paintComponent(Graphics g){
513     Dimension d = this.getSize();
514     super.paintComponent(g);
515     drawLinks(g);
516   }
517 
518   protected void drawLinks(Graphics graphics) {
519     GraphNode node;
520     GraphIcon icon;
521 
522     Enumeration enum = ViewList.elements();
523     while( enum.hasMoreElements() ) {
524       icon = (GraphIcon)enum.nextElement();
525       node = icon.getGraphNode();
526       if ( icon.isVisible() )
527          drawLinks(node,graphics);
528     }
529     revalidate();
530   }
531 
532   protected void getRelations(Vector doList, Vector doneList) {
533     Vector items;
534     GraphNode node;
535 
536     for(int i = 0; i < doList.size(); i++ ) {
537        node = (GraphNode)doList.elementAt(i);
538        if ( !doneList.contains(node) ) {
539           doneList.addElement(node);
540           items = model.getViewRelations(node);
541           items = Misc.difference(items,doneList);
542           getRelations(items,doneList);
543        }
544     }
545   }
546 
547   protected boolean computeCircularPosition(GraphNode self, Vector doneList,
548 					    Point p) {
549 
550     if ( doneList.contains(self) ) return false;
551 
552     GraphIcon icon;
553     GraphNode node;
554     Dimension size;
555     int Wm, Hm, Wt, Ht, R;
556     double theta, phi;
557     int xx, yy;
558     Point pa = new Point(0,0);
559 
560     Vector aList = new Vector();
561     aList.addElement(self);
562     getRelations(model.getViewRelations(self),aList);
563 
564     // remove invisible items from aList;
565     // and simultaneously add all items in aList to the doneList
566     for(int i = 0; i < aList.size(); i++ ) {
567        node = (GraphNode)aList.elementAt(i);
568        icon = (GraphIcon)ViewList.get(node);
569        doneList.addElement(node);
570        if ( !icon.isVisible() )
571           aList.removeElementAt(i--);
572     }
573 
574     if ( aList.size() <= 2 ) {
575        for(int i = 0; i < aList.size(); i++ ) {
576 	  node = (GraphNode)aList.elementAt(i);
577           icon = (GraphIcon)ViewList.get(node);
578 	  size = icon.getSize();
579 	  pa.x = p.x+(BETA+size.width)/2;
580 	  pa.y = p.y+(3*GAMMA+size.height)/2;
581 	  icon.setLocation(new Point(pa.x - size.width/2, pa.y - size.height/2));
582 	  p.x += size.width+BETA;
583        }
584        return true;
585     }
586 
587     Dimension[] wh = new Dimension[aList.size()];
588     Wm = Hm = 0;
589     for(int i = 0; i < aList.size(); i++ ) {
590        node = (GraphNode)aList.elementAt(i);
591        icon = (GraphIcon)ViewList.get(node);
592        wh[i] = icon.getSize();
593        Wm = Math.max(Wm,wh[i].width);
594        Hm = Math.max(Hm,wh[i].height);
595     }
596     R = (int)(HEIGHT_FACTOR*Hm/Math.tan(Math.PI/aList.size()));
597     Wt = (2*Wm+BETA+2*R);
598     Ht = (2*Hm+GAMMA+2*R);
599     xx = p.x + Wt/2;
600     yy = p.y + Ht/2;
601 
602     p.x += Wt;
603 
604     theta = (2*Math.PI/aList.size());
605     for(int i = 0; i < aList.size(); i++ ) {
606        phi = i*theta;
607        pa.x = (int)(xx + R*Math.cos(phi)) - wh[i].width/2;
608        pa.y = (int)(yy + R*Math.sin(phi)) - wh[i].height/2;
609        node = (GraphNode)aList.elementAt(i);
610        icon = (GraphIcon)ViewList.get(node);
611        icon.setLocation(pa);
612     }
613     return true;
614   }
615 
616   protected boolean centerView(GraphNode self, Vector aList) {
617     /***
618        Centers everyone about self.
619        Only visible nodes are passed to this method.
620     */
621 
622     GraphIcon icon = (GraphIcon)ViewList.get(self);
623     if ( icon == null )
624        return false;
625 
626     Dimension ps;
627     if ( (getParent()).getClass() == JViewport.class ) {
628        // If the panel is in a scrollpane
629        JViewport viewport = (JViewport)getParent();
630        viewport.setViewPosition(new Point(0,0));
631        ps = viewport.getSize();
632     }
633     else {
634        // The panel is not in a scrollpane
635        ps = getSize();
636     }
637 
638     Dimension ms = icon.getSize();
639     int R = Math.min(ps.width,ps.height)/2;
640 
641     icon.setLocation(new Point(ps.width/2 - ms.width/2,
642                                ps.height/2 - ms.height/2));
643 
644     int sr = (ms.width+ms.height)/2;
645     int n = 0;
646     GraphNode node;
647     for(int i = 0; i < aList.size(); i++ ) {
648        node = (GraphNode)aList.elementAt(i);
649        if ( node != self && (icon = (GraphIcon)ViewList.get(node)) != null ) {
650           ms = icon.getSize();
651 	  sr += (ms.width+ms.height)/2;
652           n++;
653        }
654     }
655 
656     if ( n != 0 )  {
657        R -= sr/n;
658        double phi;
659        Point pa = new Point(0,0);
660        double theta = (2*Math.PI/n);
661        int j = 0;
662        for(int i = 0; i < aList.size(); i++ ) {
663           node = (GraphNode)aList.elementAt(i);
664           if ( node != self && (icon = (GraphIcon)ViewList.get(node)) != null ) {
665              phi = j*theta;
666              pa.x = (int)(ps.width/2 + R*Math.cos(phi));
667              pa.y = (int)(ps.height/2 + R*Math.sin(phi));
668              ms = icon.getSize();
669              icon.setLocation(new Point(pa.x - ms.width/2,pa.y - ms.height/2));
670              j++;
671           }
672        }
673     }
674     return false;
675   }
676 
677   public boolean computeHorizontalPosition(GraphNode self, Vector doneList,
678                                            Point p) {
679     GraphNode node;
680     Dimension size;
681     Point p0 = new Point(0,0);
682     Point p1 = new Point(0,0);
683     Point loc = new Point(0,0);
684 
685     if ( doneList.contains(self) )
686        return false;
687 
688     doneList.addElement(self);
689     GraphIcon icon = (GraphIcon)ViewList.get(self);
690     if ( icon == null )
691        return false;
692 
693     if ( !icon.isVisible() )
694        return false;
695 
696     GraphNode[] children = self.getChildren();
697     Vector Items = new Vector();
698     for(int i = 0; i < children.length; i++ ) {
699        if ( !doneList.contains(children[i]) )
700           Items.addElement(children[i]);
701     }
702 
703     size = icon.getSize();
704     if ( Items.isEmpty() ) {
705       loc.y = p.y+(3*GAMMA+size.height)/2 - size.height/2;
706       loc.x = p.x-(BETA+size.width)/2 - size.width/2;
707 
708       icon.setLocation(loc);
709 
710       p.y += size.height+3*GAMMA;
711     }
712     else {
713       p0.y = p.y;
714       p0.x = p.x-(size.width+BETA);
715 
716       for(int i = 0; i < Items.size(); i++ ) {
717          node = (GraphNode) Items.elementAt(i);
718 	 computeHorizontalPosition(node,doneList,p0);
719       }
720       loc.y = (p.y+p0.y)/2 - size.height/2;
721       loc.x = p.x-(size.width+BETA)/2 - size.width/2;
722 
723       icon.setLocation(loc);
724 
725       p.y = p0.y;
726     }
727 
728     GraphNode[] siblings = self.getSiblings();
729     for(int i = 0; i < siblings.length; i++ ) {
730        if ( !doneList.contains(siblings[i]) )
731           computeHorizontalPosition(siblings[i],doneList,p);
732     }
733     return true;
734   }
735 
736   public boolean computeVerticalPosition(GraphNode self, Vector doneList, Point p) {
737     GraphNode node;
738     Dimension size;
739     Point p0 = new Point(0,0);
740     Point p1 = new Point(0,0);
741     Point loc = new Point(0,0);
742 
743     if ( doneList.contains(self) )
744        return false;
745 
746     doneList.addElement(self);
747     GraphIcon icon = (GraphIcon)ViewList.get(self);
748     if ( icon == null )
749        return false;
750 
751     if ( !icon.isVisible() )
752        return false;
753 
754     GraphNode[] children = self.getChildren();
755     Vector Items = new Vector();
756     for(int i = 0; i < children.length; i++ ) {
757        if ( !doneList.contains(children[i]) )
758           Items.addElement(children[i]);
759     }
760 
761     size = icon.getSize();
762     if ( Items.isEmpty() ) {
763        loc.x = p.x+(BETA+size.width)/2 - size.width/2;
764        loc.y = p.y+(3*GAMMA+size.height)/2 - size.height/2;
765 
766        icon.setLocation(loc);
767        p.x += size.width+BETA;
768     }
769     else {
770        p0.x = p.x;
771        p0.y = p.y+size.height+3*GAMMA;
772 
773        for(int i = 0; i < Items.size(); i++ ) {
774           node = (GraphNode) Items.elementAt(i);
775           computeVerticalPosition(node,doneList,p0);
776        }
777        loc.x = (p.x+p0.x)/2 - size.width/2;
778        loc.y = p.y+(size.height+3*GAMMA)/2 - size.height/2;
779 
780        icon.setLocation(loc);
781 
782        p.x = p0.x;
783     }
784 
785     GraphNode[] siblings = self.getSiblings();
786     for(int i = 0; i < siblings.length; i++ ) {
787        if ( !doneList.contains(siblings[i]) )
788           computeVerticalPosition(siblings[i],doneList,p);
789     }
790 
791     return true;
792   }
793 
794 
795   protected void drawLinks(GraphNode node, Graphics graphics) {
796     GraphIcon icon = (GraphIcon)ViewList.get(node);
797 
798     Point p1 = new Point(0,0);
799     Point p2 = new Point(0,0);
800 
801     Color color;
802     GraphNode[] parent = node.getParents();
803     for(int i = 0; i < parent.length; i++ )  {
804        if ( model.isLinkVisible(node,parent[i]) &&
805             getConnections(node,parent[i],p1,p2) ) {
806           color = model.getLinkColor(node,parent[i]);
807           drawEdge(color,p1,p2,graphics);
808        }
809     }
810 
811     GraphNode[] children = node.getChildren();
812     for(int i = 0; i < children.length; i++ )  {
813        if ( model.isLinkVisible(node,children[i]) &&
814             getConnections(node,children[i],p1,p2) ) {
815           color = model.getLinkColor(node,children[i]);
816           drawEdge(color,p1,p2,graphics);
817        }
818     }
819     
820     GraphNode[] sibling = node.getSiblings();
821     for(int i = 0; i < sibling.length; i++ ) {
822        if ( model.isLinkVisible(node,sibling[i]) &&
823             getConnections(node,sibling[i],p1,p2) ) {
824           color = model.getLinkColor(node,sibling[i]);
825           drawEdge(color,p1,p2,graphics);
826        }
827     }
828   }
829 
830   protected void getConnection(Rectangle bb, int edge, Point p) {
831     switch( edge ) {
832     case BOTTOM_EDGE:
833       p.x = bb.x + bb.width/2;
834       p.y = bb.y;
835       return;
836 
837     case TOP_EDGE:
838       p.x = bb.x + bb.width/2;
839       p.y = bb.y + bb.height;
840       return;
841 
842     case LEFT_EDGE:
843       p.x = bb.x;
844       p.y = bb.y + bb.height/2;
845       return;
846 
847     case RIGHT_EDGE:
848       p.x = bb.x + bb.width;
849       p.y = bb.y + bb.height/2;
850       return;
851 
852     default:
853       return;
854     }
855   }
856 
857   protected boolean getConnections(GraphNode node1, GraphNode node2,
858                                    Point p1, Point p2) {
859 
860     Rectangle a, b;
861     GraphIcon icon1 = (GraphIcon)ViewList.get(node1);
862     GraphIcon icon2 = (GraphIcon)ViewList.get(node2);
863 
864     if ( icon1 == null || icon2 == null ||
865          !icon1.isVisible() || !icon2.isVisible() )
866        return false;
867 
868 
869     a = icon1.getBounds();
870     b = icon2.getBounds();
871 
872     switch( viewMode ) {
873        case HORIZONTAL_CHILD_PARENT:
874        case HORIZONTAL_PARENT_CHILD:
875             if ( b.x > a.x+a.width ) {
876                getConnection(a,RIGHT_EDGE,p1);
877                getConnection(b,LEFT_EDGE,p2);
878                return true;
879             }
880             else if ( b.x+b.width < a.x ) {
881                getConnection(a,LEFT_EDGE,p1);
882                getConnection(b,RIGHT_EDGE,p2);
883                return true;
884             }
885             else if ( b.y > a.y+a.height ) {
886                getConnection(a,TOP_EDGE,p1);
887                getConnection(b,BOTTOM_EDGE,p2);
888                return true;
889             }
890             else if ( b.y+b.height < a.y ) {
891                getConnection(a,BOTTOM_EDGE,p1);
892                getConnection(b,TOP_EDGE,p2);
893                return true;
894             }
895             // in case of overlapping rects -- don't draw links
896             return false;
897 
898        default:
899             if ( b.y > a.y+a.height ) {
900                getConnection(a,TOP_EDGE,p1);
901                getConnection(b,BOTTOM_EDGE,p2);
902                return true;
903             }
904             else if ( b.y+b.height < a.y ) {
905                getConnection(a,BOTTOM_EDGE,p1);
906                getConnection(b,TOP_EDGE,p2);
907                return true;
908             }
909             else if ( b.x > a.x+a.width ) {
910                getConnection(a,RIGHT_EDGE,p1);
911                getConnection(b,LEFT_EDGE,p2);
912                return true;
913             }
914             else if ( b.x+b.width < a.x ) {
915                getConnection(a,LEFT_EDGE,p1);
916                getConnection(b,RIGHT_EDGE,p2);
917                return true;
918             }
919             return false;
920     }
921   }
922 
923   protected void drawEdge(Color color, Point p1, Point p2, Graphics g) {
924     Point[] pts;
925 
926     Color c = g.getColor();
927     g.setColor( color );
928     g.setPaintMode();
929 
930     g.drawLine(p1.x,p1.y,p2.x,p2.y);
931     pts = ArrowData.getPoints((double)p1.x, (double)p1.y,
932 			      (double)p2.x, (double)p2.y);
933     for(int i = 0; i < 2; i++ )
934       g.drawLine(pts[i].x,pts[i].y,pts[i+1].x,pts[i+1].y);
935     g.setColor(c);
936   }
937 
938   protected GraphIcon findIcon(Point pt) {
939      GraphIcon icon;
940      Rectangle region;
941      Enumeration enum = ViewList.elements();
942      while( enum.hasMoreElements() ) {
943         icon = (GraphIcon)enum.nextElement();
944         region = icon.getBounds();
945         if ( region.contains(pt.x,pt.y) )
946            return icon;
947      }
948      return null;
949   }
950 
951 
952   // GraphIcon Events
953   public void locationChanged(GraphIconEvent evt) {
954      redraw();
955   }
956   public void performLeftMouseDClickAction(GraphIconEvent evt) {
957      // if appropriate, configure node editor
958      if ( nodeEditorComponent == null && isNodeEditable ) {
959         GraphIcon icon = (GraphIcon)evt.getSource();
960         GraphNode node = icon.getGraphNode();
961         if ( model.isNodeEditable(node) ) {
962            if ( nodeEditor == null ) {
963               nodeEditor = new DefaultGraphNodeEditor();
964               nodeEditor.addGraphNodeEditorListener(this);
965            }
966            nodeEditorComponent = nodeEditor.getNodeEditorComponent(this,node);
967            Component root = SwingUtilities.getRoot(this);
968            if ( !(nodeEditorComponent instanceof Window) ) {
969               if ( root instanceof JDialog )
970                  pane = ((JDialog)root).getLayeredPane();
971               else if ( root instanceof JFrame )
972                  pane = ((JFrame)root).getLayeredPane();
973               else
974                  Core.ERROR(null,1,this);
975               pane.add(nodeEditorComponent,JLayeredPane.PALETTE_LAYER);
976               pane.moveToFront(nodeEditorComponent);
977            }
978            Point pt = SwingUtilities.convertPoint(icon,0,0,root);
979            nodeEditorComponent.setLocation(pt);
980            nodeEditorComponent.setVisible(true);
981            if ( nodeEditorComponent instanceof JComponent )
982 	      nodeEditorComponent.requestFocus();
983         }
984      }
985 
986   }
987   public void performLeftMouseAction(GraphIconEvent evt) {
988   }
989   public void performRightMouseAction(GraphIconEvent evt) {
990     if ( !isLinkEditable ) return;
991 
992     Point[] pts;
993     Graphics g = this.getGraphics();
994     Color color = g.getColor();
995     g.setColor(Color.black);
996     if (evt.isRightMousePressed() ){
997        if ( !(evt.getSource() instanceof GraphIcon) ) {
998          sourceIcon = null;
999          return;
1000        }
1001        sourceIcon = (GraphIcon)evt.getSource();
1002        lastPoint = stretchedPoint= startPoint = SwingUtilities.convertPoint(
1003           sourceIcon,evt.getPoint().x,evt.getPoint().y,this);
1004     }
1005     else if (evt.isRightMouseDragged() ){
1006       if ( sourceIcon == null ) return;
1007       lastPoint  = stretchedPoint;
1008       stretchedPoint =  SwingUtilities.convertPoint(
1009          sourceIcon,evt.getPoint().x,evt.getPoint().y,this);
1010 
1011       g.setXORMode(this.getBackground());
1012       g.drawLine(startPoint.x,startPoint.y,lastPoint.x,lastPoint.y);
1013       g.drawLine(startPoint.x,startPoint.y,stretchedPoint.x,stretchedPoint.y);
1014     }
1015     else if (evt.isRightMouseReleased() ){
1016       if ( sourceIcon == null ) return;
1017       lastPoint = stretchedPoint;
1018       stretchedPoint =  SwingUtilities.convertPoint(
1019          sourceIcon,evt.getPoint().x,evt.getPoint().y,this);
1020 
1021       g.setXORMode(this.getBackground());
1022       g.drawLine(startPoint.x,startPoint.y,lastPoint.x,lastPoint.y);
1023       destIcon = findIcon(stretchedPoint);
1024       if (destIcon == null) return;
1025       g.drawLine(startPoint.x,startPoint.y,stretchedPoint.x,stretchedPoint.y);
1026       pts = ArrowData.getPoints((double)startPoint.x, (double)startPoint.y,
1027 			      (double)stretchedPoint.x, (double)stretchedPoint.y);
1028       for(int i = 0; i < 2; i++ )
1029          g.drawLine( pts[i].x, pts[i].y, pts[i+1].x, pts[i+1].y);
1030     }
1031     g.setColor(color);
1032   }
1033 
1034   public void performMiddleMouseAction(GraphIconEvent evt) {
1035   }
1036 
1037   // GraphNodeEditor events
1038   public void graphNodeEditingStopped(GraphNodeEditorEvent evt) {
1039      if ( nodeEditorComponent != null ) {
1040         nodeEditorComponent.setVisible(false);
1041         if ( !(nodeEditorComponent instanceof Window) )
1042 	   pane.remove(nodeEditorComponent);
1043         nodeEditorComponent = null;
1044         model.setValue(evt.getNode(),evt.getValue());
1045      }
1046   }
1047   public void graphNodeEditingCancelled(GraphNodeEditorEvent evt) {
1048      if ( nodeEditorComponent != null ) {
1049         nodeEditorComponent.setVisible(false);
1050         if ( !(nodeEditorComponent instanceof Window) )
1051 	   pane.remove(nodeEditorComponent);
1052         nodeEditorComponent = null;
1053      }
1054   }
1055 
1056   // GraphModel Events
1057   public void graphStructureChanged(GraphModelEvent evt) {
1058      if ( model == evt.getModel() )
1059         reset();
1060   }
1061   public void graphNodeAdded(GraphModelEvent evt) {
1062      if ( model == evt.getModel() ) {
1063         addNode(evt.getNode());
1064         redraw();
1065      }
1066   }
1067   public void graphNodeRemoved(GraphModelEvent evt) {
1068      if ( model == evt.getModel() ) {
1069         removeNode(evt.getNode());
1070         redraw();
1071      }
1072   }
1073   public void graphNodeStateChanged(GraphModelEvent evt) {
1074      if ( model == evt.getModel() ) {
1075         updateNode(evt.getNode());
1076         redraw();
1077      }
1078   }
1079 }