org.biojava.bio.gui.sequence.PairwiseSequencePanel 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.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.Serializable;
import java.util.ArrayList;
import javax.swing.JComponent;
import org.biojava.bio.seq.FeatureHolder;
import org.biojava.bio.seq.Sequence;
import org.biojava.bio.symbol.RangeLocation;
import org.biojava.bio.symbol.SymbolList;
import org.biojava.utils.ChangeAdapter;
import org.biojava.utils.ChangeEvent;
import org.biojava.utils.ChangeListener;
import org.biojava.utils.ChangeSupport;
import org.biojava.utils.ChangeType;
import org.biojava.utils.ChangeVetoException;
import org.biojava.utils.Changeable;
/**
* A PairwiseSequencePanel
is a panel that displays a
* pair of sequences; one sequence (the primary) may be either
* left-to-right (HORIZONTAL) or from top-to-bottom (VERTICAL). The
* other sequence (secondary) will then occupy the remaining
* direction. It has an associated scale which is the number of pixels
* per symbol and applies to both sequences. The leading and trailing
* borders apply to the primary sequence only.
*
* The primary purpose of this component is to provide a means for
* representing graphically comparisons between two sequences. This
* could be anything from traditional dotplots (or variants created
* with lines) to a more complex layered plot involving superimposed
* renderers.
*
* Each sequence has a translation which is the number of
* Symbol
s to skip before rendering starts. In order to
* produce a scrolling effect, the setSymbolTranslation
* or setSecondarySymbolTranslation
method may be hooked
* up to an Adjustable
such as JScrollBar
or
* to an event listener.
*
* The exact number of Symbol
s rendered in each
* sequence depends on the dimensions of the panel and the
* scale. Resizing the panel will cause the number of
* Symbol
s rendered to change accordingly.
*
* The panel will fill its background to the Color
* defined by the setBackground()
method provided that it
* has been defined as opaque using setOpaque()
.
*
* The change event handling code is based on the original panel
* and other BioJava components by Matthew and Thomas.
*
* @author Keith James
* @author Matthew Pocock
* @since 1.2
*/
public class PairwiseSequencePanel extends JComponent
implements PairwiseRenderContext, Changeable, Serializable
{
/**
* Constant RENDERER
is a ChangeType
* which indicates a change to the renderer, requiring a layout
* update.
*/
public static final ChangeType RENDERER =
new ChangeType("The renderer for this PairwiseSequencePanel has changed",
"org.biojava.bio.gui.sequence.PairwiseSequencePanel",
"RENDERER", SequenceRenderContext.LAYOUT);
/**
* Constant TRANSLATION
is a ChangeType
* which indicates a change to the translation, requiring a paint
* update.
*/
public static final ChangeType TRANSLATION =
new ChangeType("The translation for this PairwiseSequencePanel has changed",
"org.biojava.bio.gui.sequence.PairwiseSequencePanel",
"TRANSLATION", SequenceRenderContext.REPAINT);
// The query sequence to be rendered
private Sequence sequence;
// The number of residues to skip before starting to render
private int translation;
// The rendering direction (HORIZONTAL or VERTICAL)
private int direction;
// The subject sequence to be rendered
private Sequence secSequence;
// The number of residues to skip before starting to render
private int secTranslation;
// The rendering direction (HORIZONTAL or VERTICAL)
private int secDirection;
// The rendering scale in pixels per residue
private double scale;
// The homology renderer
private PairwiseSequenceRenderer renderer;
// RenderingHints to be used by renderers
private RenderingHints hints;
// The rendering context borders
private SequenceRenderContext.Border leadingBorder;
private SequenceRenderContext.Border trailingBorder;
// Listens for bound property changes which require a repaint
// afterwards
private PropertyChangeListener propertyListener =
new PropertyChangeListener()
{
public void propertyChange(PropertyChangeEvent pce)
{
repaint();
}
};
// ChangeSupport helper for BioJava ChangeListeners
private transient ChangeSupport changeSupport = null;
// Listens for BioJava changes which will require a repaint
// afterwards
private ChangeListener repaintListener = new ChangeAdapter()
{
public void postChange(ChangeEvent ce)
{
repaint();
}
};
// Listens for BioJava changes which will require a revalidation
// afterwards
private ChangeListener layoutListener = new ChangeAdapter()
{
public void postChange(ChangeEvent ce)
{
revalidate();
}
};
// SequenceViewerSupport helper for BioJava SequenceViewerListeners
private SequenceViewerSupport svSupport = new SequenceViewerSupport();
// Listens for mouse click events
private MouseListener mouseListener = new MouseAdapter()
{
public void mouseClicked(MouseEvent me)
{
if (! isActive())
return;
Insets insets = getInsets();
me.translatePoint(-insets.left, -insets.top);
SequenceViewerEvent sve =
renderer.processMouseEvent(PairwiseSequencePanel.this, me,
new ArrayList());
me.translatePoint(insets.left, insets.top);
svSupport.fireMouseClicked(sve);
}
public void mousePressed(MouseEvent me)
{
if (! isActive())
return;
Insets insets = getInsets();
me.translatePoint(-insets.left, -insets.top);
SequenceViewerEvent sve =
renderer.processMouseEvent(PairwiseSequencePanel.this, me,
new ArrayList());
me.translatePoint(insets.left, insets.top);
svSupport.fireMousePressed(sve);
}
public void mouseReleased(MouseEvent me)
{
if (! isActive())
return;
Insets insets = getInsets();
me.translatePoint(-insets.left, -insets.top);
SequenceViewerEvent sve =
renderer.processMouseEvent(PairwiseSequencePanel.this, me,
new ArrayList());
me.translatePoint(insets.left, insets.top);
svSupport.fireMouseReleased(sve);
}
};
// SequenceViewerMotionSupport helper for BioJava
// SequenceViewerMotionListeners
private SequenceViewerMotionSupport svmSupport =
new SequenceViewerMotionSupport();
// Listens for mouse movement events
private MouseMotionListener mouseMotionListener = new MouseMotionListener()
{
public void mouseDragged(MouseEvent me)
{
if (! isActive())
return;
Insets insets = getInsets();
me.translatePoint(-insets.left, -insets.top);
SequenceViewerEvent sve =
renderer.processMouseEvent(PairwiseSequencePanel.this, me,
new ArrayList());
me.translatePoint(insets.left, insets.top);
svmSupport.fireMouseDragged(sve);
}
public void mouseMoved(MouseEvent me)
{
if (! isActive())
return;
Insets insets = getInsets();
me.translatePoint(-insets.left, -insets.top);
SequenceViewerEvent sve =
renderer.processMouseEvent(PairwiseSequencePanel.this, me,
new ArrayList());
me.translatePoint(insets.left, insets.top);
svmSupport.fireMouseMoved(sve);
}
};
/**
* Creates a new PairwiseSequencePanel
with the
* default settings (primary sequence direction HORIZONTAL, scale
* 10.0 pixels per symbol, symbol translation 0, secondary symbol
* translation 0, leading border 0.0, trailing border 0.0, 12
* point sanserif font).
*/
public PairwiseSequencePanel()
{
super();
// Direction of the primary sequence
direction = HORIZONTAL;
scale = 10.0;
translation = 0;
secTranslation = 0;
leadingBorder = new SequenceRenderContext.Border();
trailingBorder = new SequenceRenderContext.Border();
leadingBorder.setSize(0.0);
trailingBorder.setSize(0.0);
hints = new RenderingHints(null);
this.addPropertyChangeListener(propertyListener);
this.addMouseListener(mouseListener);
this.addMouseMotionListener(mouseMotionListener);
}
/**
* getSequence
returns the entire
* Sequence
currently being rendered.
*
* @return a Sequence
.
*/
public Sequence getSequence()
{
return sequence;
}
/**
* setSequence
sets the Sequence
* to be rendered.
*
* @param sequence a Sequence
.
*/
public void setSequence(Sequence sequence)
{
Sequence prevSequence = sequence;
// Remove out listener from the sequence, if necessary
if (prevSequence != null)
prevSequence.removeChangeListener(layoutListener);
this.sequence = sequence;
// Add our listener to the sequence, if necessary
if (sequence != null)
sequence.addChangeListener(layoutListener);
firePropertyChange("sequence", prevSequence, sequence);
resizeAndValidate();
}
/**
* getSecondarySequence
returns the entire secondary
* Sequence
currently being rendered.
*
* @return a Sequence
.
*/
public Sequence getSecondarySequence()
{
return secSequence;
}
/**
* setSecondarySequence
sets the secondary
* Sequence
to be rendered.
*
* @param sequence a Sequence
.
*/
public void setSecondarySequence(Sequence sequence)
{
Sequence prevSecSequence = secSequence;
// Remove out listener from the sequence, if necessary
if (prevSecSequence != null)
prevSecSequence.removeChangeListener(layoutListener);
secSequence = sequence;
// Add our listener to the sequence, if necessary
if (sequence != null)
sequence.addChangeListener(layoutListener);
firePropertyChange("secSequence", prevSecSequence, sequence);
resizeAndValidate();
}
/**
* getSymbols
returns all of the Symbol
s
* belonging to the currently rendered Sequence
.
*
* @return a SymbolList
.
*/
public SymbolList getSymbols()
{
return sequence;
}
/**
* getSecondarySymbols
returns all of the
* Symbol
s belonging to the currently rendered
* secondary Sequence
.
*
* @return a SymbolList
.
*/
public SymbolList getSecondarySymbols()
{
return secSequence;
}
/**
* getFeatures
returns all of the
* Feature
s belonging to the currently rendered
* Sequence
.
*
* @return a FeatureHolder
.
*/
public FeatureHolder getFeatures()
{
return sequence;
}
/**
* getSecondaryFeatures
returns all of the
* Feature
s belonging to the currently rendered
* secondary Sequence
.
*
* @return a FeatureHolder
.
*/
public FeatureHolder getSecondaryFeatures()
{
return secSequence;
}
/**
* getRange
returns a RangeLocation
* representing the region of the sequence currently being
* rendered. This is calculated from the size of the
* PairwiseSequencePanel
, the current rendering
* translation and the current scale. The value will therefore
* change when the PairwiseSequencePanel
is resized
* or "scrolled" by changing the translation.
*
* @return a RangeLocation
.
*/
public RangeLocation getRange()
{
int visibleSymbols = getVisibleSymbolCount();
// This is a fudge as we have to return a RangeLocation, which
// can not have start == end
if (visibleSymbols == 0)
return new RangeLocation(translation + 1,
translation + 2);
else
return new RangeLocation(translation + 1, visibleSymbols);
}
/**
* getSecondaryRange
returns a
* RangeLocation
representing the region of the
* secondary sequence currently being rendered. This is calculated
* from the size of the PairwiseSequencePanel
, the
* current rendering translation and the current scale. The value
* will therefore change when the
* PairwiseSequencePanel
is resized or "scrolled" by
* changing the translation.
*
* @return a RangeLocation
.
*/
public RangeLocation getSecondaryRange()
{
int visibleSecSymbols = getVisibleSecondarySymbolCount();
// This is a fudge as we have to return a RangeLocation, which
// can not have start == end
if (visibleSecSymbols == 0)
return new RangeLocation(secTranslation + 1,
secTranslation + 2);
else
return new RangeLocation(secTranslation + 1, visibleSecSymbols);
}
/**
* getDirection
returns the direction in which this
* context expects the sequence to be rendered - HORIZONTAL or
* VERTICAL.
*
* @return an int
.
*/
public int getDirection()
{
return direction;
}
/**
* setDirection
sets the direction in which this
* context will render the sequence - HORIZONTAL or VERTICAL.
*
* @param direction an int
.
*
* @exception IllegalArgumentException if an invalid direction is
* used.
*/
public void setDirection(int direction)
throws IllegalArgumentException
{
int prevDirection = direction;
int prevSecDirection = secDirection;
if (direction == HORIZONTAL)
secDirection = VERTICAL;
else if (direction == VERTICAL)
secDirection = HORIZONTAL;
else
throw new IllegalArgumentException("Direction must be either HORIZONTAL or VERTICAL");
this.direction = direction;
firePropertyChange("direction", prevDirection, direction);
firePropertyChange("secDirection", prevSecDirection, secDirection);
resizeAndValidate();
}
/**
* getSecondaryDirection
returns the direction in
* which this context expects the secondary sequence to be
* rendered - HORIZONTAL or VERTICAL.
*
* @return an int
.
*/
public int getSecondaryDirection()
{
return secDirection;
}
/**
* getScale
returns the scale in pixels per
* Symbol
.
*
* @return a double
.
*/
public double getScale()
{
return scale;
}
/**
* setScale
sets the scale in pixels per
* Symbol
.
*
* @param scale a double
.
*/
public void setScale(double scale)
{
double prevScale = this.scale;
this.scale = scale;
firePropertyChange("scale", prevScale, scale);
resizeAndValidate();
}
/**
* getSymbolTranslation
returns the current
* translation in Symbol
s which will be applied when
* rendering. The sequence will be rendered starting at this
* translation. Values may be from 0 to the length of the rendered
* sequence.
*
* @return an int
.
*/
public int getSymbolTranslation()
{
return translation;
}
/**
* setSymbolTranslation
sets the translation in
* Symbol
s which will be applied when rendering. The
* sequence will be rendered starting at that translation. Values
* may be from 0 to the length of the rendered sequence.
*
* @param translation an int
.
*
* @exception IndexOutOfBoundsException if the translation is
* greater than the sequence length.
*/
public void setSymbolTranslation(int translation)
throws IndexOutOfBoundsException
{
if (translation >= sequence.length())
throw new IndexOutOfBoundsException("Tried to set symbol translation offset equal to or greater than SymbolList length");
int prevTranslation = this.translation;
if (hasListeners())
{
ChangeSupport cs = getChangeSupport(TRANSLATION);
ChangeEvent ce = new ChangeEvent(this, TRANSLATION);
cs.firePostChangeEvent(ce);
this.translation = translation;
cs.firePostChangeEvent(ce);
}
else
{
this.translation = translation;
}
firePropertyChange("translation", prevTranslation, translation);
resizeAndValidate();
}
/**
* getSecondarySymbolTranslation
returns the current
* translation in Symbol
s which will be applied when
* rendering. The secondary sequence will be rendered starting at
* this translation. Values may be from 0 to the length of the
* rendered sequence.
*
* @return an int
.
*/
public int getSecondarySymbolTranslation()
{
return secTranslation;
}
/**
* setSecondarySymbolTranslation
sets the translation
* in Symbol
s which will be applied when
* rendering. The secondary sequence will be rendered starting at
* that translation. Values may be from 0 to the length of the
* rendered sequence.
*
* @param translation an int
.
*
* @exception IndexOutOfBoundsException if the translation is
* greater than the sequence length.
*/
public void setSecondarySymbolTranslation(int translation)
throws IndexOutOfBoundsException
{
if (translation >= secSequence.length())
throw new IndexOutOfBoundsException("Tried to set secondary symbol translation offset equal to or greater than SymbolList length");
int prevSecTranslation = secTranslation;
if (hasListeners())
{
ChangeSupport cs = getChangeSupport(TRANSLATION);
ChangeEvent ce = new ChangeEvent(this, TRANSLATION);
cs.firePostChangeEvent(ce);
secTranslation = translation;
cs.firePostChangeEvent(ce);
}
else
{
secTranslation = translation;
}
firePropertyChange("secTranslation", prevSecTranslation, translation);
resizeAndValidate();
}
/**
* getLeadingBorder
returns the leading border of the
* primary sequence.
*
* @return a SequenceRenderContext.Border
.
*/
public SequenceRenderContext.Border getLeadingBorder()
{
return leadingBorder;
}
/**
* getTrailingBorder
returns the trailing border of
* the primary sequence.
*
* @return a SequenceRenderContext.Border
.
*/
public SequenceRenderContext.Border getTrailingBorder()
{
return trailingBorder;
}
/**
* getRenderer
returns the current
* PairwiseSequenceRenderer
.
*
* @return a PairwiseSequenceRenderer
.
*/
public PairwiseSequenceRenderer getRenderer()
{
return renderer;
}
/**
* setRenderer
sets the current
* PairwiseSequenceRenderer
.
*/
public void setRenderer(PairwiseSequenceRenderer renderer)
throws ChangeVetoException
{
if (hasListeners())
{
ChangeSupport cs = getChangeSupport(RENDERER);
// We are originating a change event so use this
// constructor
ChangeEvent ce = new ChangeEvent(this, RENDERER,
renderer, this.renderer);
synchronized(cs)
{
cs.firePreChangeEvent(ce);
_setRenderer(renderer);
cs.firePostChangeEvent(ce);
}
}
else
{
_setRenderer(renderer);
}
resizeAndValidate();
}
/**
* getRenderingHints
returns the
* RenderingHints
currently being used by the
* Graphics2D
instances of delegate renderers. If
* none is set, the constructor creates one with a null
* Map
.
*
* @return a RenderingHints
.
*/
public RenderingHints getRenderingHints()
{
return hints;
}
/**
* setRenderingHints
sets the
* RenderingHints
which will be used by the
* Graphics2D
instances of delegate renderers.
*
* @param hints a RenderingHints
.
*/
public void setRenderingHints(RenderingHints hints)
{
RenderingHints prevHints = this.hints;
this.hints = hints;
firePropertyChange("hints", prevHints, hints);
}
/**
* sequenceToGraphics
converts a sequence index
* to a graphical position.
*
* @param sequencePos an int
.
*
* @return a double
.
*/
public double sequenceToGraphics(int sequencePos)
{
return (sequencePos - translation - 1) * scale;
}
/**
* secondarySequenceToGraphics
converts a sequence
* index to a graphical position.
*
* @param sequencePos an int
.
*
* @return a double
.
*/
public double secondarySequenceToGraphics(int sequencePos)
{
return (sequencePos - secTranslation - 1) * scale;
}
/**
* graphicsToSequence
converts a graphical position
* to a sequence index.
*
* @param graphicsPos a double
.
*
* @return an int
.
*/
public int graphicsToSequence(double graphicsPos)
{
return ((int) (graphicsPos / scale)) + translation + 1;
}
/**
* graphicsToSequence
converts a graphical position
* to a sequence index.
*
* @param point a graphic position.
*
* @return an int
.
*/
public int graphicsToSequence(Point2D point)
{
if (direction == HORIZONTAL)
return graphicsToSequence(point.getX());
else
return graphicsToSequence(point.getY());
}
/**
* graphicsToSecondarySequence
converts a graphical
* position to a secondary sequence index.
*
* @param graphicsPos a double
.
*
* @return an int
.
*/
public int graphicsToSecondarySequence(double graphicsPos)
{
return ((int) (graphicsPos / scale)) + secTranslation + 1;
}
/**
* graphicsToSecondarySequence
converts a graphical
* position to a secondary sequence index.
*
* @param point a Point
.
*
* @return an int
.
*/
public int graphicsToSecondarySequence(Point point)
{
if (secDirection == HORIZONTAL)
return graphicsToSecondarySequence(point.getX());
else
return graphicsToSecondarySequence(point.getY());
}
/**
* getVisibleSymbolCount
returns the
* maximum number of Symbol
s which
* can be rendered in the visible area (excluding all borders) of
* the PairwiseSequencePanel
at the current
* scale. Note that if the translation is greater than 0, the
* actual number of Symbol
s rendered will be less.
*
* @return an int
.
*/
public int getVisibleSymbolCount()
{
// The Insets
Insets insets = getInsets();
int visible;
if (direction == HORIZONTAL)
visible = getWidth() - insets.left - insets.right;
else
visible = getHeight() - insets.top - insets.bottom;
return Math.min(graphicsToSequence(visible), sequence.length());
}
/**
* getVisibleSecondarySymbolCount
returns the
* maximum number of secondary
* Symbol
s which can be rendered in the visible area
* (excluding all borders) of the
* PairwiseSequencePanel
at the current scale. Note
* that if the translation is greater than 0, the actual number of
* Symbol
s rendered will be less.
*
* @return an int
.
*/
public int getVisibleSecondarySymbolCount()
{
// The Insets
Insets insets = getInsets();
int visible;
if (secDirection == HORIZONTAL)
visible = getWidth() - insets.left - insets.right;
else
visible = getHeight() - insets.top - insets.bottom;
return Math.min(graphicsToSecondarySequence(visible),
secSequence.length());
}
public void paintComponent(Graphics g)
{
if (! isActive())
return;
super.paintComponent(g);
// Set hints
Graphics2D g2 = (Graphics2D) g;
g2.addRenderingHints(hints);
// As we subclass JComponent we have to paint our own
// background, but only if we are opaque
if (isOpaque())
{
g2.setPaint(getBackground());
g2.fillRect(0, 0, getWidth(), getHeight());
}
// Save current transform and clip
AffineTransform prevTransform = g2.getTransform();
Shape prevClip = g2.getClip();
Insets insets = getInsets();
Rectangle2D.Double clip = new Rectangle2D.Double();
clip.x = 0.0;
clip.y = 0.0;
if (direction == HORIZONTAL)
{
clip.width = sequenceToGraphics(getVisibleSymbolCount() + 1);
clip.height = secondarySequenceToGraphics(getVisibleSecondarySymbolCount() + 1);
g2.translate(leadingBorder.getSize() + insets.left, insets.top);
}
else
{
clip.width = secondarySequenceToGraphics(getVisibleSecondarySymbolCount() + 1);
clip.height = sequenceToGraphics(getVisibleSymbolCount() + 1);
g2.translate(insets.left, leadingBorder.getSize() + insets.top);
}
// Clip and paint
g2.clip(clip);
renderer.paint(g2, this);
// Restore
g2.setTransform(prevTransform);
g2.setClip(prevClip);
}
/**
* resizeAndValidate
sets the minimum, preferred and
* maximum sizes of the component according to the current visible
* symbol count.
*/
public void resizeAndValidate()
{
Dimension d = null;
if (! isActive())
{
d = new Dimension(0, 0);
}
else
{
double width;
double height;
if (direction == HORIZONTAL)
{
width = sequenceToGraphics(getVisibleSymbolCount());
height = secondarySequenceToGraphics(getVisibleSecondarySymbolCount());
}
else
{
width = secondarySequenceToGraphics(getVisibleSecondarySymbolCount());
height = sequenceToGraphics(getVisibleSymbolCount());
}
d = new Dimension((int) Math.ceil(width),
(int) Math.ceil(height));
}
setMinimumSize(d);
setPreferredSize(d);
setMaximumSize(d);
revalidate();
}
/**
* addChangeListener
adds a listener for all types of
* change.
*
* @param cl a ChangeListener
.
*/
public void addChangeListener(ChangeListener cl)
{
addChangeListener(cl, ChangeType.UNKNOWN);
}
/**
* addChangeListener
adds a listener for specific
* types of change.
*
* @param cl a ChangeListener
.
* @param ct a ChangeType
.
*/
public void addChangeListener(ChangeListener cl, ChangeType ct)
{
ChangeSupport cs = getChangeSupport(ct);
cs.addChangeListener(cl, ct);
}
/**
* removeChangeListener
removes a listener.
*
* @param cl a ChangeListener
.
*/
public void removeChangeListener(ChangeListener cl)
{
removeChangeListener(cl, ChangeType.UNKNOWN);
}
/**
* removeChangeListener
removes a listener.
*
* @param cl a ChangeListener
.
* @param ct a ChangeType
.
*/
public void removeChangeListener(ChangeListener cl, ChangeType ct)
{
if(hasListeners()) {
ChangeSupport cs = getChangeSupport(ct);
cs.removeChangeListener(cl);
}
}
public boolean isUnchanging(ChangeType ct) {
ChangeSupport cs = getChangeSupport(ct);
return cs.isUnchanging(ct);
}
/**
* addSequenceViewerListener
adds a listener for
* mouse click SequenceViewerEvent
s.
*
* @param svl a SequenceViewerListener
.
*/
public void addSequenceViewerListener(SequenceViewerListener svl)
{
svSupport.addSequenceViewerListener(svl);
}
/**
* removeSequenceViewerListener
removes a listener
* for mouse click SequenceViewerEvent
s.
*
* @param svl a SequenceViewerListener
.
*/
public void removeSequenceViewerListener(SequenceViewerListener svl)
{
svSupport.removeSequenceViewerListener(svl);
}
/**
* addSequenceViewerMotionListener
adds a listener for
* mouse motion SequenceViewerEvent
s.
*
* @param svml a SequenceViewerMotionListener
.
*/
public void addSequenceViewerMotionListener(SequenceViewerMotionListener svml)
{
svmSupport.addSequenceViewerMotionListener(svml);
}
/**
* addSequenceViewerMotionListener
removes a listener for
* mouse motion SequenceViewerEvent
s.
*
* @param svml a SequenceViewerMotionListener
.
*/
public void removeSequenceViewerMotionListener(SequenceViewerMotionListener svml)
{
svmSupport.removeSequenceViewerMotionListener(svml);
}
/**
* getChangeSupport
lazily instantiates a helper for
* change listeners.
*
* @param ct a ChangeType
.
*
* @return a ChangeSupport
object.
*/
protected ChangeSupport getChangeSupport(ChangeType ct)
{
if(changeSupport != null) {
return changeSupport;
}
synchronized(this) {
if (changeSupport == null) {
changeSupport = new ChangeSupport();
}
return changeSupport;
}
}
/**
* hasListeners
returns true if there are active
* listeners for BioJava events.
*
* @return a boolean
value.
*/
protected boolean hasListeners()
{
return changeSupport != null;
}
/**
* isActive
returns true if both the
* Sequence
s to be rendered and the
* PairwiseHomologyRenderer
are not null.
*
* @return a boolean
value.
*/
protected boolean isActive()
{
return (sequence != null) && (secSequence != null) &&
(renderer != null);
}
private void _setRenderer(PairwiseSequenceRenderer renderer)
{
// Remove our listeners from the old renderer, if necessary
if (this.renderer != null &&
Changeable.class.isInstance(this.renderer))
{
Changeable c = (Changeable) this.renderer;
c.removeChangeListener(layoutListener, SequenceRenderContext.LAYOUT);
c.removeChangeListener(repaintListener, SequenceRenderContext.REPAINT);
}
this.renderer = renderer;
// Add our listeners to the new renderer, if necessary
if (renderer != null &&
Changeable.class.isInstance(renderer))
{
Changeable c = (Changeable) renderer;
c.addChangeListener(layoutListener, SequenceRenderContext.LAYOUT);
c.addChangeListener(repaintListener, SequenceRenderContext.REPAINT);
}
}
}