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.Color;
025import java.awt.Graphics2D;
026import java.awt.Paint;
027import java.awt.Shape;
028import java.awt.event.MouseEvent;
029import java.awt.geom.GeneralPath;
030
031import org.biojava.bio.seq.Feature;
032import org.biojava.bio.seq.FeatureHolder;
033import org.biojava.bio.seq.StrandedFeature;
034import org.biojava.bio.symbol.Location;
035import org.biojava.utils.AbstractChangeable;
036import org.biojava.utils.ChangeEvent;
037import org.biojava.utils.ChangeSupport;
038import org.biojava.utils.ChangeType;
039import org.biojava.utils.ChangeVetoException;
040
041/**
042 * @author Thomas Down
043 * @author Matthew Pocock
044 * @author David Huen
045 */
046public class TickFeatureRenderer
047extends AbstractChangeable
048implements FeatureRenderer {
049  public static final ChangeType FILL = new ChangeType(
050    "The fill paint has changed",
051    "org.biojava.bio.gui.sequence.TickFeatureRenderer",
052    "FILL"
053  );
054
055  public static final ChangeType OUTLINE = new ChangeType(
056    "The outline paint has changed",
057    "org.biojava.bio.gui.sequence.TickFeatureRenderer",
058    "OUTLINE"
059  );
060
061  public static final ChangeType DEPTH = new ChangeType(
062    "The size of the arrow has changed",
063    "org.biojava.bio.gui.sequence.TickFeatureRenderer",
064    "DEPTH"
065  );
066
067  private Paint fill;
068  private Paint outline;
069  private double depth = 25.0;
070
071  public TickFeatureRenderer() {
072    fill = Color.blue;
073    outline = Color.black;
074  }
075
076  public void setFill(Paint p)
077  throws ChangeVetoException {
078    if(hasListeners()) {
079      ChangeSupport cs = getChangeSupport(SequenceRenderContext.REPAINT);
080      synchronized(cs) {
081        ChangeEvent ce = new ChangeEvent(
082          this, SequenceRenderContext.REPAINT,
083          null, null, new ChangeEvent(
084            this, FILL, p, fill
085          )
086        );
087        cs.firePreChangeEvent(ce);
088        fill = p;
089        cs.firePostChangeEvent(ce);
090      }
091    } else {
092      fill = p;
093    }
094  }
095
096  public Paint getFill() {
097    return fill;
098  }
099
100  public void setOutline(Paint p)
101  throws ChangeVetoException {
102    if(hasListeners()) {
103      ChangeSupport cs = getChangeSupport(SequenceRenderContext.REPAINT);
104      synchronized(cs) {
105        ChangeEvent ce = new ChangeEvent(
106          this, SequenceRenderContext.REPAINT,
107          null, null, new ChangeEvent(
108            this, OUTLINE, p, outline
109          )
110        );
111        cs.firePreChangeEvent(ce);
112        outline = p;
113        cs.firePostChangeEvent(ce);
114      }
115    } else {
116      outline = p;
117    }
118  }
119
120  public Paint getOutline() {
121    return outline;
122  }
123
124  public void setDepth(double arrowSize)
125  throws ChangeVetoException {
126    if(hasListeners()) {
127      ChangeSupport cs = getChangeSupport(SequenceRenderContext.LAYOUT);
128      synchronized(cs) {
129        ChangeEvent ce = new ChangeEvent(
130          this, SequenceRenderContext.LAYOUT,
131          null, null, new ChangeEvent(
132            this, DEPTH, new Double(this.depth), new Double(arrowSize)
133          )
134        );
135        cs.firePreChangeEvent(ce);
136        this.depth = arrowSize;
137        cs.firePostChangeEvent(ce);
138      }
139    } else {
140      this.depth = arrowSize;
141    }
142  }
143
144  public double getDepth() {
145    return depth;
146  }
147
148  public void renderFeature(
149    Graphics2D g,
150    Feature f,
151    SequenceRenderContext src
152  ) {
153    Shape s = null;
154    Location loc = f.getLocation();
155    float min = (float) src.sequenceToGraphics(loc.getMin());
156    float max = (float) src.sequenceToGraphics(loc.getMax());
157    float pos = (min + max) / 2;
158
159    float fDepth = (float) depth;
160    float fDepthByThree = fDepth / 3.0F;
161
162    if (f instanceof StrandedFeature) {
163      StrandedFeature.Strand strand = ((StrandedFeature) f).getStrand();
164      if(src.getDirection() == SequenceRenderContext.HORIZONTAL) {
165        if(strand == StrandedFeature.POSITIVE) {
166          GeneralPath path = new GeneralPath();
167          path.moveTo(pos, 0.0F);
168          path.lineTo(pos, fDepth);
169          path.lineTo(pos + fDepthByThree, fDepth);
170          path.lineTo(pos, fDepth - fDepthByThree);
171          path.closePath();
172          s = path;
173        } else if(strand == StrandedFeature.NEGATIVE) {
174          GeneralPath path = new GeneralPath();
175          path.moveTo(pos, 0.0F);
176          path.lineTo(pos, fDepth);
177          path.lineTo(pos - fDepthByThree, fDepth);
178          path.lineTo(pos, fDepth - fDepthByThree);
179          path.closePath();
180          s = path;
181        }
182      } else { // vertical
183        if(strand == StrandedFeature.POSITIVE) {
184          GeneralPath path = new GeneralPath();
185          path.moveTo(0.0F, pos);
186          path.lineTo(fDepth, pos);
187          path.lineTo(fDepth, pos + fDepthByThree);
188          path.lineTo(fDepth - fDepthByThree, pos);
189          path.closePath();
190          s = path;
191        } else if(strand == StrandedFeature.NEGATIVE) {
192          GeneralPath path = new GeneralPath();
193          path.moveTo(0.0F, pos);
194          path.lineTo(fDepth, pos);
195          path.lineTo(fDepth, pos - fDepthByThree);
196          path.lineTo(fDepth - fDepthByThree, pos);
197          path.closePath();
198          s = path;
199        }
200      }
201    }
202
203    if(fill != null) {
204      Paint prevPaint = g.getPaint();
205      g.setPaint(fill);
206      g.fill(s);
207      g.setPaint(prevPaint);
208    }
209    if (outline != null) {
210      Paint prevPaint = g.getPaint();
211      g.setPaint(outline);
212      g.draw(s);
213      g.setPaint(prevPaint);
214    }
215  }
216
217  public double getDepth(SequenceRenderContext src) {
218      return depth;
219  }
220
221  public FeatureHolder processMouseEvent(
222    FeatureHolder hits,
223    SequenceRenderContext src,
224    MouseEvent me
225  ) {
226    return hits;
227  }
228}