View Javadoc

1   /*
2   * Copyright (c) 2003, the JUNG Project and the Regents of the University 
3   * of California
4   * All rights reserved.
5   *
6   * This software is open-source under the BSD license; see either
7   * "license.txt" or
8   * http://jung.sourceforge.net/license.txt for a description.
9   */
10  package edu.uci.ics.jung.visualization;
11  
12  import java.awt.Color;
13  import java.awt.Dimension;
14  import java.awt.Graphics;
15  import java.awt.Graphics2D;
16  import java.awt.RenderingHints;
17  import java.awt.RenderingHints.Key;
18  import java.awt.event.ComponentAdapter;
19  import java.awt.event.ComponentEvent;
20  import java.awt.event.ItemEvent;
21  import java.awt.event.ItemListener;
22  import java.awt.geom.AffineTransform;
23  import java.awt.geom.Point2D;
24  import java.awt.image.BufferedImage;
25  import java.util.ArrayList;
26  import java.util.HashMap;
27  import java.util.List;
28  import java.util.Map;
29  
30  import javax.swing.JPanel;
31  import javax.swing.event.ChangeEvent;
32  import javax.swing.event.ChangeListener;
33  
34  import edu.uci.ics.jung.algorithms.layout.GraphElementAccessor;
35  import edu.uci.ics.jung.algorithms.layout.Layout;
36  import edu.uci.ics.jung.visualization.control.ScalingControl;
37  import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer;
38  import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer;
39  import edu.uci.ics.jung.visualization.picking.MultiPickedState;
40  import edu.uci.ics.jung.visualization.picking.PickedState;
41  import edu.uci.ics.jung.visualization.picking.ShapePickSupport;
42  import edu.uci.ics.jung.visualization.renderers.BasicRenderer;
43  import edu.uci.ics.jung.visualization.renderers.Renderer;
44  import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator;
45  import edu.uci.ics.jung.visualization.util.Caching;
46  import edu.uci.ics.jung.visualization.util.ChangeEventSupport;
47  import edu.uci.ics.jung.visualization.util.DefaultChangeEventSupport;
48  
49  /**
50   * A class that maintains many of the details necessary for creating 
51   * visualizations of graphs.
52   * This is the old VisualizationViewer without tooltips and mouse behaviors. Its purpose is
53   * to be a base class that can also be used on the server side of a multi-tiered application.
54   * 
55   * @author Joshua O'Madadhain
56   * @author Tom Nelson
57   * @author Danyel Fisher
58   */
59  @SuppressWarnings("serial")
60  public class BasicVisualizationServer<V, E> extends JPanel 
61                  implements ChangeListener, ChangeEventSupport, VisualizationServer<V, E>{
62  
63      protected ChangeEventSupport changeSupport =
64          new DefaultChangeEventSupport(this);
65      
66      /**
67       * holds the state of this View
68       */
69      protected VisualizationModel<V,E> model;
70  
71  	/**
72  	 * handles the actual drawing of graph elements
73  	 */
74  	protected Renderer<V,E> renderer = new BasicRenderer<V,E>();
75  	
76  	/**
77  	 * rendering hints used in drawing. Anti-aliasing is on
78  	 * by default
79  	 */
80  	protected Map<Key, Object> renderingHints = new HashMap<Key, Object>();
81  		
82  	/**
83  	 * holds the state of which vertices of the graph are
84  	 * currently 'picked'
85  	 */
86  	protected PickedState<V> pickedVertexState;
87  	
88  	/**
89  	 * holds the state of which edges of the graph are
90  	 * currently 'picked'
91  	 */
92      protected PickedState<E> pickedEdgeState;
93      
94      /**
95       * a listener used to cause pick events to result in
96       * repaints, even if they come from another view
97       */
98      protected ItemListener pickEventListener;
99  	
100 	/**
101 	 * an offscreen image to render the graph
102 	 * Used if doubleBuffered is set to true
103 	 */
104 	protected BufferedImage offscreen;
105 	
106 	/**
107 	 * graphics context for the offscreen image
108 	 * Used if doubleBuffered is set to true
109 	 */
110 	protected Graphics2D offscreenG2d;
111 	
112 	/**
113 	 * user-settable choice to use the offscreen image
114 	 * or not. 'false' by default
115 	 */
116 	protected boolean doubleBuffered;
117 	
118 	/**
119 	 * a collection of user-implementable functions to render under
120 	 * the topology (before the graph is rendered)
121 	 */
122 	protected List<Paintable> preRenderers = new ArrayList<Paintable>();
123 	
124 	/**
125 	 * a collection of user-implementable functions to render over the
126 	 * topology (after the graph is rendered)
127 	 */
128 	protected List<Paintable> postRenderers = new ArrayList<Paintable>();
129 	
130     protected RenderContext<V,E> renderContext = new PluggableRenderContext<V,E>();
131     
132     /**
133      * Create an instance with passed parameters.
134      * 
135      * @param layout		The Layout to apply, with its associated Graph
136      * @param renderer		The Renderer to draw it with
137      */
138 	public BasicVisualizationServer(Layout<V,E> layout) {
139 	    this(new DefaultVisualizationModel<V,E>(layout));
140 	}
141 	
142     /**
143      * Create an instance with passed parameters.
144      * 
145      * @param layout		The Layout to apply, with its associated Graph
146      * @param renderer		The Renderer to draw it with
147      * @param preferredSize the preferred size of this View
148      */
149 	public BasicVisualizationServer(Layout<V,E> layout, Dimension preferredSize) {
150 	    this(new DefaultVisualizationModel<V,E>(layout, preferredSize), preferredSize);
151 	}
152 	
153 	/**
154 	 * Create an instance with passed parameters.
155 	 * 
156 	 * @param model
157 	 * @param renderer
158 	 */
159 	public BasicVisualizationServer(VisualizationModel<V,E> model) {
160 	    this(model, new Dimension(600,600));
161 	}
162 	/**
163 	 * Create an instance with passed parameters.
164 	 * 
165 	 * @param model
166 	 * @param renderer
167 	 * @param preferredSize initial preferred size of the view
168 	 */
169 	@SuppressWarnings("unchecked")
170     public BasicVisualizationServer(VisualizationModel<V,E> model,
171 	        Dimension preferredSize) {
172 	    this.model = model;
173 //        renderContext.setScreenDevice(this);
174 	    model.addChangeListener(this);
175 	    setDoubleBuffered(false);
176 		this.addComponentListener(new VisualizationListener(this));
177 
178 		setPickSupport(new ShapePickSupport<V,E>(this));
179 		setPickedVertexState(new MultiPickedState<V>());
180 		setPickedEdgeState(new MultiPickedState<E>());
181         
182         renderContext.setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer<E>(getPickedEdgeState(), Color.black, Color.cyan));
183         renderContext.setVertexFillPaintTransformer(new PickableVertexPaintTransformer<V>(getPickedVertexState(), 
184                 Color.red, Color.yellow));
185 		
186 		setPreferredSize(preferredSize);
187 		renderingHints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
188 
189         renderContext.getMultiLayerTransformer().addChangeListener(this);
190 	}
191 	
192 	@Override
193     public void setDoubleBuffered(boolean doubleBuffered) {
194 	    this.doubleBuffered = doubleBuffered;
195 	}
196 	
197 	@Override
198     public boolean isDoubleBuffered() {
199 	    return doubleBuffered;
200 	}
201 	
202 	/**
203 	 * Always sanity-check getSize so that we don't use a
204 	 * value that is improbable
205 	 * @see java.awt.Component#getSize()
206 	 */
207 	@Override
208 	public Dimension getSize() {
209 		Dimension d = super.getSize();
210 		if(d.width <= 0 || d.height <= 0) {
211 			d = getPreferredSize();
212 		}
213 		return d;
214 	}
215 
216 	/**
217 	 * Ensure that, if doubleBuffering is enabled, the offscreen
218 	 * image buffer exists and is the correct size.
219 	 * @param d
220 	 */
221 	protected void checkOffscreenImage(Dimension d) {
222 	    if(doubleBuffered) {
223 	        if(offscreen == null || offscreen.getWidth() != d.width || offscreen.getHeight() != d.height) {
224 	            offscreen = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_ARGB);
225 	            offscreenG2d = offscreen.createGraphics();
226 	        }
227 	    }
228 	}
229 	
230     /* (non-Javadoc)
231      * @see edu.uci.ics.jung.visualization.VisualizationServer#getModel()
232      */
233     public VisualizationModel<V,E> getModel() {
234         return model;
235     }
236     /* (non-Javadoc)
237      * @see edu.uci.ics.jung.visualization.VisualizationServer#setModel(edu.uci.ics.jung.visualization.VisualizationModel)
238      */
239     public void setModel(VisualizationModel<V,E> model) {
240         this.model = model;
241     }
242 	/* (non-Javadoc)
243      * @see edu.uci.ics.jung.visualization.VisualizationServer#stateChanged(javax.swing.event.ChangeEvent)
244      */
245 	public void stateChanged(ChangeEvent e) {
246 	    repaint();
247 	    fireStateChanged();
248 	}
249 
250 	/* (non-Javadoc)
251      * @see edu.uci.ics.jung.visualization.VisualizationServer#setRenderer(edu.uci.ics.jung.visualization.Renderer)
252      */
253 	public void setRenderer(Renderer<V,E> r) {
254 	    this.renderer = r;
255 	    repaint();
256 	}
257 	
258 	/* (non-Javadoc)
259      * @see edu.uci.ics.jung.visualization.VisualizationServer#getRenderer()
260      */
261 	public Renderer<V,E> getRenderer() {
262 	    return renderer;
263 	}
264 
265 	/* (non-Javadoc)
266      * @see edu.uci.ics.jung.visualization.VisualizationServer#setGraphLayout(edu.uci.ics.jung.visualization.layout.Layout)
267      */
268     public void setGraphLayout(Layout<V,E> layout) {
269     	Dimension viewSize = getPreferredSize();
270     	if(this.isShowing()) {
271     		viewSize = getSize();
272     	}
273 	    model.setGraphLayout(layout, viewSize);
274     }
275     
276     public void scaleToLayout(ScalingControl scaler) {
277     	Dimension vd = getPreferredSize();
278     	if(this.isShowing()) {
279     		vd = getSize();
280     	}
281 		Dimension ld = getGraphLayout().getSize();
282 		if(vd.equals(ld) == false) {
283 			scaler.scale(this, (float)(vd.getWidth()/ld.getWidth()), new Point2D.Double());
284 		}
285     }
286 	
287 	public Layout<V,E> getGraphLayout() {
288 	        return model.getGraphLayout();
289 	}
290 	
291 	@Override
292     public void setVisible(boolean aFlag) {
293 		super.setVisible(aFlag);
294 		if(aFlag == true) {
295 			Dimension d = this.getSize();
296 			if(d.width <= 0 || d.height <= 0) {
297 				d = this.getPreferredSize();
298 			}
299 			model.getGraphLayout().setSize(d);
300 		}
301 	}
302 
303     public Map<Key, Object> getRenderingHints() {
304         return renderingHints;
305     }
306     
307     public void setRenderingHints(Map<Key, Object> renderingHints) {
308         this.renderingHints = renderingHints;
309     }
310     
311 	@Override
312     protected void paintComponent(Graphics g) {
313         super.paintComponent(g);
314 
315 		Graphics2D g2d = (Graphics2D)g;
316 		if(doubleBuffered) {
317 		    checkOffscreenImage(getSize());
318 			renderGraph(offscreenG2d);
319 		    g2d.drawImage(offscreen, null, 0, 0);
320 		} else {
321 		    renderGraph(g2d);
322 		}
323 	}
324 	
325 	protected void renderGraph(Graphics2D g2d) {
326 	    if(renderContext.getGraphicsContext() == null) {
327 	        renderContext.setGraphicsContext(new GraphicsDecorator(g2d));
328         } else {
329         	renderContext.getGraphicsContext().setDelegate(g2d);
330         }
331         renderContext.setScreenDevice(this);
332 	    Layout<V,E> layout = model.getGraphLayout();
333 
334 		g2d.setRenderingHints(renderingHints);
335 		
336 		// the size of the VisualizationViewer
337 		Dimension d = getSize();
338 		
339 		// clear the offscreen image
340 		g2d.setColor(getBackground());
341 		g2d.fillRect(0,0,d.width,d.height);
342 
343 		AffineTransform oldXform = g2d.getTransform();
344         AffineTransform newXform = new AffineTransform(oldXform);
345         newXform.concatenate(
346         		renderContext.getMultiLayerTransformer().getTransformer(Layer.VIEW).getTransform());
347 //        		viewTransformer.getTransform());
348 		
349         g2d.setTransform(newXform);
350 
351 		// if there are  preRenderers set, paint them
352 		for(Paintable paintable : preRenderers) {
353 
354 		    if(paintable.useTransform()) {
355 		        paintable.paint(g2d);
356 		    } else {
357 		        g2d.setTransform(oldXform);
358 		        paintable.paint(g2d);
359                 g2d.setTransform(newXform);
360 		    }
361 		}
362 		
363         if(layout instanceof Caching) {
364         	((Caching)layout).clear();
365         }
366         
367         renderer.render(renderContext, layout);
368 		
369 		// if there are postRenderers set, do it
370 		for(Paintable paintable : postRenderers) {
371 
372 		    if(paintable.useTransform()) {
373 		        paintable.paint(g2d);
374 		    } else {
375 		        g2d.setTransform(oldXform);
376 		        paintable.paint(g2d);
377                 g2d.setTransform(newXform);
378 		    }
379 		}
380 		g2d.setTransform(oldXform);
381 	}
382 
383 	/**
384 	 * VisualizationListener reacts to changes in the size of the
385 	 * VisualizationViewer. When the size changes, it ensures
386 	 * that the offscreen image is sized properly. 
387 	 * If the layout is locked to this view size, then the layout
388 	 * is also resized to be the same as the view size.
389 	 *
390 	 *
391 	 */
392 	protected class VisualizationListener extends ComponentAdapter {
393 		protected BasicVisualizationServer<V,E> vv;
394 		public VisualizationListener(BasicVisualizationServer<V,E> vv) {
395 			this.vv = vv;
396 		}
397 
398 		/**
399 		 * create a new offscreen image for the graph
400 		 * whenever the window is resied
401 		 */
402 		@Override
403         public void componentResized(ComponentEvent e) {
404 		    Dimension d = vv.getSize();
405 		    if(d.width <= 0 || d.height <= 0) return;
406 		    checkOffscreenImage(d);
407 		    repaint();
408 		}
409 	}
410 
411     public void addPreRenderPaintable(Paintable paintable) {
412         if(preRenderers == null) {
413             preRenderers = new ArrayList<Paintable>();
414         }
415         preRenderers.add(paintable);
416     }
417     
418     public void prependPreRenderPaintable(Paintable paintable) {
419         if(preRenderers == null) {
420             preRenderers = new ArrayList<Paintable>();
421         }
422         preRenderers.add(0,paintable);
423     }
424     
425     public void removePreRenderPaintable(Paintable paintable) {
426         if(preRenderers != null) {
427             preRenderers.remove(paintable);
428         }
429     }
430     
431     public void addPostRenderPaintable(Paintable paintable) {
432         if(postRenderers == null) {
433             postRenderers = new ArrayList<Paintable>();
434         }
435         postRenderers.add(paintable);
436     }
437     
438     public void prependPostRenderPaintable(Paintable paintable) {
439         if(postRenderers == null) {
440             postRenderers = new ArrayList<Paintable>();
441         }
442         postRenderers.add(0,paintable);
443     }
444     
445     public void removePostRenderPaintable(Paintable paintable) {
446         if(postRenderers != null) {
447             postRenderers.remove(paintable);
448         }
449     }
450 
451     public void addChangeListener(ChangeListener l) {
452         changeSupport.addChangeListener(l);
453     }
454     
455     public void removeChangeListener(ChangeListener l) {
456         changeSupport.removeChangeListener(l);
457     }
458     
459     public ChangeListener[] getChangeListeners() {
460         return changeSupport.getChangeListeners();
461     }
462 
463     public void fireStateChanged() {
464         changeSupport.fireStateChanged();
465     }   
466     
467     public PickedState<V> getPickedVertexState() {
468         return pickedVertexState;
469     }
470 
471     public PickedState<E> getPickedEdgeState() {
472         return pickedEdgeState;
473     }
474     
475     public void setPickedVertexState(PickedState<V> pickedVertexState) {
476         if(pickEventListener != null && this.pickedVertexState != null) {
477             this.pickedVertexState.removeItemListener(pickEventListener);
478         }
479         this.pickedVertexState = pickedVertexState;
480         this.renderContext.setPickedVertexState(pickedVertexState);
481         if(pickEventListener == null) {
482             pickEventListener = new ItemListener() {
483 
484                 public void itemStateChanged(ItemEvent e) {
485                     repaint();
486                 }
487             };
488         }
489         pickedVertexState.addItemListener(pickEventListener);
490     }
491     
492     /* (non-Javadoc)
493      * @see edu.uci.ics.jung.visualization.VisualizationServer#setPickedEdgeState(edu.uci.ics.jung.visualization.picking.PickedState)
494      */
495     public void setPickedEdgeState(PickedState<E> pickedEdgeState) {
496         if(pickEventListener != null && this.pickedEdgeState != null) {
497             this.pickedEdgeState.removeItemListener(pickEventListener);
498         }
499         this.pickedEdgeState = pickedEdgeState;
500         this.renderContext.setPickedEdgeState(pickedEdgeState);
501         if(pickEventListener == null) {
502             pickEventListener = new ItemListener() {
503 
504                 public void itemStateChanged(ItemEvent e) {
505                     repaint();
506                 }
507             };
508         }
509         pickedEdgeState.addItemListener(pickEventListener);
510     }
511     
512     /* (non-Javadoc)
513      * @see edu.uci.ics.jung.visualization.VisualizationServer#getPickSupport()
514      */
515     public GraphElementAccessor<V,E> getPickSupport() {
516         return renderContext.getPickSupport();
517     }
518     /* (non-Javadoc)
519      * @see edu.uci.ics.jung.visualization.VisualizationServer#setPickSupport(edu.uci.ics.jung.visualization.GraphElementAccessor)
520      */
521     public void setPickSupport(GraphElementAccessor<V,E> pickSupport) {
522         renderContext.setPickSupport(pickSupport);
523     }
524     
525     /* (non-Javadoc)
526      * @see edu.uci.ics.jung.visualization.VisualizationServer#getCenter()
527      */
528     public Point2D getCenter() {
529         Dimension d = getSize();
530         return new Point2D.Float(d.width/2, d.height/2);
531     }
532 
533     /* (non-Javadoc)
534      * @see edu.uci.ics.jung.visualization.VisualizationServer#getRenderContext()
535      */
536     public RenderContext<V,E> getRenderContext() {
537         return renderContext;
538     }
539 
540     /* (non-Javadoc)
541      * @see edu.uci.ics.jung.visualization.VisualizationServer#setRenderContext(edu.uci.ics.jung.visualization.RenderContext)
542      */
543     public void setRenderContext(RenderContext<V,E> renderContext) {
544         this.renderContext = renderContext;
545     }
546 }