org.biojava.bio.gui.sequence.AbstractBeadRenderer Maven / Gradle / Ivy
/*
* BioJava development code
*
* This code may be freely distributed and modified under the
* terms of the GNU Lesser General Public Licence. This should
* be distributed with the code. If you do not have a copy,
* see:
*
* http://www.gnu.org/copyleft/lesser.html
*
* Copyright for this code is held jointly by the individual
* authors. These should be listed in @author doc comments.
*
* For more information on the BioJava project and its aims,
* or to join the biojava-l mailing list, visit the home page
* at:
*
* http://www.biojava.org/
*
*/
package org.biojava.bio.gui.sequence;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.biojava.bio.seq.Feature;
import org.biojava.bio.seq.FeatureFilter;
import org.biojava.bio.seq.FeatureHolder;
import org.biojava.bio.seq.OptimizableFilter;
import org.biojava.utils.AbstractChangeable;
import org.biojava.utils.ChangeEvent;
import org.biojava.utils.ChangeSupport;
import org.biojava.utils.ChangeType;
import org.biojava.utils.ChangeVetoException;
/**
* AbstractBeadRenderer
is a an abstract base class
* for the creation of FeatureRenderer
s which use a
* 'string of beads' metaphor for displaying features. Each subclass
* of AbstractBeadRenderer
should override the abstract
* method renderBead()
and provide the drawing routine
* for its particular bead type.
*
* A concrete BeadFeatureRenderer
may render a series
* of features in more than one style by delegating to other
* BeadFeatureRenderer
s for the additional style(s). This
* is achieved using the setDelegateRenderer()
method
* which associates an OptimizableFilter
with another
* BeadFeatureRenderer
. Any feature accepted by the
* filter is rendered with that renderer, while the remainder are
* rendered by the current renderer.
*
* @author Keith James
* @author Paul Seed
* @since 1.2
*/
public abstract class AbstractBeadRenderer extends AbstractChangeable
implements BeadFeatureRenderer, Serializable
{
/**
* Constant DISPLACEMENT
indicating a change to the
* Y-axis displacement of the features.
*/
public static final ChangeType DISPLACEMENT =
new ChangeType("The displacement of the features has changed",
"org.biojava.bio.gui.sequence.AbstractBeadRenderer",
"DISPLACEMENT", SequenceRenderContext.LAYOUT);
/**
* Constant DEPTH
indicating a change to the depth of
* the renderer.
*/
public static final ChangeType DEPTH =
new ChangeType("The depth of the renderer has changed",
"org.biojava.bio.gui.sequence.AbstractBeadRenderer",
"DEPTH", SequenceRenderContext.LAYOUT);
/**
* Constant OUTLINE
indicating a change to the
* outline paint of the features.
*/
public static final ChangeType OUTLINE =
new ChangeType("The outline of the features has changed",
"org.biojava.bio.gui.sequence.AbstractBeadRenderer",
"OUTLINE", SequenceRenderContext.REPAINT);
/**
* Constant STROKE
indicating a change to the outline
* stroke of the features.
*/
public static final ChangeType STROKE =
new ChangeType("The stroke of the features has changed",
"org.biojava.bio.gui.sequence.AbstractBeadRenderer",
"STROKE", SequenceRenderContext.REPAINT);
/**
* Constant FILL
indicating a change to the fill of
* the features.
*/
public static final ChangeType FILL =
new ChangeType("The fill of the features has changed",
"org.biojava.bio.gui.sequence.AbstractBeadRenderer",
"FILL", SequenceRenderContext.REPAINT);
protected double beadDepth;
protected double beadDisplacement;
protected Paint beadOutline;
protected Paint beadFill;
protected Stroke beadStroke;
// Map of FeatureFilter -> FeatureRenderer
protected Map delegates;
// Map of Feature -> FeatureRenderer
protected Map delegationCache;
/**
* Creates a new AbstractBeadRenderer
with no
* delegates. It will render all features itself, using its own
* style settings.
*/
public AbstractBeadRenderer()
{
this(10.0f, 0.0f, Color.black, Color.black, new BasicStroke());
}
/**
* Creates a new AbstractBeadRenderer
object.
*
* @param beadDepth a double
.
* @param beadDisplacement a double
.
* @param beadOutline a Paint
.
* @param beadFill a Paint
.
* @param beadStroke a Stroke
.
*/
public AbstractBeadRenderer(double beadDepth,
double beadDisplacement,
Paint beadOutline,
Paint beadFill,
Stroke beadStroke)
{
this.beadDepth = beadDepth;
this.beadDisplacement = beadDisplacement;
this.beadOutline = beadOutline;
this.beadFill = beadFill;
this.beadStroke = beadStroke;
delegates = new HashMap();
delegationCache = new HashMap();
}
/**
* processMouseEvent
defines the behaviour on
* revieving a mouse event.
*
* @param holder a FeatureHolder
.
* @param context a SequenceRenderContext
.
* @param mEvent a MouseEvent
.
*
* @return a FeatureHolder
.
*/
public FeatureHolder processMouseEvent(FeatureHolder holder,
SequenceRenderContext context,
MouseEvent mEvent)
{
return holder;
}
/**
* renderFeature
draws a feature using the supplied
* graphics context. The rendering may be delegated to another
* FeatureRenderer
instance.
*
* @param g2 a Graphics2D
context.
* @param f a Feature
to render.
* @param context a SequenceRenderContext
context.
*/
public void renderFeature(Graphics2D g2,
Feature f,
SequenceRenderContext context)
{
// Check the cache first
if (delegationCache.containsKey(f))
{
// System.err.println("Used cache for: " + f);
BeadFeatureRenderer cachedRenderer =
(BeadFeatureRenderer) delegationCache.get(f);
cachedRenderer.renderBead(g2, f, context);
return;
}
for (Iterator di = delegates.keySet().iterator(); di.hasNext();)
{
FeatureFilter filter = (FeatureFilter) di.next();
if (filter.accept(f))
{
// System.err.println(filter + " accepted " + f);
FeatureRenderer delegate =
(FeatureRenderer) delegates.get(filter);
delegate.renderFeature(g2, f, context);
return;
}
}
delegationCache.put(f, this);
// System.err.println("Rendering: " + f);
renderBead(g2, f, context);
}
/**
* setDelegateRenderer
associates an
* OptimizableFilter
with a
* BeadFeatureRenderer
. Any feature accepted by the
* filter will be passed to the associated renderer for
* drawing. The OptimizableFilter
s should be disjoint
* with respect to each other (a feature may not be rendered more
* than once).
*
* @param filter an OptimizableFilter
.
* @param renderer a BeadFeatureRenderer
.
*
* @exception IllegalArgumentException if the filter is not
* disjoint with existing delegate filters.
*/
public void setDelegateRenderer(OptimizableFilter filter,
BeadFeatureRenderer renderer)
throws IllegalArgumentException
{
// Ensure the cache doesn't hide the new delegate
delegationCache.clear();
Set delegateFilters = delegates.keySet();
if (delegateFilters.size() == 0)
{
delegates.put(filter, renderer);
}
else
{
for (Iterator fi = delegateFilters.iterator(); fi.hasNext();)
{
OptimizableFilter thisFilter = (OptimizableFilter) fi.next();
if (! thisFilter.isDisjoint(filter))
{
throw new IllegalArgumentException("Failed to apply filter as it clashes with existing filter "
+ thisFilter
+ " (filters must be disjoint)");
}
else
{
delegates.put(filter, renderer);
break;
}
}
}
}
/**
* removeDelegateRenderer
removes any association
* of the given OptimizableFilter
with a
* BeadFeatureRenderer
.
*
* @param filter an OptimizableFilter
.
*/
public void removeDelegateRenderer(OptimizableFilter filter)
{
// Ensure the cache doesn't hide the change of delegate
delegationCache.clear();
delegates.remove(filter);
}
/**
* getDepth
calculates the depth required by this
* renderer to display its beads. It recurses through its delegate
* renderers and returns the highest value. Concrete renderers
* should override this method and supply code to calculate their
* own depth. If a subclass needs to know the depth of its
* delegates (as is likely if it has any) they can call this
* method using super.getDepth()
.
*
* @param context a SequenceRenderContext
.
*
* @return a double
.
*/
public double getDepth(SequenceRenderContext context)
{
Collection delegateRenderers = delegates.values();
double maxDepth = 0.0;
if (delegateRenderers.size() == 0)
{
return maxDepth + 1.0;
}
else
{
for (Iterator ri = delegateRenderers.iterator(); ri.hasNext();)
{
maxDepth = Math.max(maxDepth, ((FeatureRenderer) ri.next()).getDepth(context));
}
return maxDepth + 1.0;
}
}
/**
* getBeadDepth
returns the depth of a single bead
* produced by this renderer.
*
* @return a double
.
*/
public double getBeadDepth()
{
return beadDepth;
}
/**
* setBeadDepth
sets the depth of a single bead
* produced by this renderer.
*
* @param depth a double
.
*
* @exception ChangeVetoException if an error occurs.
*/
public void setBeadDepth(double depth) throws ChangeVetoException
{
if (hasListeners())
{
ChangeSupport cs = getChangeSupport(SequenceRenderContext.LAYOUT);
synchronized(cs)
{
ChangeEvent ce = new ChangeEvent(this, SequenceRenderContext.LAYOUT,
null, null,
new ChangeEvent(this, DEPTH,
new Double(beadDepth),
new Double(depth)));
cs.firePreChangeEvent(ce);
beadDepth = depth;
cs.firePostChangeEvent(ce);
}
}
else
{
beadDepth = depth;
}
}
/**
* getBeadDisplacement
returns the displacement of
* beads from the centre line of the renderer. A positive value
* indicates displacment downwards (for horizontal renderers) or
* to the right (for vertical renderers).
*
* @return a double
.
*/
public double getBeadDisplacement()
{
return beadDisplacement;
}
/**
* setBeadDisplacement
sets the displacement of
* beads from the centre line of the renderer. A positive value
* indicates displacment downwards (for horizontal renderers) or
* to the right (for vertical renderers).
*
* @param displacement a double
.
*
* @exception ChangeVetoException if an error occurs.
*/
public void setBeadDisplacement(double displacement)
throws ChangeVetoException
{
if (hasListeners())
{
ChangeSupport cs = getChangeSupport(SequenceRenderContext.LAYOUT);
synchronized(cs)
{
ChangeEvent ce = new ChangeEvent(this, SequenceRenderContext.LAYOUT,
null, null,
new ChangeEvent(this, DISPLACEMENT,
new Double(beadDisplacement),
new Double(displacement)));
cs.firePreChangeEvent(ce);
beadDisplacement = displacement;
cs.firePostChangeEvent(ce);
}
}
else
{
beadDisplacement = displacement;
}
}
/**
* getBeadOutline
returns the bead outline paint.
*
* @return a Paint
.
*/
public Paint getBeadOutline()
{
return beadOutline;
}
/**
* setBeadOutline
sets the bead outline paint.
*
* @param outline a Paint
.
*
* @exception ChangeVetoException if an error occurs.
*/
public void setBeadOutline(Paint outline) throws ChangeVetoException
{
if (hasListeners())
{
ChangeSupport cs = getChangeSupport(SequenceRenderContext.LAYOUT);
synchronized(cs)
{
ChangeEvent ce = new ChangeEvent(this, SequenceRenderContext.LAYOUT,
null, null,
new ChangeEvent(this, OUTLINE,
outline,
beadOutline));
cs.firePreChangeEvent(ce);
beadOutline = outline;
cs.firePostChangeEvent(ce);
}
}
else
{
beadOutline = outline;
}
}
/**
* getBeadStroke
returns the bead outline stroke.
*
* @return a Stroke
.
*/
public Stroke getBeadStroke()
{
return beadStroke;
}
/**
* setBeadStroke
sets the bead outline stroke.
*
* @param stroke a Stroke
.
*
* @exception ChangeVetoException if an error occurs.
*/
public void setBeadStroke(Stroke stroke) throws ChangeVetoException
{
if (hasListeners())
{
ChangeSupport cs = getChangeSupport(SequenceRenderContext.LAYOUT);
synchronized(cs)
{
ChangeEvent ce = new ChangeEvent(this, SequenceRenderContext.LAYOUT,
null, null,
new ChangeEvent(this, STROKE,
stroke,
beadStroke));
cs.firePreChangeEvent(ce);
beadStroke = stroke;
cs.firePostChangeEvent(ce);
}
}
else
{
beadStroke = stroke;
}
}
/**
* getBeadFill
returns the bead fill paint.
*
* @return a Paint
.
*/
public Paint getBeadFill()
{
return beadFill;
}
/**
* setBeadFill
sets the bead fill paint.
*
* @param fill a Paint
.
*
* @exception ChangeVetoException if an error occurs.
*/
public void setBeadFill(Paint fill) throws ChangeVetoException
{
if (hasListeners())
{
ChangeSupport cs = getChangeSupport(SequenceRenderContext.LAYOUT);
synchronized(cs)
{
ChangeEvent ce = new ChangeEvent(this, SequenceRenderContext.LAYOUT,
null, null,
new ChangeEvent(this, FILL,
fill,
beadFill));
cs.firePreChangeEvent(ce);
beadFill = fill;
cs.firePostChangeEvent(ce);
}
}
else
{
beadFill = fill;
}
}
/**
* renderBead
should be overridden by the concrete
* BeadRenderer
.
*
* @param g2 a Graphics2D
.
* @param f a Feature
to render.
* @param context a SequenceRenderContext
context.
*/
public abstract void renderBead(Graphics2D g2,
Feature f,
SequenceRenderContext context);
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy