/* * Copyright (C) 2008 Genome Research Limited * * 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; either version 2 * of the License, or (at your option) any later version. * * 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. * * @author: Tim Carver */ package uk.ac.sanger.artemis.circular; import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Cursor; import java.awt.Dimension; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Image; import java.awt.Point; import java.awt.RenderingHints; import java.awt.event.*; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.image.RenderedImage; import java.awt.print.PageFormat; import java.awt.print.Printable; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; import java.util.Collections; import java.util.Enumeration; import java.util.Hashtable; import java.util.List; import java.util.Vector; import java.awt.datatransfer.Transferable; import java.awt.dnd.DnDConstants; import java.awt.dnd.DragGestureEvent; import java.awt.dnd.DragGestureListener; import java.awt.dnd.DragSource; import java.awt.dnd.DragSourceDragEvent; import java.awt.dnd.DragSourceDropEvent; import java.awt.dnd.DragSourceEvent; import java.awt.dnd.DragSourceListener; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DropTargetListener; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import javax.swing.JButton; import javax.swing.JCheckBoxMenuItem; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import uk.ac.sanger.artemis.EntryGroup; import uk.ac.sanger.artemis.editor.BrowserControl; import uk.ac.sanger.artemis.io.Range; import uk.ac.sanger.artemis.sequence.Bases; public class DNADraw extends ScrollPanel implements Printable, DragGestureListener, DragSourceListener, DropTargetListener { private static final long serialVersionUID = 1L; public static JScrollPane jsp = new JScrollPane(); private DNADraw current_dna; private JFrame mainFrame; private Point location = new Point(); private Dimension border = new Dimension(150,100); private Dimension panelSize = new Dimension(690,690); private Dimension linearPanelSize = new Dimension(800,350); private Hashtable lineAttr; private Vector minorTicks; private Vector majorTicks; private Vector block; private Vector restrictionEnzyme; private int startTick = 0; private int minorTick = 100; private int majorTick = 500; // // store the tick positions -- there appears to be // a bug in AffineTransform when it comes to using // elements from the matrix when printing // private int[] tickMajorXPositions; private int[] tickMajorYPositions; private int[] tickMinorXPositions; private int[] tickMinorYPositions; private boolean labelTicks = true; private int[] reXPositions; private int[] reYPositions; private boolean close = false; private EntryGroup artemisEntryGroup; private Bases bases; private Graph gcGraph; private Graph gcSkewGraph; private Vector userGraphs = new Vector(); final JMenu userDraw = new JMenu("Draw"); final JMenu userOptions = new JMenu("Options"); protected static int Y_SHIFT = -35; protected static int THETA = -90; private TrackManager trackManager; private AffineTransform original; // linear plot variables private JMenuItem linearPlotOptions = new JMenuItem("Linear plot...");; private int numberOfLines; private int basesPerLine = 20000; private float lineHeight = 200.f; private float singleBaseWidth; private int borderWidth2; private int borderHeight2; public DNADraw() { super(new BorderLayout()); location.setLocation(75.d, 75.d); current_dna = this; setBackground(Color.white); setPreferredSize(panelSize); setOpaque(false); setToolTipText(""); if( System.getProperty("java.awt.headless") == null || !System.getProperty("java.awt.headless").equals("true") ) { DragSource dragSource = DragSource.getDefaultDragSource(); dragSource.createDefaultDragGestureRecognizer( this, // component where drag originates DnDConstants.ACTION_COPY_OR_MOVE, // actions this); setDropTarget(new DropTarget(this,this)); } lineAttr = new Hashtable(); lineAttr.put("start",new Integer(0)); lineAttr.put("end",new Integer(4000)); lineAttr.put("lsize",new Integer(5)); lineAttr.put("circular",new Boolean(true)); MouseListener mouseListener = new MouseAdapter() { public void mouseClicked(MouseEvent me) { if(me.getClickCount() == 2 && !me.isPopupTrigger()) { final Block b = getBlockAtLocation(me.getPoint()); if(b != null) { final JFrame f = new JFrame("Properties"); JButton butt = new JButton("Delete"); butt.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { block.remove(b); current_dna.repaint(); f.setVisible(false); f.dispose(); } }); b.showProperties(f, DNADraw.this, butt); } } } }; this.addMouseListener(mouseListener); } public String getVersion() { final ClassLoader cl = this.getClass().getClassLoader(); try { String line; InputStream in = cl.getResourceAsStream("etc/versions"); BufferedReader reader = new BufferedReader(new InputStreamReader(in)); while((line = reader.readLine()) != null) { if(line.startsWith("DNAPlotter")) return line.substring( "DNAPlotter".length() ).trim(); } reader.close(); in.close(); } catch (Exception ex) { } return null; } /** * Add list of features to a track * @param features * @param track * @param drawLabel */ public void addFeaturesToTrack(final List features, final Track track, final boolean drawLabel) { for(int i=0; i0) basesPerLine = majorTick; } //major ticks drawCircularTicks(g2,ddiameter,ddiameter2,diameter,origin, widthPanel,heightPanel,rad,pi,widDash,fm, lineSize,record,majorTicks,false); //minor ticks drawCircularTicks(g2,ddiameter,ddiameter2,diameter,origin, widthPanel,heightPanel,rad,pi,widDash/2,fm, lineSize,record,minorTicks,true); // draw features Vector markers = getGeneticMarker(); for(int i=0; i pi) theta = theta - pi*2.d; newOrig = (AffineTransform)(origin.clone()); // rotate and add tick mark newOrig.rotate(theta,widthPanel/2.d,heightPanel/2.d); String label = Integer.toString(tick); double wid = fm.stringWidth(label); int x = 0; int y = 0; if(record) { x = (int)( (ddiameter2) + (newOrig.getScaleX()* (widDash+lineSize+3+(diameter+wid)/2.d)) - (wid/2.d)); y = (int)( (ddiameter2) + (newOrig.getShearY()* (widDash+lineSize+3+(diameter+hgt)/2.d)) + (hgt/2.d)); int index = ticks.indexOf(new Integer(tick)); if(smallTicks) { tickMinorXPositions[index] = x; tickMinorYPositions[index] = y; } else { tickMajorXPositions[index] = x; tickMajorYPositions[index] = y; } } else // use stored positions for printing { int index = ticks.indexOf(new Integer(tick)); if(smallTicks) { x = tickMinorXPositions[index]; y = tickMinorYPositions[index]; } else { x = tickMajorXPositions[index]; y = tickMajorYPositions[index]; } } if(labelTicks && !smallTicks) // add tick label { AffineTransform labelTransform = (AffineTransform)(origin.clone()); labelTransform.translate(location.x+ddiameter2, location.y+ddiameter2); labelTransform.rotate(Math.toRadians(-THETA)); labelTransform.translate(-(location.x+ddiameter2), -(location.y+ddiameter2)); g2.setTransform(labelTransform); g2.drawString(label, location.x+x, location.y+y); g2.setTransform(origin); } g2.setTransform(newOrig); g2.setStroke(new BasicStroke(1.f)); int xLine = location.x+(int)(ddiameter); int yLine = location.y+(int)(ddiameter/2.d); g2.drawLine(xLine,yLine,(int)(xLine+lineSize+widDash),yLine); /* System.out.println("THETA "+Math.toDegrees(theta)); System.out.println("m00 "+newOrig.getScaleX()+ " m01 "+newOrig.getShearX()+ " m02 "+newOrig.getTranslateX()); System.out.println("m10 "+newOrig.getScaleY()+ " m12 "+newOrig.getTranslateY()); */ g2.setTransform(origin); } return; } /** * * Calculate the tick marks to be drawn * */ protected void calculateTickPosistions() { minorTicks = new Vector(); majorTicks = new Vector(); int start = getStart(); int end = getEnd(); if(majorTick == 0) return; for(int i=startTick; i= start) majorTicks.add(new Integer(i)); if(minorTick == 0) return; for(int i=startTick; i= start && !majorTicks.contains(tk)) minorTicks.add(tk); } } /** * * Return the position tick marks start at * */ protected int getStartTick() { return startTick; } /** * * Set the position tick marks start at * */ protected boolean setStartTick(int startTick) { this.startTick = startTick; if((startTick >= getStart()) && (startTick < getEnd())) return true; return false; } /** * * Return the interval for the tick marks * */ protected int getTickInterval() { return majorTick; } /** * * Set the interval for the tick marks * */ public boolean setTickInterval(int majorTick) { if(majorTick < (getEnd()-getStart())) { this.majorTick = majorTick; return true; } return false; } /** * * Return the interval for the tick marks * */ protected int getMinorTickInterval() { return minorTick; } /** * * Set the interval for the tick marks * */ public boolean setMinorTickInterval(int minorTick) { if(minorTick < (getEnd()-getStart())) { this.minorTick = minorTick; return true; } return false; } /** * * Return an angle in degrees * */ protected double getAngleFromPosition(int pos,double rad) { int start = getStart(); int end = getEnd(); return - ((pos-start)*rad)/(end-start); } /** * * The method @print@ must be implemented for @Printable@ interface. * Parameters are supplied by system. * */ public int print(Graphics g, PageFormat pf, int pageIndex) throws PrinterException { Graphics2D g2 = (Graphics2D)g; g2.setColor(Color.black); //set default foreground color to black //RepaintManager.currentManager(this).setDoubleBufferingEnabled(false); Dimension d = this.getSize(); //get size of document double panelWidth = d.width; //width in pixels double panelHeight = d.height; //height in pixels double pageHeight = pf.getImageableHeight(); //height of printer page double pageWidth = pf.getImageableWidth(); //width of printer page double scale = pageWidth/panelWidth; int totalNumPages = (int)Math.ceil(scale * panelHeight / pageHeight); // Make sure not print empty pages if(pageIndex >= totalNumPages) { System.out.println("NO SUCH PAGE "+pageIndex); return Printable.NO_SUCH_PAGE; } // Shift Graphic to line up with beginning of print-imageable region g2.translate(pf.getImageableX(), pf.getImageableY()); // Shift Graphic to line up with beginning of next page to print g2.translate(0f, -pageIndex*pageHeight); // Scale the page so the width fits... g2.scale(scale, scale); try { drawAll(g2,false); } catch(Exception e){ e.printStackTrace(); } return Printable.PAGE_EXISTS; } public void drawAll(Graphics2D g2, boolean l) { if(((Boolean)lineAttr.get("circular")).booleanValue()) drawCircularPanel(g2,l); //repaint the page for printing else drawLinearPanel(g2); if(gcGraph != null && containsGraph(gcGraph)) { if(isCircular()) gcGraph.draw(g2); else gcGraph.drawLinear(g2); } if(gcSkewGraph != null && containsGraph(gcSkewGraph)) { if(isCircular()) gcSkewGraph.draw(g2); else gcSkewGraph.drawLinear(g2); } if(userGraphs.size() > 0) { for(int i=0; i getUserGraphs() { return userGraphs; } public void setGcGraph(Graph gcGraph) { this.gcGraph = gcGraph; } public Graph getGcSkewGraph() { return gcSkewGraph; } public void setGcSkewGraph(Graph gcSkewGraph) { this.gcSkewGraph = gcSkewGraph; } protected void setUserGraph(final UserGraph thisUserGraph) { userGraphs.add(thisUserGraph); JMenuItem thisGraphOptions = new JMenuItem(thisUserGraph.getFileName()); thisGraphOptions.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { thisUserGraph.showOptions(); } }); userOptions.add(thisGraphOptions); final JCheckBoxMenuItem thisGraphDraw = new JCheckBoxMenuItem(thisUserGraph.getFileName()); thisGraphDraw.setSelected(true); thisGraphDraw.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { if(thisGraphDraw.isSelected()) thisUserGraph.setVisible(true); else thisUserGraph.setVisible(false); } }); userDraw.add(thisGraphDraw); } public static void main(final String arg[]) { SwingUtilities.invokeLater(new Runnable() { public void run() { Wizard wiz = null; if(arg.length > 0 && arg[0].equals("-t")) { try { wiz = new Wizard(arg[1]); } catch (Exception e) { System.err.println("\nUnable to load template file: " + arg[1]); System.exit(1); } } else { wiz = new Wizard((DNADraw)null); } final DNADraw dna = wiz.getDNADraw(); // final String version = dna.getVersion(); final JFrame f = new JFrame(); if(version == null) f.setTitle("DNAPlotter"); else f.setTitle("DNAPlotter :: "+version); Dimension d = f.getToolkit().getScreenSize(); jsp.setViewportView(dna); jsp.getViewport().setBackground(Color.white); f.getContentPane().add(jsp); f.setJMenuBar(dna.createMenuBar()); //dna.add(new Graph(dna)); f.pack(); f.setLocation(((int)d.getWidth()-f.getWidth())/4, ((int)d.getHeight()-f.getHeight())/2); //dna.add(dna.new BlockPanel(dna)); f.setVisible(true); if(wiz.getWorkerGraph() != null) wiz.getWorkerGraph().start(); } }); } }