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
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 }