001/* 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org/ 019 * 020 */ 021 022package org.biojava.bio.gui.sequence; 023 024import java.awt.Graphics2D; 025import java.awt.event.MouseEvent; 026import java.io.Serializable; 027import java.util.ArrayList; 028import java.util.Collections; 029import java.util.Iterator; 030import java.util.List; 031 032import org.biojava.utils.AbstractChangeable; 033import org.biojava.utils.ChangeEvent; 034import org.biojava.utils.ChangeForwarder; 035import org.biojava.utils.ChangeSupport; 036import org.biojava.utils.ChangeType; 037import org.biojava.utils.ChangeVetoException; 038import org.biojava.utils.Changeable; 039 040/** 041 * <code>PairwiseOverlayRenderer</code> allows a list of other 042 * <code>PairwiseSequenceRenderer</code>s to superimpose their 043 * output. Operations on the child renderers (rendering, event 044 * handling) are carried out in the order in which they were 045 * added. 046 * 047 * @author Keith James 048 * @author Matthew Pocock 049 * @since 1.2 050 */ 051public class PairwiseOverlayRenderer extends AbstractChangeable 052 implements PairwiseSequenceRenderer, Serializable 053{ 054 /** 055 * Constant <code>RENDERERS</code> indicating a change to the 056 * renderers handled by the overlay. 057 */ 058 public static final ChangeType RENDERERS = 059 new ChangeType("A PairwiseSequenceRenderer has been added or removed", 060 "org.biojava.bio.gui.sequence.PairwiseOverlayRenderer", 061 "RENDERERS", 062 SequenceRenderContext.LAYOUT); 063 064 private List renderers; 065 private transient ChangeForwarder rendererForwarder = null; 066 067 /** 068 * Creates a new, empty <code>PairwiseOverlayRenderer</code>. 069 */ 070 public PairwiseOverlayRenderer() 071 { 072 renderers = new ArrayList(); 073 } 074 075 /** 076 * <code>addRenderer</code> adds a renderer. 077 * 078 * @param renderer a <code>PairwiseSequenceRenderer</code>. 079 * 080 * @exception ChangeVetoException if an error occurs. 081 */ 082 public void addRenderer(PairwiseSequenceRenderer renderer) 083 throws ChangeVetoException 084 { 085 if (hasListeners()) 086 { 087 ChangeSupport cs = getChangeSupport(RENDERERS); 088 089 ChangeEvent ce = new ChangeEvent(this, RENDERERS, renderer, null); 090 091 synchronized(cs) 092 { 093 cs.firePreChangeEvent(ce); 094 _addRenderer(renderer); 095 096 if (renderer instanceof Changeable) 097 { 098 Changeable c = (Changeable) renderer; 099 c.addChangeListener(rendererForwarder, 100 SequenceRenderContext.REPAINT); 101 } 102 cs.firePostChangeEvent(ce); 103 } 104 } 105 else 106 { 107 _addRenderer(renderer); 108 } 109 } 110 111 /** 112 * <code>removeRenderer</code> removes a renderer. 113 * 114 * @param renderer a <code>PairwiseSequenceRenderer</code>. 115 * 116 * @exception ChangeVetoException if an error occurs. 117 */ 118 public void removeRenderer(PairwiseSequenceRenderer renderer) 119 throws ChangeVetoException 120 { 121 if (hasListeners()) 122 { 123 ChangeSupport cs = getChangeSupport(RENDERERS); 124 125 ChangeEvent ce = new ChangeEvent(this, RENDERERS, null, renderer); 126 127 synchronized(cs) 128 { 129 cs.firePreChangeEvent(ce); 130 _removeRenderer(renderer); 131 132 if (renderer instanceof Changeable) 133 { 134 Changeable c = (Changeable) renderer; 135 c.removeChangeListener(rendererForwarder, 136 SequenceRenderContext.REPAINT); 137 } 138 cs.firePostChangeEvent(ce); 139 } 140 } 141 else 142 { 143 _removeRenderer(renderer); 144 } 145 } 146 147 /** 148 * <code>clearRenderers</code> removes all the renderers. 149 * 150 * @exception ChangeVetoException if an error occurs. 151 */ 152 public void clearRenderers() throws ChangeVetoException 153 { 154 if (hasListeners()) 155 { 156 ChangeSupport cs = getChangeSupport(RENDERERS); 157 158 ChangeEvent ce = new ChangeEvent(this, RENDERERS); 159 160 synchronized(cs) 161 { 162 cs.firePreChangeEvent(ce); 163 for (Iterator i = renderers.iterator(); i.hasNext();) 164 { 165 Object renderer = i.next(); 166 if (renderer instanceof Changeable) 167 { 168 Changeable c = (Changeable) renderer; 169 c.removeChangeListener(rendererForwarder, 170 SequenceRenderContext.REPAINT); 171 } 172 } 173 renderers.clear(); 174 cs.firePostChangeEvent(ce); 175 } 176 } 177 else 178 { 179 renderers.clear(); 180 } 181 } 182 183 /** 184 * <code>paint</code> applies all renderers in the order in which 185 * they were added. 186 * 187 * @param g2 a <code>Graphics2D</code>. 188 * @param context a <code>PairwiseRenderContext</code>. 189 */ 190 public void paint(Graphics2D g2, PairwiseRenderContext context) 191 { 192 for (Iterator ri = renderers.iterator(); ri.hasNext();) 193 { 194 PairwiseSequenceRenderer renderer = (PairwiseSequenceRenderer) ri.next(); 195 renderer.paint(g2, context); 196 } 197 } 198 199 protected ChangeSupport getChangeSupport(ChangeType ct) 200 { 201 ChangeSupport cs = super.getChangeSupport(ct); 202 203 if (rendererForwarder == null) 204 { 205 rendererForwarder = 206 new PairwiseSequenceRenderer.PairwiseRendererForwarder(this, cs); 207 208 for (Iterator i = renderers.iterator(); i.hasNext();) 209 { 210 PairwiseSequenceRenderer renderer = (PairwiseSequenceRenderer) i.next(); 211 if (renderer instanceof Changeable) 212 { 213 Changeable c = (Changeable) renderer; 214 c.addChangeListener(rendererForwarder, 215 SequenceRenderContext.REPAINT); 216 } 217 } 218 } 219 220 return cs; 221 } 222 223 /** 224 * <code>processMouseEvent</code> produces a 225 * <code>SequenceViewerEvent</code> in response to a mouse 226 * gesture. The list of renderers are probed in the order in which 227 * they were added and the first renderer to accept the event will 228 * return. 229 * 230 * @param context a <code>PairwiseRenderContext</code>. 231 * @param me a <code>MouseEvent</code> that caused the request. 232 * @param path a <code>List</code> of 233 * <code>PairwiseSequenceRenderer</code> instances passed through 234 * so far. 235 * 236 * @return a <code>SequenceViewerEvent</code> encapsulating the 237 * mouse gesture. 238 */ 239 public SequenceViewerEvent processMouseEvent(PairwiseRenderContext context, 240 MouseEvent me, 241 List path) 242 { 243 path.add(this); 244 245 SequenceViewerEvent event = null; 246 247 List contextCopies = Collections.nCopies(renderers.size(), context); 248 Iterator ci = contextCopies.iterator(); 249 Iterator ri = renderers.iterator(); 250 251 while (ci.hasNext() && ri.hasNext()) 252 { 253 PairwiseRenderContext contextCopy = (PairwiseRenderContext) ci.next(); 254 PairwiseSequenceRenderer renderer = (PairwiseSequenceRenderer) ri.next(); 255 event = renderer.processMouseEvent(contextCopy, me, path); 256 } 257 258 if (event == null) 259 event = new SequenceViewerEvent(this, null, 260 context.graphicsToSequence(me.getPoint()), 261 me, path); 262 263 return event; 264 } 265 266 protected void _addRenderer(PairwiseSequenceRenderer renderer) 267 { 268 renderers.add(renderer); 269 } 270 271 protected void _removeRenderer(PairwiseSequenceRenderer renderer) 272 { 273 renderers.remove(renderer); 274 } 275}