//===================================================================== // 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){} }