/*
* 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);
}
}
}