/* * EuroCarbDB, a framework for carbohydrate bioinformatics * * Copyright (c) 2006-2009, Eurocarb project, or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * A copy of this license accompanies this distribution in the file LICENSE.txt. * * 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 Lesser General Public License * for more details. * * Last commit: $Rev: 1210 $ by $Author: glycoslave $ on $Date:: 2009-06-12 #$ */ /** @author Alessio Ceroni (a.ceroni@imperial.ac.uk) */ package org.eurocarbdb.application.glycoworkbench.plugin.reporting; import org.eurocarbdb.application.glycoworkbench.plugin.*; import org.eurocarbdb.application.glycoworkbench.*; import org.eurocarbdb.application.glycanbuilder.*; import java.util.*; import javax.swing.*; import javax.swing.border.*; import java.awt.*; import java.awt.image.*; import java.awt.geom.*; import java.awt.event.*; import java.awt.print.*; import java.text.DecimalFormat; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.JFreeChart; import org.jfree.chart.ChartRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.data.Range; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYBarDataset; import org.jfree.data.xy.DefaultXYDataset; import org.jfree.chart.annotations.XYShapeAnnotation; import org.jfree.chart.annotations.XYTextAnnotation; import org.jfree.chart.renderer.xy.StandardXYItemRenderer; import org.jfree.chart.renderer.xy.XYBarRenderer; import static org.eurocarbdb.application.glycanbuilder.Geometry.*; public class AnnotationReportCanvas extends JComponent implements SVGUtils.Renderable, BaseDocument.DocumentChangeListener, MouseListener, MouseMotionListener, Printable { public interface SelectionChangeListener { public void selectionChanged(SelectionChangeEvent e); } public static class SelectionChangeEvent { private AnnotationReportCanvas src; public SelectionChangeEvent(AnnotationReportCanvas _src) { src = _src; } public AnnotationReportCanvas getSource() { return src; } } private AnnotationReportDocument theDocument; private GlycanRenderer theGlycanRenderer; private JScrollPane theScrollPane = null; private DefaultXYDataset theDataset; private DefaultXYDataset maxIntensityDataset; private XYPlot thePlot; private JFreeChart theChart; private boolean first_time; private boolean first_time_init_pos; // drawing private HashSet<AnnotationObject> selections; private HashMap<AnnotationObject,Rectangle> rectangles; private HashMap<AnnotationObject,Rectangle> rectangles_text; private HashMap<AnnotationObject,Rectangle> rectangles_complete; private HashMap<AnnotationObject,Polygon> connections; private HashMap<AnnotationObject,Point2D> connections_cp; private boolean is_printing; private Point mouse_start_point = null; private Point mouse_end_point = null; private AnnotationObject start_position = null; private boolean is_dragndrop = false; private boolean is_resizing = false; private boolean is_movingcp = false; private boolean was_dragged = false; private Rectangle draw_area; private Rectangle chart_area; private Rectangle2D data_area; private AnnotationReportOptions theOptions; private GraphicOptions theGraphicOptions; // private Vector<SelectionChangeListener> listeners; // construction public AnnotationReportCanvas(AnnotationReportDocument doc, boolean init_pos) { theDocument = doc; theDocument.addDocumentChangeListener(this); theOptions = theDocument.getAnnotationReportOptions(); theGraphicOptions = theDocument.getGraphicOptions(); theGlycanRenderer = theDocument.getWorkspace().getGlycanRenderer(); // create chart updateChart(); // finish setting up listeners = new Vector<SelectionChangeListener>(); is_printing = false; // adding data and structures setScale(1.); resetSelection(); // add events addMouseMotionListener( this ); addMouseListener( this ); first_time = true; first_time_init_pos = init_pos; } public void setScrollPane(JScrollPane sp) { theScrollPane = sp; } public AnnotationReportDocument getDocument() { return theDocument; } public BuilderWorkspace getWorkspace() { return theDocument.getWorkspace(); } public AnnotationReportOptions getAnnotationReportOptions() { return theDocument.getAnnotationReportOptions(); } public GraphicOptions getGraphicOptions() { return theDocument.getGraphicOptions(); } // drawing public void beforeRendering() { is_printing = true; // make sure everything is initialized updateDrawArea(true); updateData(); } public void afterRendering() { is_printing = false; } public Dimension getRenderableSize() { return getPreferredSize(); } public void paintRenderable(Graphics2D g2d) { paintComponent(g2d); } protected void paintComponent(Graphics g) { // prepare graphic object Graphics2D g2d = (Graphics2D)g.create(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // set clipping area if( is_printing ) { g2d.translate(-draw_area.x,-draw_area.y); g2d.setClip(draw_area); } //paint canvas background if( !is_printing ) { g2d.setColor(getBackground()); g2d.fillRect(0, 0, getWidth(), getHeight()); } // paint white background on drawing area g2d.setColor(Color.white); g2d.fillRect(draw_area.x, draw_area.y, draw_area.width, draw_area.height); if( !is_printing ) { g2d.setColor(Color.black); g2d.draw(draw_area); } // paint paintChart(g2d); paintAnnotations(g2d); // dispose graphic object g2d.dispose(); if( !is_printing ) { if( first_time ) { if( first_time_init_pos ) placeStructures(true); else theDocument.fireDocumentInit(); first_time = false; } else revalidate(); } } protected void paintChart(Graphics2D g2d) { ChartRenderingInfo cri = new ChartRenderingInfo(); theChart.draw(g2d,chart_area,cri); data_area = cri.getPlotInfo().getDataArea(); } protected Rectangle computeRectangles() { return computeRectangles(new PositionManager(), new BBoxManager()); } protected Rectangle computeRectangles(PositionManager pman, BBoxManager bbman) { DecimalFormat mz_df = new DecimalFormat("0.0"); Rectangle all_bbox = null; rectangles = new HashMap<AnnotationObject,Rectangle>(); rectangles_text = new HashMap<AnnotationObject,Rectangle>(); rectangles_complete = new HashMap<AnnotationObject,Rectangle>(); for( AnnotationObject a : theDocument.getAnnotations() ) { // set scale theGlycanRenderer.getGraphicOptions().setScale(theOptions.SCALE_GLYCANS*theDocument.getScale(a)); // compute bbox Point2D anchor = dataToScreenCoords(theDocument.getAnchor(a)); Rectangle bbox = theGlycanRenderer.computeBoundingBoxes(a.getStructures(),false,false,pman,bbman,false); int x = (int)anchor.getX()-bbox.width/2; int y = (int)anchor.getY()-bbox.height-theOptions.ANNOTATION_MARGIN-theOptions.ANNOTATION_MZ_SIZE; bbman.translate(x-bbox.x,y-bbox.y,a.getStructures()); bbox.translate(x-bbox.x,y-bbox.y); // save bbox rectangles.put(a,bbox); // compute text bbox String mz_text = mz_df.format(a.getPeakPoint().getX()); Dimension mz_dim = textBounds(mz_text, theOptions.ANNOTATION_MZ_FONT , theOptions.ANNOTATION_MZ_SIZE); Rectangle text_bbox = new Rectangle((int)anchor.getX()-mz_dim.width/2, (int)anchor.getY()-2*theOptions.ANNOTATION_MARGIN/3-mz_dim.height, mz_dim.width,mz_dim.height); // save text bbox rectangles_text.put(a,text_bbox); rectangles_complete.put(a,expand(union(bbox,text_bbox),theOptions.ANNOTATION_MARGIN/2)); // update all bbox all_bbox = union(all_bbox,bbox); all_bbox = union(all_bbox,text_bbox); } if( all_bbox==null ) return new Rectangle(0,0,0,0); return all_bbox; } public Rectangle2D computeSizeData(AnnotationObject a) { // compute bbox theGlycanRenderer.getGraphicOptions().setScale(theOptions.SCALE_GLYCANS*theDocument.getScale(a)); Rectangle bbox = theGlycanRenderer.computeBoundingBoxes(a.getStructures(),false,false,new PositionManager(),new BBoxManager(),false); // compute text bbox DecimalFormat mz_df = new DecimalFormat("0.0"); String mz_text = mz_df.format(a.getPeakPoint().getX()); Dimension mz_dim = textBounds(mz_text, theOptions.ANNOTATION_MZ_FONT , theOptions.ANNOTATION_MZ_SIZE); // update bbox double width = Math.max(bbox.getWidth(),mz_dim.getWidth()); double height = bbox.getHeight()+theOptions.ANNOTATION_MARGIN+theOptions.ANNOTATION_MZ_SIZE; return new Rectangle2D.Double(0,0,screenToDataX(width),screenToDataY(height)); } protected Point2D computeAnchor( Rectangle rect, Point2D cp, Point2D peak ) { Point2D anchor; if( peak.getY()>bottom(rect) ) { if( cp.getY()>bottom(rect) ) anchor = new Point2D.Double(midx(rect),bottom(rect)); else if( cp.getY()<top(rect) ) anchor = new Point2D.Double(midx(rect),top(rect)); else if( cp.getX()<left(rect) ) anchor = new Point2D.Double(left(rect),midy(rect)); else anchor = new Point2D.Double(right(rect),midy(rect)); } else { if( peak.getY()<top(rect) ) anchor = new Point2D.Double(midx(rect),top(rect)); else if( peak.getX()<left(rect) ) anchor = new Point2D.Double(left(rect),midy(rect)); else anchor = new Point2D.Double(right(rect),midy(rect)); } return anchor; } protected void computeConnections() { connections = new HashMap<AnnotationObject,Polygon>(); connections_cp = new HashMap<AnnotationObject,Point2D>(); for( AnnotationObject a : theDocument.getAnnotations() ) { Rectangle rect = rectangles_complete.get(a); Point2D cp = dataToScreenCoords(theDocument.getControlPoint(a)); Point2D peak = dataToScreenCoords(a.getPeakPoint()); // select anchor Point2D anchor = computeAnchor(rect,cp,peak); boolean add_cp = (peak.getY()>bottom(rect)); if( anchor.distance(peak)>10 ) { // create shape Polygon connection = new Polygon(); connection.addPoint((int)anchor.getX(),(int)anchor.getY()); if( add_cp ) connection.addPoint((int)cp.getX(),(int)cp.getY()); connection.addPoint((int)peak.getX(),(int)peak.getY()); if( add_cp ) connection.addPoint((int)cp.getX(),(int)cp.getY()); // save connections.put(a,connection); if( add_cp ) connections_cp.put(a,cp); } } } protected void paintAnnotations(Graphics2D g2d) { DecimalFormat mz_df = new DecimalFormat("0.0"); // set font Font old_font = g2d.getFont(); Font new_font = new Font(theOptions.ANNOTATION_MZ_FONT,Font.PLAIN,theOptions.ANNOTATION_MZ_SIZE); // compute bboxes PositionManager pman = new PositionManager(); BBoxManager bbman = new BBoxManager(); computeRectangles(pman,bbman); // compute connections computeConnections(); // paint connections for( AnnotationObject a : theDocument.getAnnotations() ) { boolean selected = !is_printing && selections.contains(a); // paint arrow Polygon connection = connections.get(a); if( connection!=null ) { g2d.setColor(theOptions.CONNECTION_LINES_COLOR); g2d.setStroke( (selected) ?new BasicStroke((float)(1.+theOptions.ANNOTATION_LINE_WIDTH)) :new BasicStroke((float)theOptions.ANNOTATION_LINE_WIDTH)); g2d.draw(connection); g2d.setStroke(new BasicStroke(1)); } // paint control point if( selected ) { g2d.setColor(Color.black); Point2D cp = connections_cp.get(a); if( cp!=null ) { int s = (int)(2+theOptions.ANNOTATION_LINE_WIDTH); g2d.fill(new Rectangle((int)cp.getX()-s,(int)cp.getY()-s,2*s,2*s)); } } } // paint glycans for( AnnotationObject a : theDocument.getAnnotations() ) { boolean highlighted = a.isHighlighted(); boolean selected = !is_printing && selections.contains(a); // set scale theGlycanRenderer.getGraphicOptions().setScale(theOptions.SCALE_GLYCANS*theDocument.getScale(a)); // paint highlighted region if( highlighted ) { Rectangle c_bbox = rectangles_complete.get(a); g2d.setColor(theOptions.HIGHLIGHTED_COLOR); g2d.setXORMode(Color.white); g2d.fill(c_bbox); g2d.setPaintMode(); g2d.setColor(Color.black); g2d.draw(c_bbox); } // paint glycan for( Glycan s: a.getStructures() ) theGlycanRenderer.paint(g2d,s,null,null,false,false,pman,bbman); // paint MZ text g2d.setFont(new_font); g2d.setColor(theOptions.MASS_TEXT_COLOR); String mz_text = mz_df.format(a.getPeakPoint().getX()); Rectangle mz_bbox = rectangles_text.get(a); g2d.drawString(mz_text,mz_bbox.x,mz_bbox.y+mz_bbox.height); // paint selection if( selected ) { // paint rectangle Rectangle c_bbox = rectangles_complete.get(a); g2d.setStroke( new BasicStroke(highlighted ?2 :1) ); g2d.setColor(Color.black); g2d.draw(c_bbox); g2d.setStroke(new BasicStroke(1)); // paint resize points Polygon p1 = new Polygon(); int cx1 = right(c_bbox); int cy1 = top(c_bbox); p1.addPoint(cx1,cy1); p1.addPoint(cx1-2*theOptions.ANNOTATION_MARGIN/3,cy1); p1.addPoint(cx1,cy1+2*theOptions.ANNOTATION_MARGIN/3); g2d.fill(p1); Polygon p2 = new Polygon(); int cx2 = left(c_bbox); int cy2 = top(c_bbox); p2.addPoint(cx2,cy2); p2.addPoint(cx2+2*theOptions.ANNOTATION_MARGIN/3,cy2); p2.addPoint(cx2,cy2+2*theOptions.ANNOTATION_MARGIN/3); g2d.fill(p2); } } g2d.setFont(old_font); } private void xorRectangle(Point start_point, Point end_point) { Graphics g = getGraphics(); g.setXORMode(Color.white); g.setColor(Color.gray); Rectangle rect = makeRectangle(start_point,end_point); g.drawRect(rect.x,rect.y,rect.width,rect.height); } private void xorSelections(Point start_point, Point end_point) { Graphics g = getGraphics(); g.setXORMode(Color.white); g.setColor(Color.gray); int dx = end_point.x-start_point.x; int dy = end_point.y-start_point.y; for( AnnotationObject a : selections ) { Rectangle rect = rectangles_complete.get(a); g.drawRect(dx+rect.x, dy+rect.y, rect.width, rect.height); } } private double scaleFactor(AnnotationObject selection, Point start_point, Point end_point) { Rectangle rect = rectangles.get(selection); double scale = 1.; if( start_point.x>midx(rect) ) { scale = Math.min((double)(end_point.x-start_point.x+rect.width/2.)/(double)(rect.width/2.), (double)(start_point.y-end_point.y+rect.height)/(double)(rect.height)); } else { scale = Math.min((double)(start_point.x-end_point.x+rect.width/2.)/(double)(rect.width/2.), (double)(start_point.y-end_point.y+rect.height)/(double)(rect.height)); } return Math.max(0.,scale); } private void xorConnection(AnnotationObject selection, Point start_point, Point end_point) { Graphics g = getGraphics(); g.setXORMode(Color.white); g.setColor(Color.gray); Rectangle rect = rectangles_complete.get(selection); Point2D peak = dataToScreenCoords(selection.getPeakPoint()); // select anchor Point2D anchor = computeAnchor(rect,end_point,peak); // draw connection g.drawLine((int)anchor.getX(),(int)anchor.getY(),(int)end_point.getX(),(int)end_point.getY()); g.drawLine((int)end_point.getX(),(int)end_point.getY(),(int)peak.getX(),(int)peak.getY()); } private void xorResizing(AnnotationObject selection, Point start_point, Point end_point) { Graphics g = getGraphics(); g.setXORMode(Color.white); g.setColor(Color.gray); Rectangle rect = rectangles.get(selection); double scale = 1.; if( start_point.x>midx(rect) ) { scale = Math.min((double)(end_point.x-start_point.x+rect.width/2.)/(double)(rect.width/2.), (double)(start_point.y-end_point.y+rect.height)/(double)(rect.height)); } else { scale = Math.min((double)(start_point.x-end_point.x+rect.width/2.)/(double)(rect.width/2.), (double)(start_point.y-end_point.y+rect.height)/(double)(rect.height)); } scale = Math.max(0.,scale); g.drawRect((int)(midx(rect)-rect.width*scale/2.),(int)(bottom(rect)-rect.height*scale), (int)(rect.width*scale),(int)(rect.height*scale)); } public Dimension getPreferredSize() { if( is_printing ) return draw_area.getSize(); return theOptions.getViewDimension(draw_area.getSize()); } public Dimension getMinimumSize() { return new Dimension(0,0); } public double screenToDataX(double length) { return length / thePlot.getDomainAxis().lengthToJava2D(1.,data_area,thePlot.getDomainAxisEdge()); } public double screenToDataY(double length) { return length/thePlot.getRangeAxis().lengthToJava2D(1.,data_area,thePlot.getRangeAxisEdge()); } public Point2D screenToDataCoords(Point2D p) { double x = thePlot.getDomainAxis().java2DToValue(p.getX(),data_area,thePlot.getDomainAxisEdge()); double y = thePlot.getRangeAxis().java2DToValue(p.getY(),data_area,thePlot.getRangeAxisEdge()); return new Point2D.Double(x,y); } public Point2D dataToScreenCoords(Point2D p) { double x = thePlot.getDomainAxis().valueToJava2D(p.getX(),data_area,thePlot.getDomainAxisEdge()); double y = thePlot.getRangeAxis().valueToJava2D(p.getY(),data_area,thePlot.getRangeAxisEdge()); return new Point2D.Double(x,y); } public AnnotationObject getAnnotationAtPoint(Point2D p) { for( Map.Entry<AnnotationObject,Rectangle> e : rectangles_complete.entrySet() ) { if( e.getValue().contains(p) ) return e.getKey(); } return null; } public AnnotationObject getConnectionAtPoint(Point2D p) { for( Map.Entry<AnnotationObject,Polygon> e : connections.entrySet() ) { if( e.getValue().intersects(p.getX()-3,p.getY()-3,6.,6.) ) return e.getKey(); } return null; } public AnnotationObject getCPAtPoint(Point2D p) { for( Map.Entry<AnnotationObject,Point2D> e : connections_cp.entrySet() ) { if( e.getValue().distance(p)<=3 ) return e.getKey(); } return null; } public Collection<AnnotationObject> getAnnotationsInside(Rectangle r) { Vector<AnnotationObject> ret = new Vector<AnnotationObject>(); if( r!=null ) { for( Map.Entry<AnnotationObject,Rectangle> e : rectangles_complete.entrySet() ) { if( r.intersects(e.getValue()) ) ret.add(e.getKey()); } } return ret; } // selection public void selectAll() { selections.addAll(theDocument.getAnnotations()); fireUpdatedSelection(); } public void enforceSelection(Point2D p) { AnnotationObject a = getAnnotationAtPoint(p); if( a==null ) a = getConnectionAtPoint(p); if( !isSelected(a) ) setSelection(a); } public void resetSelection() { selections = new HashSet<AnnotationObject>(); fireUpdatedSelection(); } public boolean hasSelection() { return selections.size()>0; } public boolean isSelected(AnnotationObject a) { return selections.contains(a); } public void setSelection(AnnotationObject a) { selections.clear(); if( a!=null ) selections.add(a); fireUpdatedSelection(); } public void addSelection(AnnotationObject a) { if( a!=null ) { selections.add(a); fireUpdatedSelection(); } } public void setSelection(Collection<AnnotationObject> toselect) { selections.clear(); selections.addAll(toselect); fireUpdatedSelection(); } public void addSelection(Collection<AnnotationObject> toselect) { selections.addAll(toselect); fireUpdatedSelection(); } // actions public void print( PrinterJob job ) throws PrinterException { // do something before is_printing = true; job.print(); // do something after is_printing = false; } public int print(Graphics g, PageFormat pageFormat, int pageIndex) throws PrinterException { if (pageIndex > 0) { return NO_SUCH_PAGE; } else { Graphics2D g2d = (Graphics2D)g; g2d.setBackground(Color.white); g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY()); Dimension td = this.getPreferredSize(); double sx = pageFormat.getImageableWidth()/td.width; double sy = pageFormat.getImageableHeight()/td.height; double s = Math.min(sx,sy); if( s<1. ) g2d.scale(s,s); RepaintManager.currentManager(this).setDoubleBufferingEnabled(false); this.paint(g2d); RepaintManager.currentManager(this).setDoubleBufferingEnabled(true); return PAGE_EXISTS; } } public void setScale(double scale) { theOptions.setScale(scale); double scaleg = theGraphicOptions.setScale(theOptions.SCALE_GLYCANS); theOptions.setScale(scale*scaleg/theOptions.SCALE_GLYCANS); // set to nearest feasible scale updateView(); } public double getScale() { return theOptions.SCALE; } public void moveSelections(int dx, int dy) { HashSet<AnnotationObject> old_selections = selections; double ddx = screenToDataX(dx); double ddy = screenToDataY(dy); theDocument.move(selections,ddx,ddy); selections = old_selections; } public void moveControlPointTo(AnnotationObject selection, Point2D p) { Point2D dp = screenToDataCoords(p); theDocument.moveControlPointTo(selection,dp.getX(),dp.getY()); } public void rescaleSelections(double factor) { HashSet<AnnotationObject> old_selections = selections; theDocument.rescale(selections,factor); selections = old_selections; } public void resetSelectionsScale() { HashSet<AnnotationObject> old_selections = selections; theDocument.resetScale(selections); selections = old_selections; } public void highlightSelections() { HashSet<AnnotationObject> old_selections = selections; boolean all_highlighted = true; for( AnnotationObject a : selections ) all_highlighted = all_highlighted && a.isHighlighted(); theDocument.setHighlighted(selections,!all_highlighted); selections = old_selections; } public void updateAnnotations(Glycan parent, PeakAnnotationCollection pac, boolean merge) { // update document Vector<AnnotationObject> added = new Vector<AnnotationObject>(); boolean changed = theDocument.updateData(parent,pac,added,false,merge); if( added.size()>0 ) { // update canvas updateDrawArea(false); resetSelection(); repaint(); // place new structures placeStructures(added,false); } else if( changed ) theDocument.fireDocumentChanged(); } public void cut() { copy(); delete(); } public void copy() { Vector<Glycan> sel_structures = new Vector<Glycan>(); for( AnnotationObject a : selections ) sel_structures.addAll(a.getStructures()); ClipUtils.setContents(new GlycanSelection(theGlycanRenderer,sel_structures)); } public void delete() { theDocument.remove(selections); } public void getScreenshot() { ClipUtils.setContents(getImage()); } public BufferedImage getImage() { // Create an image that supports transparent pixels Dimension d = getPreferredSize(); BufferedImage img = GraphicUtils.createCompatibleImage(d.width,d.height,false); // prepare graphics context Graphics2D g2d = img.createGraphics(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); g2d.setBackground(new Color(255,255,255,0)); // paint is_printing = true; this.paint(g2d); is_printing = false; return img; } public void placeStructures() { placeStructures(theDocument.getAnnotations(),false); } private void placeStructures(boolean init) { placeStructures(theDocument.getAnnotations(),init); } private void placeStructures(Vector<AnnotationObject> annotations, boolean init) { // get starting point double y = thePlot.getRangeAxis().getRange().getUpperBound(); double cur_x = thePlot.getDomainAxis().getRange().getLowerBound(); double all_width = 0.; for( AnnotationObject a : annotations ) { if( a.hasAnnotations() ) all_width += screenToDataX(rectangles_complete.get(a).width); } double min_pp_x = annotations.firstElement().getPeakPoint().getX(); double max_pp_x = annotations.lastElement().getPeakPoint().getX(); double center_pp_x = (max_pp_x + min_pp_x)/2; cur_x = Math.max(cur_x,center_pp_x-all_width/2); // place annotations for( AnnotationObject a : annotations ) { Point2D pp = a.getPeakPoint(); if( a.hasAnnotations() ) { double cur_width = screenToDataX(rectangles_complete.get(a).width); double x = cur_x + cur_width/2.; theDocument.getAnchor(a).setLocation(x,y); theDocument.getControlPoints().put(a,theDocument.computeControlPoint(new Point2D.Double(x,y),pp)); cur_x += cur_width; } else { theDocument.getAnchor(a).setLocation(pp.getX(),pp.getY()); theDocument.getControlPoints().put(a,theDocument.computeControlPoint(pp,pp)); } } // refine control points for( int i=0; i<annotations.size(); i++ ) { AnnotationObject ai = annotations.get(i); Point2D aai = theDocument.getAnchor(ai); Point2D cpi = theDocument.getControlPoint(ai); if( aai.getX()<cpi.getX() ) { for( int l=i+1; l<annotations.size(); l++ ) { AnnotationObject al = annotations.get(l); Point2D aal = theDocument.getAnchor(al); Point2D cpl = theDocument.getControlPoint(al); if( aal.getX()>cpi.getX() ) break; if( cpl.getY()<cpi.getY() ) { cpl.setLocation(cpl.getX(),cpi.getY()); ai = al; aai = aal; cpi = cpl; } else break; } } else { for( int l=i-1; l>=0; l-- ) { AnnotationObject al = annotations.get(l); Point2D aal = theDocument.getAnchor(al); Point2D cpl = theDocument.getControlPoint(al); if( aal.getX()<cpi.getX() ) break; if( cpl.getY()<cpi.getY() ) { cpl.setLocation(cpl.getX(),cpi.getY()); ai = al; aai = aal; cpi = cpl; } else break; } } } // fire events if( init ) theDocument.fireDocumentInit(); else theDocument.fireDocumentChanged(); } public boolean canGroupSelections() { return theDocument.canGroup(selections); } public void groupSelections() { theDocument.group(selections); } public void ungroupSelections() { theDocument.ungroup(selections,this); } // events public void documentInit(BaseDocument.DocumentChangeEvent e) { updateChart(); //updateDrawArea(false); resetSelection(); updateView(); updateView(); } public void documentChanged(BaseDocument.DocumentChangeEvent e) { updateDrawArea(false); resetSelection(); thePlot.getRenderer().setPaint(theOptions.SPECTRUM_COLOR); repaint(); } public void addSelectionChangeListener(SelectionChangeListener l) { if( l!=null ) listeners.add(l); } public void removeSelectionChangeListener(SelectionChangeListener l) { if( l!=null ) listeners.remove(l); } public void fireUpdatedSelection() { for( Iterator<SelectionChangeListener> i=listeners.iterator(); i.hasNext(); ) i.next().selectionChanged(new SelectionChangeEvent(this)); repaint(); } public void updateView() { updateDrawArea(true); updateData(); repaint(); } private void updateChart() { String x_label = "m/z ratio"; String y_label = (theDocument.isShowRelativeIntensities()) ?"Intensity %" :"Intensity"; theDataset = new DefaultXYDataset(); if( theDocument.getPeakData()!=null ) { theChart = org.jfree.chart.ChartFactory.createScatterPlot(null, x_label, y_label, theDataset, org.jfree.chart.plot.PlotOrientation.VERTICAL, false, false, false); thePlot = (XYPlot)theChart.getPlot(); thePlot.setRenderer(new StandardXYItemRenderer(StandardXYItemRenderer.LINES)); } else { theChart = org.jfree.chart.ChartFactory.createScatterPlot(null, x_label, y_label, new XYBarDataset(theDataset,0.001), org.jfree.chart.plot.PlotOrientation.VERTICAL, false, false, false); thePlot = (XYPlot)theChart.getPlot(); thePlot.setRenderer(new XYBarRenderer()); } if( theDocument.isShowRelativeIntensities() && theOptions.SHOW_MAX_INTENSITY ) { // set second axis ValueAxis second_axis = new NumberAxis(""); thePlot.setRangeAxis(1, second_axis); // set dataset maxIntensityDataset = new DefaultXYDataset(); thePlot.setDataset(1, maxIntensityDataset); thePlot.mapDatasetToRangeAxis(1, 1); // set invisible renderer StandardXYItemRenderer r = new StandardXYItemRenderer(StandardXYItemRenderer.SHAPES); r.setBaseShapesVisible(false); thePlot.setRenderer(1,r); } theChart.setBackgroundPaint(Color.white); theChart.setBorderVisible(false); thePlot.getRenderer().setPaint(theOptions.SPECTRUM_COLOR); thePlot.setOutlinePaint(null); thePlot.setDomainGridlinesVisible(false); thePlot.setRangeGridlinesVisible(false); } private void updateData() { double[][] data = theDocument.getData(data_area,false); if( theDocument.isShowRelativeIntensities() && data!=null && data[0].length>0 ) { // get max intensity double min_int = data[1][0]; double max_int = data[1][0]; for( int i=1; i<data[1].length; i++ ) { max_int = Math.max(max_int,data[1][i]); min_int = Math.min(min_int,data[1][i]); } // normalize for( int i=0; i<data[1].length; i++ ) data[1][i] = 100.*data[1][i]/max_int; // set max intensity data if( maxIntensityDataset!=null ) { double[][] s = new double[2][]; s[0] = new double[]{data[0][0],data[0][data[0].length-1]}; s[1] = new double[]{min_int,max_int}; maxIntensityDataset.removeSeries("max"); maxIntensityDataset.addSeries("max",s); ((NumberAxis)thePlot.getRangeAxis(1)).setTickUnit(new org.jfree.chart.axis.NumberTickUnit(max_int)); } } // set peak data theDataset.removeSeries("intensities"); theDataset.addSeries("intensities",data); } protected void updateDrawArea(boolean update_chart) { // update data area if( update_chart ) { draw_area = theOptions.getDefaultDrawArea(); chart_area = theOptions.getDefaultChartArea(); data_area = null; paintChart(GraphicUtils.createImage(theOptions.getDefaultViewDimension(),true).createGraphics()); } // update glycan bboxes Rectangle all_bbox = computeRectangles(); // update draw area int add_left = Math.max(0,chart_area.x-all_bbox.x); int add_top = Math.max(0,chart_area.y-all_bbox.y); int add_right = Math.max(0,all_bbox.x+all_bbox.width-chart_area.x-chart_area.width); int add_bottom = Math.max(0,all_bbox.y+all_bbox.height-chart_area.y-chart_area.height); draw_area.width = chart_area.width + add_left + add_right + 2*theOptions.CHART_X_MARGIN; draw_area.height = chart_area.height + add_top + add_bottom + 2*theOptions.CHART_Y_MARGIN; chart_area.x = draw_area.x + add_left + theOptions.CHART_X_MARGIN; chart_area.y = draw_area.y + add_top + theOptions.CHART_Y_MARGIN; } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public boolean isInResizeCorner(AnnotationObject selection, Point p) { Rectangle rect = rectangles_complete.get(selection); int size = 2*theOptions.ANNOTATION_MARGIN/3; Rectangle corner1 = new Rectangle(left(rect),top(rect),size,size); if( corner1.contains(p) ) return true; Rectangle corner2 = new Rectangle(right(rect)-size,top(rect),size,size); if( corner2.contains(p) ) return true; return false; } public void mousePressed(MouseEvent e) { if( MouseUtils.isPushTrigger(e) || MouseUtils.isCtrlPushTrigger(e) ) { start_position = getAnnotationAtPoint(e.getPoint()); mouse_start_point = e.getPoint(); mouse_end_point = null; if( start_position!=null ) { if( isInResizeCorner(start_position,mouse_start_point) ) { // start resizing setSelection(start_position); is_resizing = true; } else { // start DnD if( !isSelected(start_position) ) setSelection(start_position); is_dragndrop = true; } } else { start_position = getCPAtPoint(e.getPoint()); if( start_position!=null ) { // start moving cp setSelection(start_position); is_movingcp = true; } else { // start selection is_dragndrop = false; } } } was_dragged = false; } public void mouseMoved(MouseEvent e) { } public void mouseDragged(MouseEvent e) { was_dragged = true; // if is dragging don't update selection if( is_dragndrop ) { if( mouse_end_point!=null ) xorSelections(mouse_start_point,mouse_end_point); mouse_end_point = e.getPoint(); xorSelections(mouse_start_point,mouse_end_point); } else if( is_resizing ) { if( mouse_end_point!=null ) xorResizing(start_position,mouse_start_point,mouse_end_point); mouse_end_point = e.getPoint(); xorResizing(start_position,mouse_start_point,mouse_end_point); } else if( is_movingcp ) { if( mouse_end_point!=null ) xorConnection(start_position,mouse_start_point,mouse_end_point); mouse_end_point = e.getPoint(); xorConnection(start_position,mouse_start_point,mouse_end_point); } else if( mouse_start_point!=null ) { if( mouse_end_point!=null ) xorRectangle(mouse_start_point,mouse_end_point); mouse_end_point = e.getPoint(); xorRectangle(mouse_start_point,mouse_end_point); } dragAndScroll(e); } public void mouseReleased(MouseEvent e) { // Drag and drop if( is_dragndrop && was_dragged ) { if( mouse_end_point!=null ) xorSelections(mouse_start_point,mouse_end_point); moveSelections(e.getPoint().x-mouse_start_point.x, e.getPoint().y-mouse_start_point.y); } else if( is_resizing && was_dragged ) { if( mouse_end_point!=null ) xorResizing(start_position,mouse_start_point,mouse_end_point); rescaleSelections(scaleFactor(start_position,mouse_start_point,e.getPoint())); } else if( is_movingcp && was_dragged ) { if( mouse_end_point!=null ) xorConnection(start_position,mouse_start_point,mouse_end_point); moveControlPointTo(start_position,e.getPoint()); } else if( mouse_start_point!=null ) { if( mouse_end_point!=null ) xorRectangle(mouse_start_point,mouse_end_point); Rectangle mouse_rect = makeRectangle(mouse_start_point,e.getPoint()); if( MouseUtils.isNothingPressed(e) ) setSelection(getAnnotationsInside(mouse_rect)); else if( MouseUtils.isCtrlPressed(e) ) addSelection(getAnnotationsInside(mouse_rect)); } // reset start_position = null; is_resizing = false; is_movingcp = false; is_dragndrop = false; was_dragged = false; mouse_start_point = null; mouse_end_point = null; repaint(); } public void mouseClicked(MouseEvent e) { AnnotationObject a = getAnnotationAtPoint(e.getPoint()); if( a==null ) a = getConnectionAtPoint(e.getPoint()); if( a!=null ) { if( MouseUtils.isAddSelectTrigger(e) || MouseUtils.isSelectAllTrigger(e) ) addSelection(a); else if( MouseUtils.isSelectTrigger(e) ) setSelection(a); } else resetSelection(); } private void dragAndScroll(MouseEvent e) { // move view if near borders Point point = e.getPoint(); JViewport view = theScrollPane.getViewport(); Rectangle inner = view.getViewRect(); inner.grow(-10,-10); if( !inner.contains(point) ) { Point orig = view.getViewPosition(); if( point.x<inner.x ) orig.x -= 10; else if( point.x>(inner.x+inner.width) ) orig.x += 10; if( point.y<inner.y ) orig.y -= 10; else if( point.y>(inner.y+inner.height) ) orig.y += 10; int maxx = getBounds().width-view.getViewRect().width; int maxy = getBounds().height-view.getViewRect().height; if( orig.x<0 ) orig.x = 0; if( orig.x>maxx ) orig.x = maxx; if( orig.y<0 ) orig.y = 0; if( orig.y>maxy ) orig.y = maxy; view.setViewPosition(orig); } } }