//=====================================================================
// File: Thumbnail.java
// Class: Thumbnail
// Package: AFLPgui
//
// Author: James J. Benham
// Date: August 12, 1998
// Contact: james_benham@hmc.edu
//
// Genographer v1.0 - Computer assisted scoring of gels.
// Copyright (C) 1998 Montana State University
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; version 2
// of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// The GNU General Public License is distributed in the file GPL
//=====================================================================
package AFLPgui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Label;
import java.awt.Panel;
import java.awt.PrintGraphics;
import java.awt.ScrollPane;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import AFLPcore.Bin;
import AFLPcore.Cutoff;
import AFLPcore.CutoffFunction;
import AFLPcore.DataList;
import AFLPcore.FeatureList;
import AFLPcore.Lane;
import AFLPcore.LinearCutoff;
import AFLPcore.Peak;
import AFLPcore.ProgOptions;
import AFLPcore.Option;
import AFLPcore.ScoreFunction;
import AFLPcore.ScoringFailure;
/**
* This class displays thumbnails for a bin. A thumbnail is a trace of
* a lane, but the trace is not the full trace. Instead, it is only the
* portion of the lane that is included in the bin. Each thumbnail shows
* the lane number and the scoring label as well as the trace. The
* thumbnails have a set width. The width of the display area is set
* not to exceed the the size avaiable. If all of the thumbnails do not
* fit on one row, another row will be made. There is no limit on the
* number of rows. A CutoffSlider
is provided to adjust
* the cutoff levels. Buttons allow the user to move to the next or previous
* bin, as well as add a cutoff level and show a dialog to manipulate the
* cutoffs. A button also changes the scoring to the opposite of it's current
* setting. This is useful if one has two parents, and wants to keep the
* scoring consistant. The class can be drawn to a printer page by
* setting the width with setViewWidth
and paint
.
* The paint method will recognize the print request and behave appropriately.
* Normally the display is double-buffered, but this is disabled when
* drawing to a printed page (PrinteGraphics
). The scoring can
* also be adjusted manually by clicking on the trace.
*
* @see CutoffSlider
*
* @author James J. Benham
* @version 1.0.0
* @date August 12, 1998
*/
public class Thumbnail extends Panel implements ActionListener,
MouseListener,
MouseMotionListener
{
// Error Message Location
private static int LABEL_HORZ = 20;
private static int LABEL_VERT = 20;
// Info Bar component parameters
private static int LABEL_H_INSET = 5;
private static int LABEL_V_INSET = 3;
private static int LABEL_WIDTH = 600;
private static int LABEL_HEIGHT = 18;
// Button bar constants
private static int HORZ_SPACE = 5;
// Files for images
private static String REL_PATH = "";
private static String INVERT_FILE = "invert.gif";
private static String PREVIOUS_FILE = "l_arrow.gif";
private static String NEXT_FILE = "r_arrow.gif";
private static String ADDCT_FILE = "plus.gif";
private static String CTDIALOG_FILE = "cutoff.gif";
private static String BIN_FILE = "bin.gif";
// State of mouse position
public static final int BLANK = 0;
public static final int TRACE = 1;
// Double buffering stuff
private Dimension offDimension;
private Image offImage;
private Graphics offGraphics;
private DataList lanes;
private DataList bins;
private ButtonBar buttonBar;
private Bar infoBar;
private Label infoLabel;
// Button Bar components
ImgButton invertButton;
ImgButton previousButton;
ImgButton nextButton;
ImgButton addCutoffButton;
ImgButton cutoffDialogButton;
CutoffDialog ctDialog;
private CutoffSlider slider;
private Bin currentBin;
private int height;
private int width;
private int hspace;
private int vspace;
private int sliderSpace;
private int scoreWidth;
// variables describing the layout
private int printViewWidth;
private int numColumns;
private int numRows;
// variables to track mouse location
private int overType;
private double size;
private double intensity;
private int laneNum;
private double minSize;
private double maxSize;
private double hScale;
private double vScale;
private double maxIntensity;
// the main program window
private ScrollPane parent;
private Frame topWindow;
/**
* Creates thumbnails with the specified parameters.
*
* @param lanes the lanes to produce thumbnails for
* @param bins the bins defined in the gel.
* @param parentWindow a windows used as the owner of various dialog boxes.
*/
public Thumbnail(DataList lanes, DataList bins, Frame parentWindow)
{
this.lanes = lanes;
this.bins = bins;
topWindow = parentWindow;
height = 61;
width = 26;
hspace = 3;
vspace = 13;
sliderSpace = 22;
scoreWidth = 8;
setSize(500, 500);
setLayout(null);
// set for no thumbnails
minSize = 0;
maxSize = -1;
currentBin = null;
addMouseListener(this);
addMouseMotionListener(this);
// add the slider control
slider = new CutoffSlider();
slider.addMouseListener(this);
slider.addMouseMotionListener(this);
add(slider);
slider.setBounds(0, vspace, sliderSpace, height);
ctDialog = new CutoffDialog(topWindow, "Cutoffs...", true);
createButtonBar();
createInfoBar();
}
/**
* Performs calculations neccessary to display the thumbnails. It
* finds the horizontal and vertical scales used to convert between
* pixels and base pairs, initializes the slider control with the
* correct values, scores the bin if neccessary, and ensures that
* only one cutoff appears in the bin. This is accomplished by applying
* the cutoff with the start position closest to the top of the bin
* to the entire bin. (The top has a larger size in bp.)
*/
public void init()
{
//=================== find the max intensity=========
maxIntensity = 0;
double tempIntensity;
for(int i=0; i < lanes.size(); i++)
{
tempIntensity = ((Lane) lanes.dataAt(i)).getMaxHeight(minSize,
maxSize);
if(tempIntensity > maxIntensity)
maxIntensity = tempIntensity;
}
//=================== find the scales================
hScale = (maxSize - minSize)/(width - 3); // -3 for border
vScale = (height - 1)/maxIntensity;
slider.setScale(vScale);
slider.setSize(minSize);
// fix anything that has multiple cutoffs in the bin by taking the
// largest one and applying it to the bin.
Cutoff ct;
Lane ln;
for(int i=0; i < lanes.size(); i++)
{
ln = (Lane) lanes.dataAt(i);
ct = ln.cutoffUnder(maxSize);
// Look out for errors, like MaxSize = -1, if no bin, so
// ct will be null. In that case, don't worryabout this stuff
if( (ct != null) && (ct.getStartPos() > minSize))
{
// make this cutoff cover the whole bin.
// first delete anything between it and the edge of the bin
double pos;
Cutoff temp = ln.cutoffUnder(ct.getStartPos() - 0.00000001);
if(temp != null)
pos = temp.getStartPos();
else
pos = -1;
while(pos >= minSize)
{
ln.getCutoffs().removeElement(temp);
// find the next one, subtract a little number to avoid
// equailty
temp = ln.cutoffUnder(pos - 0.00000001);
if(temp != null)
pos = temp.getStartPos();
else
pos = -1;
}
//Now adjust the cutoff.
ct.setStartPos(minSize);
}
}
// set the slider stuff
// if we don't have any lanes, then we won't be displaying anything
// anyway, so don't worry about that case.
if(lanes.size() > 0)
{
slider.setCutoff( ((Lane)lanes.dataAt(0)).cutoffUnder(maxSize));
slider.setLanes(lanes);
}
slider.refresh();
//=====score the bin initially================
// see if it is neccessary first, but check to see that it exists first
// It is possible that no bin is selected,
if(currentBin != null)
if(!currentBin.isScored())
{
try{
currentBin.score(lanes);
} catch(ScoringFailure error){
handleScoreError(error);
}
}
}
/**
* Draws the thumbnails. It will automatically adjust it's width to
* the correct size if a PrintGraphics
is supplied. Normally
* the display is double-buffered, but not when drawing to a printer page.
*/
public void paint(Graphics g)
{
//==================Set the size====================
// it differs for printing and on screen stuff
int viewWidth = 0;
if(g instanceof PrintGraphics)
{
viewWidth = printViewWidth;
}
else
{
// Find out how big the parent is, and check for the scroll bar inset
parent = (ScrollPane) getParent();
Dimension pSize = parent.getViewportSize();
//Dimension pSize = parent.getSize();
viewWidth = pSize.width - parent.getVScrollbarWidth();
}
int numToDraw = lanes.size();
numColumns = (viewWidth - sliderSpace)/(width + hspace);
numRows = (int) Math.ceil(numToDraw/(double)numColumns);
// adjust the size to the correct length
setSize(viewWidth, (vspace + height)*numRows + vspace);
// some of this stuff doesn't need to occur when printing, like
// double buffering.
if(g instanceof PrintGraphics)
{
offGraphics = g;
}
else
{
//=======================Off screen buffer setup==========
// make sure we have the offscreen buffer and that it is the right
// size.
Dimension d = getSize();
if ( (offGraphics == null)
|| (d.width != offDimension.width)
|| (d.height != offDimension.height) )
{
offDimension = d;
offImage = createImage(d.width, d.height);
offGraphics = offImage.getGraphics();
}
}
// check for a valid range. If there isn't one, we have a problem,
// probbly that there is no data. Draw to the graphics since after
// the error, we won't flip the backbuffer to the front.
if(maxSize < minSize)
{
slider.setVisible(false);
g.clearRect(0, 0, getSize().width, getSize().height);
if(maxSize == -1)
{
g.drawString("No Bin Selected. Thumbnails unavailable.",
LABEL_HORZ, LABEL_VERT);
}
else
{
g.drawString("Invalid size range.",
LABEL_HORZ, LABEL_VERT);
}
return; // get out of here
}
// clear the screen
offGraphics.clearRect(0, 0, getSize().width, getSize().height);
slider.setVisible(true);
for(int row = 0; row < numRows; row++)
{
for(int column =0; column < numColumns; column++)
{
numToDraw--;
// check to see if we're done
if(numToDraw < 0)
break;
drawTrace(offGraphics,
(Lane) lanes.dataAt(row*numColumns + column),
sliderSpace + hspace + column*(width + hspace),
vspace + row*(height + vspace));
}
}
if(!(g instanceof PrintGraphics))
g.drawImage(offImage, 0, 0, this);
// let the parent container know about the size change
// WARNING: This seems to cause terrible flickering as the
// image is constantly redrawn, so don't use it.
//parent.validate();
}
/**
* Draws the trace portion of the thumbnail. It uses the scales set
* by inti
. The trace will be drawn in the same color
* as the signal for the lane. It also draws the border box and
* adds the lane number and score label for the lane to the top
* of the box, as well as the cutoff for the lane. If the cutoff is
* a line (type LinearCutoff
then a straight line is
* drawn. Otherwise, the CutoffFunction is drawn by playing connect
* the dots.
*
* @param g the graphics to draw on
* @param lane the lane to display in the thumbnail
* @param x the top-left position of the thumbnail.
* @param y the top left position of the thumbnail.
*/
public void drawTrace(Graphics g, Lane lane, int x, int y)
{
//=================== draw the trace================
double size = minSize;
int previousIntensity = y + height - 1 - (int)(vScale*
lane.getHeightAt(minSize));
int intensity;
// draw the box and label.
g.setColor(Color.black);
g.drawRect(x, y, width, height);
g.drawString("" + lane.getLaneNumber(), x, y - 1);
g.drawString(currentBin.getScore(lane), x + width - scoreWidth, y - 1);
// find the color and set it
switch(lane.getColor())
{
case Lane.RED:
g.setColor(Color.red);
break;
case Lane.GREEN:
g.setColor(Color.green);
break;
case Lane.BLUE:
g.setColor(Color.blue);
break;
case Lane.YELLOW:
g.setColor(Color.yellow);
break;
}
// draw the actual trace
for(int i=1; i < (width - 1); i++)
{
// find the height, -1 to place it above the border.
intensity = y + height - 1 - (int) (vScale * lane.getHeightAt(size));
g.drawLine(x + i, previousIntensity, x + i + 1, intensity);
previousIntensity = intensity;
size += hScale;
}
//================Draw the cutoff functions============
g.setColor(Color.lightGray);
Cutoff ct = lane.cutoffUnder(maxSize);
// see if we only have one cutoff.
if(ct.getStartPos() > minSize)
System.err.println("Cutoff does not span bin!");
int numLevels = ct.getNumLevels();
for(int i=0; i < numLevels; i++)
{
if(ct.getCutoffFunction(i) instanceof LinearCutoff)
{
intensity = y + height - 1 - (int) (vScale *
ct.getCutoff(minSize, i));
g.drawLine(x + 1, intensity, x + width - 1, intensity);
}
else
{
size = minSize;
previousIntensity = (y + height - 1 -
(int) (vScale * ct.getCutoff(minSize, i)));
for(int j=1; j < (width - 1); j++)
{
// find the height, -1 to place it above the border.
intensity = y + height - 1 - (int) (vScale *
ct.getCutoff(size, i));
g.drawLine(x + j, previousIntensity, x + j + 1, intensity);
previousIntensity = intensity;
size += hScale;
}
}
}
}
/**
* Updates the display and is called by java. It simply calls paint.
*/
public void update(Graphics g)
{
paint(g);
}
/**
* Gives the ButtonBar associated with the thumbnails.
*
* @return the button bar.
*/
public ButtonBar getButtonBar()
{
return buttonBar;
}
/**
* Changes the scoring of the specified lane to the one that would
* result if there was a peak at the specified size and intensity.
* The bin's overall scoring is updated after this. The scoring that
* would result is assumed to be the label that has an index that
* is related to the cutoffs. This may not be true for all scoring
* functions, but it works for all of the ones at this time.
* If will adjust the confidence of the peaks so that peaks that should
* be ignored have a confidence of 0, and those that should be included
* again have a confidence of 1.
*
* @param size the size in bp used to determine the label.
* @param intensity the height used to determine the labe.
* @param ln the lane whose scoring is to be changed.
*/
private void changeScore(double size, double intensity, Lane ln)
{
boolean found = false; // used to tell when a label has been found
int cutoffIndex = 0; // tells which cutoff we're interested in
// find the cutoff, do to init(), there should only be one under
// maxSize that applies to the bin.
Cutoff ct = ln.cutoffUnder(maxSize);
String choices[];
choices = currentBin.getScoreMethod().getChoices(ct.getNumLevels());
// Go through all of the levels and find one that works
for(int i=0; i < ct.getNumLevels(); i++)
{
if(intensity >= ct.getCutoff(size, i))
{
currentBin.setScore(ln, choices[i]);
found = true;
cutoffIndex = i;
break;
}
}
// We're not above any of the cutoffs, so go with the last choice
if(!found)
{
currentBin.setScore(ln, choices[ct.getNumLevels()]);
cutoffIndex = ct.getNumLevels();
}
//===================modify the peaks ===============
// set the confidence of those above the level to 0 (marked peaks only)
// But if the peak is in the correct region and marked, set the
// confidence to 1.
// This is a signal to the scoring function to include or not
// include them.
DataList peaks = ln.getPeaksInRange(minSize, maxSize);
Peak pk;
// Look for the cutoff above this one and set the maxHeight;
double maxHeight;
if(cutoffIndex == 0)
maxHeight = 9E99; // set to a very large value
else
maxHeight = ct.getCutoff(size, cutoffIndex - 1);
// see if we are on the last one, if so, the minimum is 0
double minHeight;
if(cutoffIndex == ct.getNumLevels())
minHeight = 0;
else
minHeight = ct.getCutoff(size, cutoffIndex);
for(int i=0; i < peaks.size(); i++)
{
pk = (Peak) peaks.dataAt(i);
if( (pk.getHeight() >= minHeight) && pk.isMarked())
{
if( pk.getHeight() < maxHeight)
{
pk.setConfidence(1);
}
else
pk.setConfidence(0);
}
}
// update the overall info
try{
currentBin.scoreOverall(lanes);
} catch(ScoringFailure error) {
handleScoreError(error);
}
// Show the changes on the screen
infoLabel.setText((currentBin.getScoreInfo())[0]);
refresh();
}
/**
* Shows an options dialog if an error occurs when scoring a bin.
* The most likely reason is that the number of cutoffs do not match
* the number expected by the scoring function. The options are used
* to adjust the scoring function.
*
* @param error the orignal failure
*/
protected void handleScoreError(ScoringFailure error)
{
ErrorDialog errorDialog = new ErrorDialog(topWindow);
errorDialog.showError(error);
// try setting the number of levels for the scoring method
ScoreFunction scoreFn = currentBin.getScoreMethod();
Option opts[] = scoreFn.getOptions();
// find out the number of levels we have, try using the first lane.
Cutoff ct = new Cutoff(minSize, 2);
if(!lanes.isEmpty())
ct = ((Lane) lanes.dataAt(0)).cutoffUnder(maxSize);
// see if the first option is a number
if(opts[0].getType() != Option.NUMBER)
throw new IllegalArgumentException("Error in scoring method " +
scoreFn.getName() + ". First " +
"option should be number of levels"
+ " expected.");
opts[0].setValue((double) ct.getNumLevels() + 1);
scoreFn.setOptions(opts);
// show the options again.
OptionDialog optD = new OptionDialog(scoreFn.getOptions(),
topWindow,
scoreFn.getName() + " Parameters");
optD.setVisible(true);
if(!optD.isCanceled())
{
opts = optD.getOptions();
scoreFn.setOptions(opts);
}
}
/**
* Lays out the components on the button bar, uses the constants declared
* in this class to control the layout.
*/
private void createButtonBar()
{
buttonBar = new ButtonBar();
buttonBar.setBounds(0, 0, 640, 32);
// Let the parent window handle the action event for the orignal
// button bar components
buttonBar.sendActionEventsTo( (ActionListener) topWindow);
//============Create the invert button================
invertButton = new ImgButton(ButtonBar.retrieveImage(ProgOptions.homePath +
REL_PATH +
INVERT_FILE));
buttonBar.add(invertButton);
int startX = buttonBar.getFreeHorzPos();
invertButton.setBounds(startX + HORZ_SPACE, ButtonBar.VERT_INSET,
ButtonBar.BUTTON_WIDTH, ButtonBar.BUTTON_HEIGHT);
startX += ButtonBar.BUTTON_WIDTH + HORZ_SPACE;
invertButton.addActionListener(this);
//===========Create the previous button================
previousButton = new ImgButton(ButtonBar.retrieveImage(ProgOptions.homePath
+ REL_PATH +
PREVIOUS_FILE));
buttonBar.add(previousButton);
previousButton.setBounds(startX + HORZ_SPACE, ButtonBar.VERT_INSET,
ButtonBar.BUTTON_WIDTH, ButtonBar.BUTTON_HEIGHT);
startX += ButtonBar.BUTTON_WIDTH + HORZ_SPACE;
previousButton.addActionListener(this);
//===========Create the next button====================
nextButton = new ImgButton(ButtonBar.retrieveImage(ProgOptions.homePath +
REL_PATH +
NEXT_FILE));
buttonBar.add(nextButton);
nextButton.setBounds(startX, ButtonBar.VERT_INSET,
ButtonBar.BUTTON_WIDTH, ButtonBar.BUTTON_HEIGHT);
startX += ButtonBar.BUTTON_WIDTH;
nextButton.addActionListener(this);
//===========Create the addCutoff button====================
String imgFileLoc = ProgOptions.homePath + REL_PATH + ADDCT_FILE;
addCutoffButton = new ImgButton(ButtonBar.retrieveImage(imgFileLoc));
buttonBar.add(addCutoffButton);
addCutoffButton.setBounds(startX + HORZ_SPACE, ButtonBar.VERT_INSET,
ButtonBar.BUTTON_WIDTH, ButtonBar.BUTTON_HEIGHT);
startX += ButtonBar.BUTTON_WIDTH + HORZ_SPACE;
addCutoffButton.addActionListener(this);
//===========Create the cutoff dialog button===============
cutoffDialogButton =
new ImgButton(ButtonBar.retrieveImage(ProgOptions.homePath +
REL_PATH + CTDIALOG_FILE));
buttonBar.add(cutoffDialogButton);
cutoffDialogButton.setBounds(startX + HORZ_SPACE, ButtonBar.VERT_INSET,
ButtonBar.BUTTON_WIDTH,
ButtonBar.BUTTON_HEIGHT);
startX += ButtonBar.BUTTON_WIDTH + HORZ_SPACE;
cutoffDialogButton.addActionListener(this);
}
/**
* Lays out the components for the info bar. Layout is controlled with
* constants in this class.
*/
private void createInfoBar()
{
// create the label
infoLabel = new Label("Thumbnail info.....");
// create the bar, no bottom border.
infoBar = new Bar(true, false);
infoBar.setLayout(null);
infoBar.add(infoLabel);
infoLabel.setBounds(LABEL_H_INSET, LABEL_V_INSET,
LABEL_WIDTH, LABEL_HEIGHT);
infoBar.setBounds(0, 0, 600, FragmentMap.BAR_HEIGHT);
}
/**
* Returns a bar that displays information about the thumbnails. This object
* should be displayed somewhere by the container of this object.
*
* @return the bar as described above
*/
public Bar getInfoBar()
{
return infoBar;
}
/**
* Handles the buttons in the button bar.
*/
public void actionPerformed(ActionEvent e)
{
if(e.getSource() == invertButton)
{
if(currentBin != null)
{
currentBin.getScoreMethod().invert();
try{
currentBin.score(lanes);
} catch(ScoringFailure error){
handleScoreError(error);
}
refresh();
}
}
else if(e.getSource() == previousButton)
{
// find the location of this bin in the list
int location = bins.find(currentBin.getSearchKey()).location;
// now move to the previous one in the list
if(location <= 0)
infoLabel.setText("Already on first bin.");
else
{
Bin b = (Bin) bins.dataAt(location - 1);
setBin(b);
init();
infoLabel.setText("Bin number " + (location));
refresh();
}
}
else if(e.getSource() == nextButton)
{
// find the location of this bin in the list
int location = bins.find(currentBin.getSearchKey()).location;
// now move to the previous one in the list
if(location == (bins.size() - 1))
infoLabel.setText("Already on last bin.");
else
{
Bin b = (Bin) bins.dataAt(location + 1);
setBin(b);
init();
infoLabel.setText("Bin number " + (location + 2));
refresh();
}
}
else if(e.getSource() == addCutoffButton)
{
// Make a new function, set at half height
CutoffFunction newFunction;
newFunction = (CutoffFunction) FeatureList.getCutoffMgr().getDefault();
newFunction = (CutoffFunction) newFunction.clone();
Option opts[] = newFunction.getOptions();
opts[0].setValue(maxIntensity/2);
newFunction.setOptions(opts);
slider.addCutoffFunction(newFunction);
// rescore the bin
try{
currentBin.score(lanes);
} catch(ScoringFailure error) {
handleScoreError(error);
}
refresh();
}
else if(e.getSource() == cutoffDialogButton)
{
ctDialog.init(((Lane) lanes.dataAt(0)).cutoffUnder(maxSize),
lanes, minSize);
ctDialog.setVisible(true);
slider.setCutoff(((Lane) lanes.dataAt(0)).cutoffUnder(maxSize));
slider.refresh();
refresh();
}
}
/**
* Adjusts the scoring for a lane if the mouse is over the trace.
*/
public void mouseClicked(MouseEvent e)
{
if(e.getSource() == slider)
{
try{
currentBin.score(lanes);
}catch(ScoringFailure error){
handleScoreError(error);
}
refresh();
}
if(overType == TRACE)
{
changeScore(size, intensity, (Lane) lanes.dataAt(laneNum));
}
}
/**
* Rescores the bin if the mouse was on the slider.
*/
public void mouseReleased(MouseEvent e)
{
if(e.getSource() == slider)
{
try{
currentBin.score(lanes);
}catch(ScoringFailure error){
handleScoreError(error);
}
refresh();
}
}
/**
* Refreshes the display if the mouse is on the slider. Since the cutoffs
* are changing, the display needs to be updated.
*/
public void mouseDragged(MouseEvent e)
{
if(e.getSource() == slider)
{
infoLabel.setText(slider.getMessage());
refresh();
}
}
/**
* Determines what the mouse is over and displays info in the
* info bar.
*/
public void mouseMoved(MouseEvent e)
{
if(currentBin != null)
{
overType = findPositionInfo(e.getX(), e.getY());
if(e.getSource() == slider)
infoLabel.setText(slider.getMessage());
else
switch(overType)
{
case TRACE:
infoLabel.setText((currentBin.getScoreInfo())[0] +
" Size: " + size + " Intensity: " + intensity);
break;
case BLANK:
infoLabel.setText((currentBin.getScoreInfo())[0]);
break;
}
}
}
/**
* Sets the lanes for which to display thumbnails to the specified
* value.
*
* @param lanes a list of lanes to display.
*/
public void setLanes(DataList lanes)
{
this.lanes = lanes;
}
/**
* Gives the lanes that are being displayed.
*
* @return the lanes
*
* @see Thumbnail#setLanes
*/
public DataList getLanes()
{
return lanes;
}
/**
* Sets the width of the display area. This will only be used when
* the thumbnails are printed, otherwise this class will look
* up the size of the screen, which can change and use that as
* the view width.
*
* @param width the width of the page.
*/
public void setViewWidth(int width)
{
printViewWidth = width;
}
/**
* Sets the bin for the thumbnails to the specified one. The bin is
* required for the thumbnails to display. This method sets the range
* of the thumbnails to that of the bin
*
* @param b the bin whose thumbnails to dislay.
*/
public void setBin(Bin b)
{
currentBin = b;
if(b != null)
{
minSize = b.getLocation() - b.getRange();
maxSize = b.getLocation() + b.getRange();
}
else
{
minSize = 0;
maxSize = -1;
}
}
/**
* Gives the bin displayed. This can be changed from the one initially
* set when the thumbnail moves to the next or previous bin.
*
* @return the bin.
*/
public Bin getBin()
{
return currentBin;
}
/**
* Gives the size of an individual thumbnail, including the border around
* it.
*
* @return an container for both the widht and height
*/
public Dimension getThumbnailSize()
{
return new Dimension(width + hspace, height + vspace);
}
/**
* Sets the bins for the thumbnail. These are provided so that the
* thumbnail can move to the next or previous bin.
*
* @param bins the bins in the gel
*/
public void setBinList(DataList bins)
{
this.bins = bins;
}
/**
* Gives the size and intensity for the given point. It will also determine
* if the point is on a trace or on a blank. If it is one a trace, it
* will determine the corresponding size in bp and intensity of that
* point.
*
* @param x the x-coordinate of the point.
* @param y the y-coordinate of the point.
*
* @return the type the point is in either BLANK
or
* TRACE
, which are constants declared in this class.
*/
public int findPositionInfo(int x, int y)
{
// Find the row and column
int column = (x - sliderSpace)/(width + hspace);
int row = y/(height + vspace);
// see if we are between the spaces or not
int vRemainder = y % (height + vspace);
if(vRemainder <= vspace)
return BLANK;
int hRemainder = (x - sliderSpace) % (width + hspace);
if(hRemainder <= hspace)
return BLANK;
// find out which lane we are one
laneNum = row*numColumns + column;
// make sure we don't exceed the number possible,
if( (column >= numColumns) || (row >= numRows) ||
(laneNum >= lanes.size()))
{
laneNum = -1; // we aren't on a valid lane
return BLANK;
}
size = Math.round((minSize + hScale*(hRemainder - hspace - 1))*100)/100.0;
intensity = Math.round((height - vRemainder +
vspace - 1)/vScale * 100)/100.0;
return TRACE;
}
/**
* Updates the display so that it matches the data, which may have
* changed.
*/
public void refresh()
{
repaint();
}
//=================Unused methods to satisfy intefaces=================
/** Unused*/ public void mousePressed(MouseEvent e) {}
/** Unused*/ public void mouseEntered(MouseEvent e){}
/** Unused*/ public void mouseExited(MouseEvent e){}
}