View Javadoc

1   package org.glassbox.graphview;
2   
3   import java.awt.Graphics2D;
4   import java.awt.Point;
5   import java.awt.Rectangle;
6   import java.awt.Insets;
7   import java.util.LinkedList;
8   import java.util.List;
9   
10  import de.fzi.wim.guibase.graphview.graph.*;
11  import de.fzi.wim.guibase.graphview.view.*;
12  
13  /***
14     A decorator for a NodePainter which supplies a set of icons to a node.
15   */
16  public class NodeIconSet implements ZoomableNodePainter {
17      /***
18         Interface for an Icon which can be added to this NodeIconSet.
19       */
20      public interface Icon extends javax.swing.Icon {
21  	/***
22  	   Decide if the icon needs to be painted for the specified node.
23  	 */
24  	boolean affectsNode(Node node);
25  	/***
26  	   Get the tool tip for the specified node.
27  	 */
28  	String getToolTipText(Node node);
29      }
30  
31      /***
32         Private interface for an internal state.
33      */
34      private interface State {
35  	Icon getIconAtPoint(JGraphPane graphpane, Node node, Point point);
36  	void getIconsBounds(Node node, Rectangle nodebounds, Rectangle iconsbounds);
37  	void paintNode(JGraphPane graphpane,Graphics2D g,Node node);	
38      }
39  
40      /***
41         Subclasses of this State provide the horizontally aligned IconSets.
42       */
43      private abstract class Horizontal implements State {
44  	public Icon getIconAtPoint(JGraphPane graphpane, Node node, Point point) {
45  	    _painter.getNodeScreenBounds(graphpane, node, nR);
46  	    getIconsBounds(node, nR, isR);
47  	    
48  	    iR.x = isR.x + _insets.left;
49  	    iR.y = isR.y + _insets.top;
50  	    
51  	    for (int i=0; i<_icons.length; i++) {
52  		Icon icon = _icons[i];
53  		if (icon.affectsNode(node)) {
54  		    iR.width = icon.getIconWidth();
55  		    iR.height = icon.getIconHeight();
56  		    if (iR.contains(point)) return icon;
57  		    iR.x += icon.getIconWidth() + _insets.left;
58  		}
59  	    }
60  	    return null;
61  	}
62  	public void getIconsBounds(Node node, Rectangle nodebounds, Rectangle iconbounds) {
63  	    int height = 0;
64  	    int width = 0;
65  	    for (int i=0; i<_icons.length; i++) {
66  		Icon icon = _icons[i];
67  		if (icon.affectsNode(node)) {
68  		    height = Math.max(height, icon.getIconHeight());
69  		    width += _insets.left + icon.getIconWidth();
70  		}
71  	    }
72  	    if (width > 0) {
73  		width += _insets.right;
74  		height += _insets.top + _insets.bottom;
75  	    } else {
76  		height = 0;
77  	    }
78  	    iconbounds.setSize(width, height);
79  	    getIconsOffset(node, nodebounds, iconbounds);
80  	}
81  	public void paintNode(JGraphPane graphpane,Graphics2D g,Node node) {
82  	    if (getIconCount(node)>0) {
83  		if (isR.x == 0 && isR.y == 0 && isR.width == 0 && isR.height == 0) {
84  		    _painter.getNodeScreenBounds(graphpane, node, nR);
85  		    getIconsBounds(node, nR, isR);
86  		}
87  		
88  		int x = isR.x + _insets.left;
89  		int y = isR.y + _insets.top;
90  		
91  		for (int i=0; i<_icons.length; i++) {
92  		    Icon icon = _icons[i];
93  		    if (icon.affectsNode(node)) {
94  			paintIcon(graphpane, g, node, icon, x, y);
95  			x += _icons[i].getIconWidth() + _insets.left;
96  		    }
97  		}
98  	    }
99  	}
100 	protected abstract void getIconsOffset(Node node, Rectangle nodebounds, Rectangle iconbounds);
101     }
102 
103     /***
104        Subclasses of this State provide the vertically aligned IconSets.
105      */
106     private abstract class Vertical implements State {
107 	public Icon getIconAtPoint(JGraphPane graphpane, Node node, Point point) {
108 	    _painter.getNodeScreenBounds(graphpane, node, nR);
109 	    getIconsBounds(node, nR, isR);
110 	    
111 	    iR.x = isR.x + _insets.left;
112 	    iR.y = isR.y + _insets.top;
113 	    
114 	    for (int i=0; i<_icons.length; i++) {
115 		Icon icon = _icons[i];
116 		if (icon.affectsNode(node)) {
117 		    iR.width = icon.getIconWidth();
118 		    iR.height = icon.getIconHeight();
119 		    if (iR.contains(point)) return icon;
120 		    iR.y += icon.getIconHeight() + _insets.top;
121 		}
122 	    }
123 	    return null;
124 	}
125 	public void getIconsBounds(Node node, Rectangle nodebounds, Rectangle iconbounds) {
126 	    int height = 0;
127 	    int width = 0;
128 	    for (int i=0; i<_icons.length; i++) {
129 		Icon icon = _icons[i];
130 		if (icon.affectsNode(node)) {
131 		    height += _insets.top + icon.getIconHeight();
132 		    width = Math.max(width, icon.getIconWidth());
133 		}
134 	    }
135 	    if (height > 0) {
136 		width += _insets.left + _insets.right;
137 		height += _insets.bottom;
138 	    } else {
139 		width = 0;
140 	    }
141 	    iconbounds.setSize(width, height);
142 	    getIconsOffset(node, nodebounds, iconbounds);
143 	}
144 	public void paintNode(JGraphPane graphpane,Graphics2D g,Node node) {
145 	    if (getIconCount(node)>0) {
146 		if (isR.x == 0 && isR.y == 0 && isR.width == 0 && isR.height == 0) {
147 		    _painter.getNodeScreenBounds(graphpane, node, nR);
148 		    getIconsBounds(node, nR, isR);
149 		}
150 		
151 		int x = isR.x + _insets.left;
152 		int y = isR.y + _insets.top;
153 		
154 		for (int i=0; i<_icons.length; i++) {
155 		    Icon icon = _icons[i];
156 		    if (icon.affectsNode(node)) {
157 			paintIcon(graphpane, g, node, icon, x, y);
158 			y += _icons[i].getIconHeight() + _insets.top;
159 		    }
160 		}
161 	    }
162 	}
163 	protected abstract void getIconsOffset(Node node, Rectangle nodebounds, Rectangle iconbounds);
164     }
165 
166     /***
167        An implementation of State.
168      */
169     private class HORIZONTAL_TOP_OUTSIDE_LEFT_INSIDE extends Horizontal {
170 	protected void getIconsOffset(Node node, Rectangle nodebounds, Rectangle iconsbounds) {
171 	    iconsbounds.setLocation(nodebounds.x, nodebounds.y - iconsbounds.height);
172 	}
173     }
174 
175     /***
176        An implementation of State.
177      */
178     private class HORIZONTAL_TOP_OUTSIDE_RIGHT_INSIDE extends Horizontal {
179 	protected void getIconsOffset(Node node, Rectangle nodebounds, Rectangle iconsbounds) {
180 	    iconsbounds.setLocation(nodebounds.x + nodebounds.width - iconsbounds.width , nodebounds.y - iconsbounds.height);
181 	}
182     }
183 
184     /***
185        An implementation of State.
186      */
187     private class HORIZONTAL_BOTTOM_OUTSIDE_LEFT_INSIDE extends Horizontal {
188 	protected void getIconsOffset(Node node, Rectangle nodebounds, Rectangle iconsbounds) {
189 	    iconsbounds.setLocation(nodebounds.x, nodebounds.y);
190 	}
191     }
192 
193     /***
194        An implementation of State.
195      */
196     private class HORIZONTAL_BOTTOM_OUTSIDE_RIGHT_INSIDE extends Horizontal {
197 	protected void getIconsOffset(Node node, Rectangle nodebounds, Rectangle iconsbounds) {
198 	    iconsbounds.setLocation(nodebounds.x + nodebounds.width - iconsbounds.width , nodebounds.y);
199 	}
200     }
201 
202     /***
203        An implementation of State.
204      */
205     private class HORIZONTAL_TOP_INSIDE_LEFT_OUTSIDE extends Horizontal {
206 	protected void getIconsOffset(Node node, Rectangle nodebounds, Rectangle iconsbounds) {
207 	    iconsbounds.setLocation(nodebounds.x - iconsbounds.width, nodebounds.y);
208 	}
209     }
210     
211     /***
212        An implementation of State.
213      */
214     private class HORIZONTAL_TOP_INSIDE_RIGHT_OUTSIDE extends Horizontal {
215 	protected void getIconsOffset(Node node, Rectangle nodebounds, Rectangle iconsbounds) {
216 	    iconsbounds.setLocation(nodebounds.x + nodebounds.width, nodebounds.y);
217 	}
218     }
219 
220     /***
221        An implementation of State.
222      */
223     private class HORIZONTAL_CENTER_INSIDE_LEFT_OUTSIDE extends Horizontal {
224 	protected void getIconsOffset(Node node, Rectangle nodebounds, Rectangle iconsbounds) {
225 	    iconsbounds.setLocation(nodebounds.x - iconsbounds.width, nodebounds.y + nodebounds.height / 2 - iconsbounds.height / 2);
226 	}
227     }
228 
229     /***
230        An implementation of State.
231      */
232     private class HORIZONTAL_CENTER_INSIDE_RIGHT_OUTSIDE extends Horizontal {
233 	protected void getIconsOffset(Node node, Rectangle nodebounds, Rectangle iconsbounds) {
234 	    iconsbounds.setLocation(nodebounds.x + nodebounds.width, nodebounds.y + nodebounds.height / 2 - iconsbounds.height / 2);
235 	}
236     }
237 
238     /***
239        An implementation of State.
240      */
241     private class HORIZONTAL_BOTTOM_INSIDE_LEFT_OUTSIDE extends Horizontal {
242 	protected void getIconsOffset(Node node, Rectangle nodebounds, Rectangle iconsbounds) {
243 	    iconsbounds.setLocation(nodebounds.x - iconsbounds.width, nodebounds.y + nodebounds.height - iconsbounds.height);
244 	}
245     }
246 
247     /***
248        An implementation of State.
249      */
250     private class HORIZONTAL_BOTTOM_INSIDE_RIGHT_OUTSIDE extends Horizontal {
251 	protected void getIconsOffset(Node node, Rectangle nodebounds, Rectangle iconsbounds) {
252 	    iconsbounds.setLocation(nodebounds.x + nodebounds.width, nodebounds.y + nodebounds.height - iconsbounds.height);
253 	}
254     }
255 
256     /***
257        An implementation of State.
258      */
259     private class VERTICAL_TOP_INSIDE_LEFT_OUTSIDE extends Vertical {
260 	protected void getIconsOffset(Node node, Rectangle nodebounds, Rectangle iconsbounds) {
261 	    iconsbounds.setLocation(nodebounds.x - iconsbounds.width, nodebounds.y);
262 	}
263     }
264 
265     /***
266        An implementation of State.
267      */
268     private class VERTICAL_TOP_INSIDE_RIGHT_OUTSIDE extends Vertical {
269 	protected void getIconsOffset(Node node, Rectangle nodebounds, Rectangle iconsbounds) {
270 	    iconsbounds.setLocation(nodebounds.x + nodebounds.width, nodebounds.y);
271 	}
272     }
273 
274     /***
275        An implementation of State.
276      */
277     private class VERTICAL_CENTER_INSIDE_LEFT_OUTSIDE extends Vertical {
278 	protected void getIconsOffset(Node node, Rectangle nodebounds, Rectangle iconsbounds) {
279 	    iconsbounds.setLocation(nodebounds.x - iconsbounds.width, nodebounds.y + nodebounds.height / 2 - iconsbounds.height / 2);
280 	}
281     }
282 
283     /***
284        An implementation of State.
285      */
286     private class VERTICAL_CENTER_INSIDE_RIGHT_OUTSIDE extends Vertical {
287 	protected void getIconsOffset(Node node, Rectangle nodebounds, Rectangle iconsbounds) {
288 	    iconsbounds.setLocation(nodebounds.x + nodebounds.width, nodebounds.y + nodebounds.height / 2 - iconsbounds.height / 2);
289 	}
290     }
291 
292     /***
293        An implementation of State.
294      */
295     private class VERTICAL_BOTTOM_INSIDE_LEFT_OUTSIDE extends Vertical {
296 	protected void getIconsOffset(Node node, Rectangle nodebounds, Rectangle iconsbounds) {
297 	    iconsbounds.setLocation(nodebounds.x - iconsbounds.width, nodebounds.y + nodebounds.height - iconsbounds.height);
298 	}
299     }
300 
301     /***
302        An implementation of State.
303      */
304     private class VERTICAL_BOTTOM_INSIDE_RIGHT_OUTSIDE extends Vertical {
305 	protected void getIconsOffset(Node node, Rectangle nodebounds, Rectangle iconsbounds) {
306 	    iconsbounds.setLocation(nodebounds.x + nodebounds.width, nodebounds.y + nodebounds.height - iconsbounds.height);
307 	}
308     }
309 
310     /*** The value for horizontal orientation. */
311     public static final int HORIZONTAL = 1;
312     /*** The value for vertical orientation. */
313     public static final int VERTICAL = 2;
314     /*** The value for center alignment. */
315     public static final int CENTER = 33;
316     /*** The value for left alignment. */
317     public static final int LEFT = 2;
318     /*** The value for right alignment. */
319     public static final int RIGHT = 4;
320     /*** The value for top alignment. */
321     public static final int TOP = 8;
322     /*** The value for bottom alignment. */
323     public static final int BOTTOM = 16;
324     /*** The value for inside alignment. */
325     public static final int INSIDE = 32;
326     /*** The value for outside alignment. */
327     public static final int OUTSIDE = 64;
328 
329     protected NodePainter _painter;
330     protected Icon[] _icons;
331     protected Insets _insets;
332     protected State _state;
333 
334     protected Rectangle iR;
335     protected Rectangle isR;
336     protected Rectangle nR;
337     
338     /***
339        Constructor.
340        @param painter The <code>NodePainter</code> decorated by this painter.
341        @param insets The insets of each icon. <code>insets.left</code> / <code>insets.top</code> will be used in between icons.
342        @param orientation The orientation of the icons, either <code>HORIZONTAL</code> or <code>VERTICAL</code>.
343        @param alignx The horizontal align of the Icons, a combination <code>LEFT</code> or <code>RIGHT</code> 
344        and <code>INSIDE</code> or <code>OUTSIDE</code>.
345        @param aligny The vertical align of the Icons, one of <code>CENTER</code>, <code>TOP</code> or <code>BOTTOM</code> 
346        in combination with code>INSIDE</code> or <code>OUTSIDE</code>.
347      */
348     public NodeIconSet(NodePainter painter, Insets insets, int orientation, int alignx, int aligny) {
349 	_painter = painter;
350 	_icons = new Icon[0];
351 	_insets = insets;
352 
353 	_state = null;
354 	if (orientation == HORIZONTAL) {
355 	    if ((aligny & INSIDE) != 0) {
356 		if ((aligny & TOP) != 0) {
357 		    if ((alignx & LEFT) != 0) {
358 			_state = new HORIZONTAL_TOP_INSIDE_LEFT_OUTSIDE();
359 		    } else if ((alignx & RIGHT) != 0) {
360 			_state = new HORIZONTAL_TOP_INSIDE_RIGHT_OUTSIDE();
361 		    }
362 		} else if ((aligny & BOTTOM) != 0) {
363 		    if ((alignx & LEFT) != 0) {
364 			_state = new HORIZONTAL_BOTTOM_INSIDE_LEFT_OUTSIDE();
365 		    } else if ((alignx & RIGHT) != 0) {
366 			_state = new HORIZONTAL_BOTTOM_INSIDE_RIGHT_OUTSIDE();
367 		    }
368 		} else {
369 		    if ((alignx & LEFT) != 0) {
370 			_state = new HORIZONTAL_CENTER_INSIDE_LEFT_OUTSIDE();
371 		    } else if ((alignx & RIGHT) != 0) {
372 			_state = new HORIZONTAL_CENTER_INSIDE_RIGHT_OUTSIDE();
373 		    }
374 		}
375 	    } else {
376 		if ((aligny & TOP) != 0) {
377 		    if ((alignx & LEFT) != 0) {
378 			_state = new HORIZONTAL_TOP_OUTSIDE_LEFT_INSIDE();
379 		    } else if ((alignx & RIGHT) != 0) {
380 			_state = new HORIZONTAL_TOP_OUTSIDE_RIGHT_INSIDE();
381 		    }
382 		} else if ((aligny & BOTTOM) != 0) {
383 		    if ((alignx & LEFT) != 0) {
384 			_state = new HORIZONTAL_BOTTOM_OUTSIDE_LEFT_INSIDE();
385 		    } else if ((alignx & RIGHT) != 0) {
386 			_state = new HORIZONTAL_BOTTOM_OUTSIDE_RIGHT_INSIDE();
387 		    }
388 		} 
389 	    } 		
390 	} else {
391 	    if ((aligny & TOP) != 0) {
392 		if ((alignx & LEFT) != 0) {
393 		    _state = new VERTICAL_TOP_INSIDE_LEFT_OUTSIDE();
394 		} else {
395 		    _state = new VERTICAL_TOP_INSIDE_RIGHT_OUTSIDE();
396 		}
397 	    } else if ((aligny & CENTER) != 0) {
398 		if ((alignx & LEFT) != 0) {
399 		    _state = new VERTICAL_CENTER_INSIDE_LEFT_OUTSIDE();
400 		} else {
401 		    _state = new VERTICAL_CENTER_INSIDE_RIGHT_OUTSIDE();
402 		}
403 	    } else {
404 		if ((alignx & LEFT) != 0) {
405 		    _state = new VERTICAL_BOTTOM_INSIDE_LEFT_OUTSIDE();
406 		} else {
407 		    _state = new VERTICAL_BOTTOM_INSIDE_RIGHT_OUTSIDE();
408 		}
409 	    }
410 	}
411 	if (_state == null) 
412 	    throw new IllegalArgumentException("Unsupported combination of ORIENTATION and X/Y ALIGNMENT.");
413 
414 	iR = new Rectangle();
415 	isR = new Rectangle();
416 	nR = new Rectangle();
417     }
418 
419     public NodePainter getDelegate() {
420 	return _painter;
421     }
422 
423     /***
424        Add an icon to this NodeIconSet.
425      */
426     public void addIcon(Icon icon) {
427 	Icon[] dummy = new Icon[_icons.length+1];
428 	System.arraycopy(_icons, 0, dummy, 0, _icons.length);
429 	dummy[_icons.length] = icon;
430 	_icons = dummy;
431     }
432 
433     /***
434        Get the icon at the specified point.
435      */
436     public Icon getIconAtPoint(JGraphPane graphpane, Node node, Point point) {
437 	return _state.getIconAtPoint(graphpane, node, point);
438     }
439 
440     /***
441        Get the number of icons painted for the specified node.
442      */
443     protected int getIconCount(Node node) {
444 	int ii = 0;
445 	for (int i=0; i<_icons.length; i++) {
446 	    if (_icons[i].affectsNode(node)) ii++;
447 	}
448 	return ii;
449     }
450 
451     ///
452     /// Implementation of ZoomableNodePainter
453     /// ....................................................................................................
454 
455     public void paintNode(JGraphPane graphpane,Graphics2D g,Node node) {
456 	isR.setBounds(0, 0, 0, 0);
457 	_painter.paintNode(graphpane, g, node);
458 	_state.paintNode(graphpane, g, node);
459     }
460 
461     protected void paintIcon(JGraphPane grp, Graphics2D g, Node node, Icon icon, int x, int y) {
462 	icon.paintIcon(grp, g, x, y);
463     }
464 
465     public boolean isInNode(JGraphPane graphpane,Node node,Point point) {
466 	getNodeScreenBounds(graphpane, node, nR);
467 	return nR.contains(point);
468     }
469 
470     public void getNodeScreenBounds(JGraphPane graphpane, Node node, Rectangle nodebounds) {
471 	_painter.getNodeScreenBounds(graphpane, node, nodebounds);	
472 	_state.getIconsBounds(node, nodebounds, isR);
473 	nodebounds.add(isR);
474     }
475 
476     public String getToolTipText(JGraphPane graphpane,Node node,Point point) {
477 	Icon icon = _state.getIconAtPoint(graphpane, node, point);
478 	if (icon!=null) return icon.getToolTipText(node);
479 	else return _painter.getToolTipText(graphpane, node, point);
480     }
481 
482     public void setZoomFactor(double zf) {
483 	if (_painter instanceof ZoomableNodePainter) {
484 	    ((ZoomableNodePainter)_painter).setZoomFactor(zf);
485 	}
486     }
487 }