//===================================================================== // File: TraceView.java // Class: TraceView // 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.event.ActionEvent; import java.awt.event.ActionListener; import AFLPcore.Cutoff; import AFLPcore.CutoffFunction; import AFLPcore.DataList; import AFLPcore.Lane; import AFLPcore.LinearCutoff; import AFLPcore.ProgOptions; /** * Displays the trace of a lane. The lane can be set using the * init method. The trace's size can be adjusted and * the name of the lane will be displayed on the trace. The * paint method is capable of drawing to a page for the * printer, but this class should be sized appropriately first. When * drawing to the screen, the trace display is double-buffered. * * @author James J. Benham * @version 1.0.0 * @date August 12, 1998 */ public class TraceView extends Panel implements ActionListener { // 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; // Error message location private static int LABEL_HORZ = 20; private static int LABEL_VERT = 20; // 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"; // Double buffering stuff private Dimension offDimension; private Image offImage; private Graphics offGraphics; // Bar stuff private ButtonBar buttonBar; private Bar infoBar; private Label infoLabel; // Button Bar components private ImgButton previousButton; private ImgButton nextButton; private Frame topWindow; private DataList lanes; private Lane lane; private int h_inset; private int v_inset; private int width; private int height; private double maxIntensity; private double hScale; private double vScale; private double minSize; private double maxSize; /** * Create a new TraceView with the specified parameters. * * @param parentWindow used as an owner for various dialog boxes. */ public TraceView(Frame parentWindow) { topWindow = parentWindow; lane = null; h_inset = 10; v_inset = 60; width = 500; height = 240; setLayout(null); setBounds(0, 0, h_inset + width, v_inset + height); createButtonBar(); createInfoBar(); } /** * Sets the trace to the specified lane and sets the scales. * * @param ln the lane whose trace should be displayed */ public void init(Lane ln, DataList lanes) { this.lanes = lanes; lane = ln; if(lane != null) { //==============find the scales=================== minSize = ln.getMinSize(); maxSize = ln.getMaxSize(); maxIntensity = ln.getMaxHeight(minSize, maxSize); hScale = (maxSize - minSize)/(width - 2); // -2 for left border vScale = (height - 1)/maxIntensity; } } /** * Gives the width of the trace. * * @return the width, excluding borders */ public int getWidth() { return width; } /** * Sets the width of the trace, including borders, to the specified * value. * * @param width the new width for this object. */ public void setWidth(int width) { this.width = width - h_inset; setBounds(0, 0, width, v_inset + height); } /** * Gives the height of the trace, excluding borders. * * @return the height of the trace area. */ public int getHeight() { return height; } /** * Sets the height of the trace, including borders to the specified * value. * * @param height the new height for this */ public void setHeight(int height) { this.height = height - v_inset; setBounds(0, 0, h_inset + width, height); } /** * Draws the trace to the specified graphics. If the graphics is * a PrintGraphics then specail care is taken, such * as disabeling double-buffering. */ public void paint(Graphics g) { //==============find the scales=================== // printing could change it. hScale = (maxSize - minSize)/(width - 2); // -2 for left border vScale = (height - 1)/maxIntensity; if(g instanceof PrintGraphics) { // we don't need to doulbe buffer the printer :) 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(); } } // make sure we have a lane. If not, we don't have anything to display. // Draw to the graphics screen and then bail. don't use the double // buffer. if(lane == null) { g.clearRect(0, 0, h_inset + width, v_inset + height); g.drawString("No Lanes! Lane trace not available.", LABEL_HORZ, LABEL_VERT); return; // bail! } // ============== Normal drawing =================== offGraphics.clearRect(0, 0, h_inset + width, v_inset + height); // draw the axis int lowY = v_inset + height - 1; offGraphics.drawLine(h_inset, v_inset, h_inset, lowY); offGraphics.drawLine(h_inset, lowY, h_inset + width, lowY); // draw the header offGraphics.setColor(Color.black); offGraphics.drawString("" + lane.getLaneNumber() + " " + lane.getName(), h_inset, v_inset - 1); //================Draw the trace===================== // find the color and set it switch(lane.getColor()) { case Lane.RED: offGraphics.setColor(Color.red); break; case Lane.GREEN: offGraphics.setColor(Color.green); break; case Lane.BLUE: offGraphics.setColor(Color.blue); break; case Lane.YELLOW: offGraphics.setColor(Color.yellow); break; } // draw the actual trace double size = minSize; int previousIntensity = lowY - (int)(vScale*lane.getHeightAt(minSize)); int intensity; for(int i=1; i < (width - 1); i++) { // find the height, -1 to place it above the border. intensity = lowY - (int) (vScale * lane.getHeightAt(size)); offGraphics.drawLine(h_inset + i, previousIntensity, h_inset + i + 1, intensity); previousIntensity = intensity; size += hScale; } //================Draw the cutoff functions============ offGraphics.setColor(Color.lightGray); DataList cutoffs = lane.getCutoffs(); if(!cutoffs.isEmpty()) { int startPoint; int endPoint; Cutoff ct1 = (Cutoff) cutoffs.dataAt(0); Cutoff ct2; for(int i=1; i < (cutoffs.size() - 1); i++) { ct2 = (Cutoff) cutoffs.dataAt(i); endPoint = h_inset + (int)(hScale*ct2.getStartPos()) + 1; size = ct1.getStartPos(); startPoint = h_inset + (int)(hScale*size) + 1; drawCutoff(offGraphics, ct1, size, startPoint, endPoint, lowY); // set up the next cutoff pair. ct1 = ct2; } // draw the final one. endPoint = h_inset + (int)(hScale*maxSize); size = ct1.getStartPos(); startPoint = h_inset + (int)(hScale*size) + 1; drawCutoff(offGraphics, ct1, size, startPoint, endPoint, lowY); } if(!(g instanceof PrintGraphics)) g.drawImage(offImage, 0, 0, this); } /** * Draws a cutoff using the specified parameters. If one of the levels * is a line, a line is simply drawn. Otherwise, the method will * play connect the dots at each size to produce the cutoff function. * * @param g the graphics object to draw on * @param ct the cutoff to draw * @param size the starting size of the cutoff * @param startPoint the first horizontal position, in pixels * @param endPoint the value to stop drawing at, in pixels * @param lowY the y-value of the baseline. */ private void drawCutoff(Graphics g, Cutoff ct, double size, int startPoint, int endPoint, int lowY) { int intensity; int previousIntensity; int numLevels = ct.getNumLevels(); for(int i=0; i < numLevels; i++) { if(ct.getCutoffFunction(i) instanceof LinearCutoff) { intensity = lowY - (int) (vScale * ct.getCutoff(size, i)); g.drawLine(startPoint, intensity, endPoint, intensity); } else { previousIntensity = (lowY - (int)(vScale*ct.getCutoff(size, i))); for(int j=startPoint; j < (endPoint); j++) { // find the height, intensity = lowY - (int) (vScale * ct.getCutoff(size, i)); g.drawLine(h_inset + j, previousIntensity, h_inset + j + 1, intensity); previousIntensity = intensity; size += hScale; } } } } /** * Called by JVM to refresh screen, but it simply calls paint. */ public void update(Graphics g) { paint(g); } /** * Gives the ButtonBar associated with the trace. * * @return the button bar. */ public ButtonBar getButtonBar() { return buttonBar; } /** * 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 previous button================ previousButton = new ImgButton(ButtonBar.retrieveImage(ProgOptions.homePath + REL_PATH + PREVIOUS_FILE)); buttonBar.add(previousButton); int startX = buttonBar.getFreeHorzPos(); 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); } /** * Returns a bar that displays information about the trace. This object * should be displayed somewhere by the container of this object. * * @return the bar as described above */ public Bar getInfoBar() { return infoBar; } /** * 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("Graph 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); } /** * Update the display so that it matches the data. */ public void refresh() { repaint(); } /** * Handles the buttons in the button bar. */ public void actionPerformed(ActionEvent e) { if(e.getSource() == previousButton) { // find the location of the lane in the list int location = -1; for(int i=0; i < lanes.size(); i++) if(lane == (Lane) lanes.dataAt(i)) { location = i; break; } // now move to the previous one in the list if(location <= 0) infoLabel.setText("Already on the first lane."); else { lane = (Lane) lanes.dataAt(location - 1); infoLabel.setText("Showing lane number " + (location)); init(lane, lanes); refresh(); } } else if(e.getSource() == nextButton) { // find the location of the lane in the list int location = -1; for(int i=0; i < lanes.size(); i++) if(lane == (Lane) lanes.dataAt(i)) { location = i; break; } // now move to the previous one in the list if(location == (lanes.size() - 1)) infoLabel.setText("Already on the last lane."); else { lane = (Lane) lanes.dataAt(location + 1); infoLabel.setText("Showing lane number " + (location + 2)); init(lane, lanes); refresh(); } } } }