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.Font;
026import java.awt.Graphics2D;
027import java.awt.Paint;
028import java.awt.event.MouseEvent;
029import java.awt.geom.AffineTransform;
030import java.awt.geom.Rectangle2D;
031import java.util.List;
032
033import org.biojava.bio.BioRuntimeException;
034import org.biojava.bio.seq.io.SymbolTokenization;
035import org.biojava.bio.symbol.Location;
036import org.biojava.bio.symbol.SymbolList;
037
038/**
039 * <code>SymbolSequenceRenderer</code> renders symbols of a
040 * <code>SymbolList</code>.
041 *
042 * @author Matthew Pocock
043 * @author Thomas Down
044 * @author David Huen
045 * @author Keith James
046 * @author Kalle N&auml;slund
047 */
048public class SymbolSequenceRenderer implements SequenceRenderer
049{
050  private double depth = 25.0;
051  private Paint  outline;
052
053  public SymbolSequenceRenderer()
054  {
055    outline = Color.black;
056  }
057
058  public double getDepth(SequenceRenderContext context)
059  {
060    return depth + 1.0;
061  }
062
063  public double getMinimumLeader(SequenceRenderContext context)
064  {
065    return 0.0;
066  }
067
068  public double getMinimumTrailer(SequenceRenderContext context)
069  {
070    return 0.0;
071  }
072
073  public void paint(Graphics2D g2, SequenceRenderContext context)
074  {
075    Rectangle2D prevClip = g2.getClipBounds();
076    AffineTransform prevTransform = g2.getTransform();
077
078    g2.setPaint(outline);
079
080    Font font = context.getFont();
081
082    Rectangle2D maxCharBounds =
083            font.getMaxCharBounds(g2.getFontRenderContext());
084
085    double scale = context.getScale();
086
087    if (scale >= (maxCharBounds.getWidth() * 0.3) &&
088            scale >= (maxCharBounds.getHeight() * 0.3))
089    {
090      double xFontOffset = 0.0;
091      double yFontOffset = 0.0;
092
093      // These offsets are not set quite correctly yet. The
094      // Rectangle2D from getMaxCharBounds() seems slightly
095      // off. The "correct" application of translations based on
096      // the Rectangle2D seem to give the wrong results. The
097      // values below are mostly fudges.
098      if (context.getDirection() == SequenceRenderContext.HORIZONTAL)
099      {
100        xFontOffset = maxCharBounds.getCenterX() * 0.25;
101        yFontOffset = - maxCharBounds.getCenterY() + (depth * 0.5);
102      }
103      else
104      {
105        xFontOffset = - maxCharBounds.getCenterX() + (depth * 0.5);
106        yFontOffset = - maxCharBounds.getCenterY() * 3.0;
107      }
108
109      SymbolList seq = context.getSymbols();
110      SymbolTokenization toke = null;
111      try {
112        toke = seq.getAlphabet().getTokenization("token");
113      } catch (Exception ex) {
114        throw new BioRuntimeException(ex);
115      }
116
117      Location visible = GUITools.getVisibleRange(context, g2);
118      for (int sPos = visible.getMin(); sPos <= visible.getMax(); sPos++)
119      {
120        double gPos = context.sequenceToGraphics(sPos);
121        String s = "?";
122        try {
123          s = toke.tokenizeSymbol(seq.symbolAt(sPos));
124        } catch (Exception ex) {
125          // We'll ignore the case of not being able to tokenize it
126        }
127
128        if (context.getDirection() == SequenceRenderContext.HORIZONTAL)
129        {
130          g2.drawString(s,
131                        (float) (gPos + xFontOffset),
132                        (float) yFontOffset);
133        }
134        else
135        {
136          g2.drawString(s,
137                        (float) xFontOffset,
138                        (float) (gPos + yFontOffset));
139        }
140      }
141    }
142
143    g2.setClip(prevClip);
144    g2.setTransform(prevTransform);
145  }
146
147  public SequenceViewerEvent processMouseEvent(SequenceRenderContext context,
148                                               MouseEvent            me,
149                                               List                  path)
150  {
151    path.add(this);
152    int sPos = context.graphicsToSequence(me.getPoint());
153    return new SequenceViewerEvent(this, null, sPos, me, path);
154  }
155}