001/*
002 * BioJava development code This code may be freely distributed and modified
003 * under the terms of the GNU Lesser General Public Licence. This should be
004 * distributed with the code. If you do not have a copy, see:
005 * http://www.gnu.org/copyleft/lesser.html Copyright for this code is held
006 * jointly by the individual authors. These should be listed in @author doc
007 * comments. For more information on the BioJava project and its aims, or to
008 * join the biojava-l mailing list, visit the home page at:
009 * http://www.biojava.org/
010 */
011package org.biojava.bio.gui.sequence;
012
013import java.awt.Graphics2D;
014import java.awt.event.MouseEvent;
015import java.awt.geom.Rectangle2D;
016import java.util.ArrayList;
017import java.util.List;
018import java.util.NoSuchElementException;
019
020import org.biojava.bio.gui.glyph.ArrowGlyph;
021import org.biojava.bio.gui.glyph.Glyph;
022import org.biojava.bio.seq.Feature;
023import org.biojava.bio.seq.FeatureFilter;
024import org.biojava.bio.seq.FeatureHolder;
025import org.biojava.bio.seq.StrandedFeature;
026import org.biojava.utils.ChangeVetoException;
027
028/**
029 * A FeatureRenderer that renders a particular Glyph for Features accepted by a
030 * particular FeatureFilter
031 *
032 * @author Mark Southern
033 * @author <a href="mailto:andreas.draeger@uni-tuebingen.de">Andreas Dr&auml;ger</a>
034 * @see org.biojava.bio.gui.glyph.Glyph
035 * @since 1.5
036 */
037public class GlyphFeatureRenderer extends FilteringRenderer implements
038    FeatureRenderer {
039        /**
040         *
041         */
042        private static final long serialVersionUID = 7005846487574725181L;
043
044        private double                depth     = 15;
045
046        protected List<FeatureFilter>   fList;
047
048        protected List<Glyph>           gList;
049
050        public GlyphFeatureRenderer() {
051                super();
052                fList = new ArrayList<FeatureFilter>();
053                gList = new ArrayList<Glyph>();
054        }
055
056        public void addFilterAndGlyph(FeatureFilter ff, Glyph g)
057            throws ChangeVetoException {
058                if (!fList.contains(ff)) {
059                        fList.add(ff);
060                        gList.add(g);
061
062                        if (fList.size() == 0)
063                                setFilter(FeatureFilter.none);
064                        else {
065                                FeatureFilter f = fList.get(0);
066                                if (fList.size() > 1) for (int i = 1; i < fList.size(); i++)
067                                        f = new FeatureFilter.Or(f, fList.get(i));
068                                setFilter(f);
069                        }
070                }
071        }
072
073        public void removeFilterWithGlyph(FeatureFilter ff)
074            throws ChangeVetoException {
075                if (fList.contains(ff)) {
076                        gList.remove(fList.indexOf(ff));
077                        fList.remove(ff);
078                        if (fList.size() == 0) {
079                                setFilter(FeatureFilter.none);
080                        } else {
081                                FeatureFilter f = fList.get(0);
082                                if (fList.size() > 1) for (int i = 1; i < fList.size(); i++)
083                                        f = new FeatureFilter.Or(f, fList.get(i));
084                                setFilter(f);
085                        }
086                }
087        }
088
089        /**
090         * Returns the ith {@link FeatureFilter} in this renderer.
091         *
092         * @param i
093         * @return the featureFilter
094         */
095        public FeatureFilter getFeatureFilter(int i) {
096                return fList.get(i);
097        }
098
099        /**
100         * Returns true if the given {@link FeatureFilter} is already contained in this
101         * renderer.
102         *
103         * @param ff
104         * @return flag if featurefilter is contained
105         */
106        public boolean containsFilter(FeatureFilter ff) {
107                return fList.contains(ff);
108        }
109
110        /**
111         * Allows setting another {@link Glyph } object to be painted for the given
112         * FeatureFilter.
113         *
114         * @param ff
115         * @param glyph
116         * @throws ChangeVetoException
117         */
118        public void setGlyphForFilter(FeatureFilter ff, Glyph glyph)
119            throws NoSuchElementException {
120                if (fList.contains(ff))
121                        gList.set(fList.indexOf(ff), glyph);
122                else throw new NoSuchElementException(ff.toString());
123        }
124
125        /**
126         * Returns the {@link Glyph}  object which is assigned to the given feature
127         * filter.
128         *
129         * @param ff
130         * @return {@link Glyph} object
131         * @throws NoSuchElementException
132         */
133        public Glyph getGlyphForFilter(FeatureFilter ff)
134            throws NoSuchElementException {
135                if (fList.contains(ff)) return gList.get(fList.indexOf(ff));
136                throw new NoSuchElementException(ff.toString());
137        }
138
139        public void setDepth(double depth) {
140                this.depth = depth;
141        }
142
143        /**
144         * Returns the depth property of this class.
145         *
146         * @return the depth
147         */
148        public double getDepth(SequenceRenderContext src) {
149                return depth;
150        }
151
152        public FeatureHolder processMouseEvent(FeatureHolder fh,
153            SequenceRenderContext src, MouseEvent me) {
154                return fh;
155        }
156
157        public void renderFeature(Graphics2D g2, Feature f, SequenceRenderContext src) {
158                float minBounds = (float) src.sequenceToGraphics(f.getLocation().getMin());
159                float maxBounds = (float) src
160                    .sequenceToGraphics(f.getLocation().getMax() + 1);
161                Rectangle2D.Float bounds;
162                bounds = new Rectangle2D.Float(minBounds, 0, maxBounds - minBounds,
163                    (float) depth);
164
165                for (int i = 0; i < fList.size(); i++)
166                        if (fList.get(i).accept(f)) {
167                                Glyph g = gList.get(i);
168                                g.setBounds(bounds);
169                                if ((g instanceof ArrowGlyph) && (f instanceof StrandedFeature))
170                                  ((ArrowGlyph) g).setDirection(((StrandedFeature) f).getStrand()
171                                      .getValue());
172                                if (src.getDirection() == SequenceRenderContext.HORIZONTAL)
173                                  g.render(g2);
174                        }
175        }
176}