From 577afa99ff4e5f02e564bc0c981901de6bd711ce Mon Sep 17 00:00:00 2001
From: tjc <tjc@ee4ac58c-ac51-4696-9907-e4b3aa274f04>
Date: Wed, 2 Mar 2011 16:32:21 +0000
Subject: [PATCH] change zoom out so it switches to a coverage plot

git-svn-id: svn+ssh://svn.internal.sanger.ac.uk/repos/svn/pathsoft/artemis/trunk@15638 ee4ac58c-ac51-4696-9907-e4b3aa274f04
---
 .../alignment/AbstractGraphPanel.java         |   5 +-
 .../artemis/components/alignment/BamView.java | 274 ++++++++++-----
 .../components/alignment/CoveragePanel.java   | 330 ++++++++++++------
 .../components/alignment/SnpPanel.java        |   2 +-
 4 files changed, 418 insertions(+), 193 deletions(-)

diff --git a/uk/ac/sanger/artemis/components/alignment/AbstractGraphPanel.java b/uk/ac/sanger/artemis/components/alignment/AbstractGraphPanel.java
index d06e68d6e..17d9d1e01 100644
--- a/uk/ac/sanger/artemis/components/alignment/AbstractGraphPanel.java
+++ b/uk/ac/sanger/artemis/components/alignment/AbstractGraphPanel.java
@@ -31,6 +31,7 @@ import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 
 import javax.swing.JCheckBox;
+import javax.swing.JComponent;
 import javax.swing.JLabel;
 import javax.swing.JMenuItem;
 import javax.swing.JOptionPane;
@@ -49,7 +50,7 @@ public class AbstractGraphPanel extends JPanel
   protected int userWinSize = 1;
   protected JPopupMenu popup = new JPopupMenu();
   
-  public void initPopupMenu(final JPanel graphPanel)
+  public void initPopupMenu(final JPanel graphPanel, JComponent menu)
   {
     final JMenuItem setScale = new JMenuItem("Set the Window Size...");
     setScale.addActionListener(new ActionListener()
@@ -97,7 +98,7 @@ public class AbstractGraphPanel extends JPanel
         graphPanel.repaint();
       }
     });
-    popup.add(setScale);
+    menu.add(setScale);
     
     addMouseListener(new PopupListener());
   }
diff --git a/uk/ac/sanger/artemis/components/alignment/BamView.java b/uk/ac/sanger/artemis/components/alignment/BamView.java
index 630b0298d..3a66ed5bd 100644
--- a/uk/ac/sanger/artemis/components/alignment/BamView.java
+++ b/uk/ac/sanger/artemis/components/alignment/BamView.java
@@ -148,9 +148,7 @@ public class BamView extends JPanel
   private boolean isOrientation = false;
   private boolean isSingle = false;
   private boolean isSNPs = false;
-  private boolean isStackView = true;
-  private boolean isPairedStackView = false;
-  private boolean isStrandStackView = false;
+  
   private boolean isCoverage = false;
   private boolean isSNPplot = false;
   
@@ -175,18 +173,29 @@ public class BamView extends JPanel
   
   private JMenu bamFilesMenu = new JMenu("BAM files");
   private JCheckBoxMenuItem logMenuItem = new JCheckBoxMenuItem("Use Log Scale", logScale);
-  private JCheckBoxMenuItem checkBoxStackView = new JCheckBoxMenuItem("Stack View", isStackView);
+  
+  private JCheckBoxMenuItem cbStackView = new JCheckBoxMenuItem("Stack", true);
+  private JCheckBoxMenuItem cbPairedStackView = new JCheckBoxMenuItem("Paired Stack");
+  private JCheckBoxMenuItem cbStrandStackView = new JCheckBoxMenuItem("Strand Stack");
+  private JCheckBoxMenuItem cbIsizeStackView = new JCheckBoxMenuItem("Inferred Size", false);
+  private JCheckBoxMenuItem cbCoverageView = new JCheckBoxMenuItem("Coverage", false);
+  private JCheckBoxMenuItem cbLastSelected;
+  
+  private ButtonGroup buttonGroup = new ButtonGroup();
+  
   private JCheckBoxMenuItem colourByCoverageColour = new JCheckBoxMenuItem("Coverage Plot Colours");
   private JCheckBoxMenuItem baseQualityColour = new JCheckBoxMenuItem("Base Quality");
   private JCheckBoxMenuItem markInsertions = new JCheckBoxMenuItem("Mark Insertions", true);
   private AlphaComposite translucent = 
     AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.6f);
   
+  private CoveragePanel coverageView = new CoveragePanel();
+  
   /** Used to colour the frames. */
-  private Color lightGrey = new Color(200, 200, 200);
-  private Color darkGreen = new Color(0, 150, 0);
-  private Color darkOrange = new Color(255,140,0);
-  private Color deepPink   = new Color(139,10,80);
+  private static Color LIGHT_GREY = new Color(200, 200, 200);
+  private static Color DARK_GREEN = new Color(0, 150, 0);
+  private static Color DARK_ORANGE = new Color(255,140,0);
+  private static Color DEEP_PINK   = new Color(139,10,80);
   
   private Point lastMousePoint = null;
   private SAMRecord mouseOverSAMRecord = null;
@@ -195,6 +204,7 @@ public class BamView extends JPanel
   // record of where a mouse drag starts
   private int dragStart = -1;
   
+  private static int MAX_BASES = 26000;
   private int maxHeight = 800;
   
   private boolean concatSequences = false;
@@ -270,6 +280,12 @@ public class BamView extends JPanel
     
     MultiLineToolTipUI.initialize();
     setToolTipText("");
+    
+    buttonGroup.add(cbStackView);
+    buttonGroup.add(cbPairedStackView);
+    buttonGroup.add(cbStrandStackView);
+    buttonGroup.add(cbIsizeStackView);
+    buttonGroup.add(cbCoverageView);
   }
   
   public String getToolTipText()
@@ -392,7 +408,7 @@ public class BamView extends JPanel
    * Read a SAM or BAM file.
    * @throws IOException 
    */
-  private void readFromBamPicard(int start, int end, int bamIndex) 
+  private void readFromBamPicard(int start, int end, int bamIndex, float pixPerBase) 
           throws IOException
   {
     // Open the input file.  Automatically detects whether input is SAM or BAM
@@ -426,7 +442,7 @@ public class BamView extends JPanel
             thisEnd = thisLength;
           
           //System.out.println("READ "+seqNames.get(i)+"  "+thisStart+".."+thisEnd);
-          iterateOverBam(inputSam, seqNames.get(i), thisStart, thisEnd, bamIndex);
+          iterateOverBam(inputSam, seqNames.get(i), thisStart, thisEnd, bamIndex, pixPerBase);
         }
         lastLen = len;
       }
@@ -434,7 +450,7 @@ public class BamView extends JPanel
     else
     {
       String refName = (String) combo.getSelectedItem();
-      iterateOverBam(inputSam, refName, start, end, bamIndex);
+      iterateOverBam(inputSam, refName, start, end, bamIndex, pixPerBase);
     }
     
     //inputSam.close();
@@ -452,7 +468,7 @@ public class BamView extends JPanel
    */
   private void iterateOverBam(final SAMFileReader inputSam, 
                               String refName, int start, int end,
-                              int bamIndex)
+                              int bamIndex, float pixPerBase)
   { 
     boolean multipleBAM = false;
     if(bamList.size() > 1)
@@ -478,7 +494,12 @@ public class BamView extends JPanel
           {
             if(multipleBAM)
               samRecord.setAttribute("FL", bamIndex);
-            readsInView.add(samRecord);
+            
+            if(isCoverageView(pixPerBase) || isCoverage)
+              coverageView.addRecord(samRecord);
+            
+            if(!isCoverageView(pixPerBase))
+              readsInView.add(samRecord);
           }
         }
         
@@ -610,11 +631,18 @@ public class BamView extends JPanel
         end = seqLength;
     }
     
+    
     boolean changeToStackView = false;
     MemoryMXBean memory = ManagementFactory.getMemoryMXBean();
     if(laststart != start ||
-       lastend   != end)
+       lastend   != end ||
+       coverageView.isRedraw())
     {
+      if(isCoverageView(pixPerBase) || isCoverage)
+      {
+        coverageView.init(this, pixPerBase, start, end);
+      }
+      
       synchronized (this)
       {
         try
@@ -626,11 +654,11 @@ public class BamView extends JPanel
             readsInView = new Vector<SAMRecord>();
           else
             readsInView.clear();
-          
+
           for(int i=0; i<bamList.size(); i++)
           {
             if(!hideBamList.contains(i))
-              readFromBamPicard(start, end, i);
+              readFromBamPicard(start, end, i, pixPerBase);
           }
           float heapFractionUsedAfter = (float) ((float) memory.getHeapMemoryUsage().getUsed() / 
                                                  (float) memory.getHeapMemoryUsage().getMax());
@@ -640,19 +668,17 @@ public class BamView extends JPanel
           // System.out.println("Heap memory used "+heapFractionUsedAfter);
 
           if ((heapFractionUsedAfter - heapFractionUsedBefore) > 0.06
-              && !isStackView && heapFractionUsedAfter > 0.8)
+              && !isStackView() && heapFractionUsedAfter > 0.8)
           {
-            checkBoxStackView.setSelected(true);
-            isStackView = true;
+            cbStackView.setSelected(true);
             changeToStackView = true;
           }
 
-          if ((!isStackView && !isStrandStackView)
-              || pixPerBase * 1.08f >= ALIGNMENT_PIX_PER_BASE)
+          if((!isStackView() && !isStrandStackView()) || isBaseAlignmentView(pixPerBase))
           {
             Collections.sort(readsInView, new SAMRecordComparator());
           }
-          else if( (isStackView || isStrandStackView) &&
+          else if( (isStackView() || isStrandStackView()) &&
               bamList.size() > 1)
           {
             // merge multiple BAM files
@@ -679,18 +705,26 @@ public class BamView extends JPanel
     //System.out.println(start+".."+end+" " +
     //    "sequence length = "+getSequenceLength()+
     //    " pixPerBase="+pixPerBase);
-    
+       
     laststart = start;
     lastend   = end;
-	if(showBaseAlignment)
+    
+    if(showBaseAlignment)
 	  drawBaseAlignment(g2, seqLength, pixPerBase, start, end);
 	else
 	{
-	  if(isStackView)  
+	  if(isCoverageView(pixPerBase))
+	  {
+	    int hgt = jspView.getVisibleRect().height;
+	    g2.translate(0, getHeight()-hgt);
+	    coverageView.draw(g2, getWidth(), hgt);
+	    coverageView.drawMax(g2);
+	  }
+	  else if(isStackView())  
 	    drawStackView(g2, seqLength, pixPerBase, start, end);
-	  else if(isPairedStackView)
+	  else if(isPairedStackView())
 	    drawPairedStackView(g2, seqLength, pixPerBase, start, end);
-	  else if(isStrandStackView)
+	  else if(isStrandStackView())
 	    drawStrandStackView(g2, seqLength, pixPerBase, start, end);
 	  else
 	    drawLineView(g2, seqLength, pixPerBase, start, end);
@@ -778,7 +812,7 @@ public class BamView extends JPanel
         refSeq = 
           bases.getSubSequence(new Range(refSeqStart, seqEnd), Bases.FORWARD).toUpperCase();
         
-        g2.setColor(lightGrey);
+        g2.setColor(LIGHT_GREY);
         g2.fillRect(0, ypos-11, mainPanel.getWidth(), 11);
         drawSelectionRange(g2, ALIGNMENT_PIX_PER_BASE, start, end);
         g2.setColor(Color.black);
@@ -925,7 +959,7 @@ public class BamView extends JPanel
           if(insertions == null)
             insertions = new Hashtable<Integer, String>();
 
-          g2.setColor(deepPink);
+          g2.setColor(DEEP_PINK);
 
           int xscreen = (refPos+1)*ALIGNMENT_PIX_PER_BASE;
           insertions.put(xscreen, 
@@ -1001,9 +1035,9 @@ public class BamView extends JPanel
     if (baseQuality < 10)
       g2.setColor(Color.blue);
     else if (baseQuality < 20)
-      g2.setColor(darkGreen);
+      g2.setColor(DARK_GREEN);
     else if (baseQuality < 30)
-      g2.setColor(darkOrange);
+      g2.setColor(DARK_ORANGE);
     else
       g2.setColor(Color.black);
   }
@@ -1189,7 +1223,7 @@ public class BamView extends JPanel
           ypos = ypos-2;
       }
       else
-        g2.setColor(darkGreen);
+        g2.setColor(DARK_GREEN);
 
       lstStart = recordStart;
       lstEnd   = recordEnd;
@@ -1283,7 +1317,7 @@ public class BamView extends JPanel
             ypos = ypos + ystep;
         }
         else
-          g2.setColor(darkGreen);
+          g2.setColor(DARK_GREEN);
 
         lstStart = recordStart;
         lstEnd   = recordEnd;
@@ -1494,7 +1528,7 @@ public class BamView extends JPanel
     if(colourByCoverageColour.isSelected())
       g2.setColor(getColourByCoverageColour(samRecord));
     else if(offTheTop)
-      g2.setColor(darkOrange); 
+      g2.setColor(DARK_ORANGE); 
     else if(samRecord.getReadNegativeStrandFlag() &&
             samRecord.getMateNegativeStrandFlag()) // strand of the query (1 for reverse)
       g2.setColor(Color.red);
@@ -2143,104 +2177,83 @@ public class BamView extends JPanel
     menu.add(new JSeparator());
     
     JMenu viewMenu = new JMenu("Views");
-    ButtonGroup group = new ButtonGroup();
-    final JCheckBoxMenuItem checkBoxPairedStackView = new JCheckBoxMenuItem("Paired Stack View");
-    final JCheckBoxMenuItem checkBoxStrandStackView = new JCheckBoxMenuItem("Strand Stack View");
-    final JCheckBoxMenuItem checkIsizeStackView = new JCheckBoxMenuItem("Inferred Size View", false);
-    checkBoxStackView.setFont(checkIsizeStackView.getFont());
-    baseQualityColour.setFont(checkIsizeStackView.getFont());
-    colourByCoverageColour.setFont(checkIsizeStackView.getFont());
-    markInsertions.setFont(checkIsizeStackView.getFont());
-    
-    group.add(checkBoxStackView);
-    group.add(checkBoxPairedStackView);
-    group.add(checkBoxStrandStackView);
-    group.add(checkIsizeStackView);
-    
-    checkIsizeStackView.addActionListener(new ActionListener()
+    cbStackView.setFont(cbIsizeStackView.getFont());
+    baseQualityColour.setFont(cbIsizeStackView.getFont());
+    colourByCoverageColour.setFont(cbIsizeStackView.getFont());
+    markInsertions.setFont(cbIsizeStackView.getFont());
+    
+    cbIsizeStackView.addActionListener(new ActionListener()
     {
       public void actionPerformed(ActionEvent e)
       {
         laststart = -1;
         lastend = -1;
         
-        if(checkIsizeStackView.isSelected())
-        {
-          isStackView = false;
-          isPairedStackView = false;
-          isStrandStackView = false;
+        if(isIsizeStackView())
           logMenuItem.setEnabled(true);
-        }
         repaint();
       }
     });
-    viewMenu.add(checkIsizeStackView);
+    viewMenu.add(cbIsizeStackView);
     
     
-    checkBoxStackView.addActionListener(new ActionListener()
+    cbStackView.addActionListener(new ActionListener()
     {
       public void actionPerformed(ActionEvent e)
       {
         laststart = -1;
         lastend = -1;
-        isStackView = !isStackView;
-        
-        if(isStackView)
-        {
-          isPairedStackView = !isStackView;
-          isStrandStackView = !isStackView;
-          checkBoxPairedStackView.setSelected(!isStackView);
-          checkBoxStrandStackView.setSelected(!isStackView);
+
+        if(isStackView())
           logMenuItem.setEnabled(false);
-        }
         repaint();
       }
     });
-    viewMenu.add(checkBoxStackView);
+    viewMenu.add(cbStackView);
     
 
-    checkBoxPairedStackView.addActionListener(new ActionListener()
+    cbPairedStackView.addActionListener(new ActionListener()
     {
       public void actionPerformed(ActionEvent e)
       {
         laststart = -1;
         lastend = -1;
-        isPairedStackView = !isPairedStackView;
         
-        if(isPairedStackView)
-        {
-          isStackView = !isPairedStackView;
-          isStrandStackView = !isPairedStackView;
-          checkBoxStackView.setSelected(!isPairedStackView);
-          checkBoxStrandStackView.setSelected(!isPairedStackView);
+        if(cbPairedStackView.isSelected())
           logMenuItem.setEnabled(false);
-        }
         repaint();
       }
     });
-    viewMenu.add(checkBoxPairedStackView);
+    viewMenu.add(cbPairedStackView);
     
-    checkBoxStrandStackView.addActionListener(new ActionListener()
+    cbStrandStackView.addActionListener(new ActionListener()
     {
       public void actionPerformed(ActionEvent e)
       {
         laststart = -1;
         lastend = -1;
-        isStrandStackView = !isStrandStackView;
-        
-        if(isStrandStackView)
+
+        if(isStrandStackView())
         {
-          isStackView = !isStrandStackView;
-          isPairedStackView = !isStrandStackView;
-          checkBoxStackView.setSelected(!isStrandStackView);
-          checkBoxPairedStackView.setSelected(!isStrandStackView);
           setViewportMidPoint();
           logMenuItem.setEnabled(false);
         }
         repaint();
       }
     });
-    viewMenu.add(checkBoxStrandStackView);  
+    viewMenu.add(cbStrandStackView);
+    
+    cbCoverageView.addActionListener(new ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        laststart = -1;
+        lastend = -1;
+        repaint();
+      }
+    });
+    viewMenu.add(cbCoverageView);
+    
     menu.add(viewMenu);
  
     final JCheckBoxMenuItem checkBoxSNPs = new JCheckBoxMenuItem("SNP marks");
@@ -2325,6 +2338,8 @@ public class BamView extends JPanel
       {
         isCoverage = !isCoverage;
         coveragePanel.setVisible(isCoverage);
+        laststart = -1;
+        lastend = -1;
         repaint();
       }
     });
@@ -2387,7 +2402,7 @@ public class BamView extends JPanel
     }
     
     menu.add(new JSeparator());
-    logMenuItem.setFont(checkIsizeStackView.getFont());
+    logMenuItem.setFont(cbIsizeStackView.getFont());
     menu.add(logMenuItem);
     logMenuItem.addActionListener(new ActionListener()
     {
@@ -2407,7 +2422,13 @@ public class BamView extends JPanel
         new SAMRecordFilter(BamView.this);
       } 
     });
-    menu.add(new JSeparator());
+
+    //
+    JMenu coverageMenu = new JMenu("Coverage Options");
+    coverageView.init(this, 0.f, 0, 0);
+    coverageView.createMenus(coverageMenu, BamView.this);
+    viewMenu.add(new JSeparator());
+    viewMenu.add(coverageMenu);
   }
   
   public void setVisible(boolean visible)
@@ -2442,7 +2463,7 @@ public class BamView extends JPanel
     this.nbasesInView = nbasesInView;
     float pixPerBase = getPixPerBaseByWidth(); 
 
-    if(pixPerBase*1.08f >= ALIGNMENT_PIX_PER_BASE)
+    if(isBaseAlignmentView(pixPerBase))
     {
       pixPerBase = ALIGNMENT_PIX_PER_BASE;
       this.nbasesInView = (int)(mainPanel.getWidth()/pixPerBase);
@@ -2457,9 +2478,20 @@ public class BamView extends JPanel
     }
     else if(jspView != null)
     {
+      if(!cbCoverageView.isSelected() && nbasesInView >= MAX_BASES)
+      {
+        cbLastSelected = getSelectedCheckBoxMenuItem();
+        cbCoverageView.setSelected(true);
+      }
+      else if(cbCoverageView.isSelected() && nbasesInView < MAX_BASES && cbLastSelected != null)
+      {
+        cbLastSelected.setSelected(true);
+        cbLastSelected = null;
+      }
+      
       jspView.setColumnHeaderView(null);
       
-      if(!isStrandStackView)
+      if(!isStrandStackView())
         jspView.getVerticalScrollBar().setValue(
             jspView.getVerticalScrollBar().getMaximum());
       showBaseAlignment = false;
@@ -2661,6 +2693,63 @@ public class BamView extends JPanel
       fileIndex = (Integer) samRecord.getAttribute("FL");
     return lines[fileIndex].getLineColour(); 
   }
+
+  protected int getMaxBases()
+  {
+    return MAX_BASES;
+  }
+  
+  protected void setMaxBases(int max)
+  {
+    MAX_BASES = max;
+  }
+  
+  private boolean isStackView()
+  {
+    return cbStackView.isSelected();  
+  }
+  
+  private boolean isPairedStackView()
+  {
+    return cbPairedStackView.isSelected();
+  }
+  
+  private boolean isStrandStackView()
+  {
+    return cbStrandStackView.isSelected();
+  }
+  
+  private boolean isCoverageView(float pixPerBase)
+  {
+    if(isBaseAlignmentView(pixPerBase))
+      return false;
+    return cbCoverageView.isSelected();
+  }
+  
+  private boolean isIsizeStackView()
+  {
+    return cbIsizeStackView.isSelected();
+  }
+  
+  private boolean isBaseAlignmentView(float pixPerBase)
+  {
+    if(pixPerBase*1.08f >= ALIGNMENT_PIX_PER_BASE)
+      return true;
+    return false;
+  }
+  
+  private JCheckBoxMenuItem getSelectedCheckBoxMenuItem()
+  {
+    if(isStackView())
+      return cbStackView;
+    if(isPairedStackView())
+      return cbPairedStackView;
+    if(isStrandStackView())
+      return cbStrandStackView;
+    if(isIsizeStackView())
+      return cbIsizeStackView;
+    return cbCoverageView;
+  }
   
   private Selection getSelection()
   {
@@ -2672,6 +2761,11 @@ public class BamView extends JPanel
     return readsInView;
   }
   
+  protected Hashtable<String, int[]> getCoveragePlotData()
+  {
+    return coverageView.getPlotData();
+  }
+  
   protected int getBasesInView()
   {
     return nbasesInView;
diff --git a/uk/ac/sanger/artemis/components/alignment/CoveragePanel.java b/uk/ac/sanger/artemis/components/alignment/CoveragePanel.java
index 62336f11c..130ddaf4c 100644
--- a/uk/ac/sanger/artemis/components/alignment/CoveragePanel.java
+++ b/uk/ac/sanger/artemis/components/alignment/CoveragePanel.java
@@ -29,6 +29,8 @@ import java.awt.Color;
 import java.awt.FontMetrics;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.geom.GeneralPath;
@@ -36,8 +38,13 @@ import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.List;
 
-import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JCheckBox;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
 import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
 
 import net.sf.samtools.AlignmentBlock;
 import net.sf.samtools.SAMRecord;
@@ -45,46 +52,62 @@ import net.sf.samtools.SAMRecord;
   public class CoveragePanel extends AbstractGraphPanel
   {
     private static final long serialVersionUID = 1L;
-    private BamView jamView;
+    private BamView bamView;
 
     private static LineAttributes lines[];
     private boolean includeCombined = false;
+    private Hashtable<String, int[]> plots;
+    private int combinedCoverage[];
+    private int nBins;
+    private int windowSize;
+    private int max;
+    private boolean redraw = false;
+    private boolean setMaxBases = false;
     
-    public CoveragePanel(final BamView jamView)
+    protected CoveragePanel(final BamView bamView)
+    {
+      this();
+      this.bamView = bamView;
+      //initPopupMenu(this, popup);
+      createMenus(popup, this);
+      addMouseListener(new PopupListener());
+    }
+    
+    protected CoveragePanel()
     {
       super();
       setBackground(Color.white);
-      this.jamView = jamView;
-      initPopupMenu(this);
-
-      JMenuItem configure = new JMenuItem("Configure...");
+      addMouseListener(new PopupListener());
+      setMaxBases = true;
+    }
+    
+    protected void createMenus(JComponent menu, final JPanel panel)
+    {
+      JMenuItem configure = new JMenuItem("Configure Line(s)...");
       configure.addActionListener(new ActionListener()
       {
         public void actionPerformed(ActionEvent e)
         {
-          int size = jamView.bamList.size();
+          int size = bamView.bamList.size();
           if(includeCombined)
             size++;
           lines =
-            LineAttributes.configurePlots(jamView.bamList, 
+            LineAttributes.configurePlots(bamView.bamList, 
                 getLineAttributes(size), CoveragePanel.this);
         }
       });
-      popup.add(configure);
+      menu.add(configure);
       
-      if(jamView.bamList.size() > 1)
+      JMenuItem optMenu = new JMenuItem("Options...");
+      optMenu.addActionListener(new ActionListener()
       {
-        final JCheckBoxMenuItem showCombined = new JCheckBoxMenuItem("Show Combined Plot", false);
-        showCombined.addActionListener(new ActionListener()
+        public void actionPerformed(ActionEvent e)
         {
-          public void actionPerformed(ActionEvent e)
-          {
-            includeCombined = showCombined.isSelected();
-            repaint();
-          }
-        });
-        popup.add(showCombined);
-      }
+          defineOpts();
+          panel.repaint();
+        }
+      });
+      menu.add(optMenu);
     }
     
     /**
@@ -94,15 +117,27 @@ import net.sf.samtools.SAMRecord;
     {
       super.paintComponent(g);
       Graphics2D g2 = (Graphics2D)g;
-
-      List<SAMRecord> readsInView = jamView.getReadsInView();
+      List<SAMRecord> readsInView = bamView.getReadsInView();
       if(readsInView == null)
         return;
-
-      int windowSize;
+      drawPlot(g2);
+      drawMax(g2);
+    }
+    
+    protected void init(BamView bamView, float pixPerBase, int start, int end)
+    {
+      this.bamView = bamView;
+      setPixPerBase(pixPerBase);
+      setStartAndEnd(start, end);
+      init();
+      redraw = false;
+    }
+    
+    private void init()
+    {
       if(autoWinSize)
       {
-        windowSize = (jamView.getBasesInView()/200);
+        windowSize = (bamView.getBasesInView()/300);
         userWinSize = windowSize;
       }
       else
@@ -110,84 +145,97 @@ import net.sf.samtools.SAMRecord;
       
       if(windowSize < 1)
         windowSize = 1;
+      nBins = Math.round((end-start+1.f)/windowSize);
 
-      int nBins = Math.round((end-start+1.f)/windowSize);
-      int max = drawPlot(g2, nBins, windowSize);
-
+      plots = new Hashtable<String, int[]>();
+      combinedCoverage = null;
+      if(includeCombined)
+      {
+        combinedCoverage = new int[nBins];
+        for(int k=0; k<combinedCoverage.length; k++)
+          combinedCoverage[k] = 0;
+        plots.put("-1", combinedCoverage);
+      }
+      max = 0;
+    }
+    
+    protected void drawMax(Graphics2D g2)
+    {
       String maxStr = Float.toString(max/windowSize);
       FontMetrics fm = getFontMetrics(getFont());
       g2.setColor(Color.black);
       
-      int xpos = getWidth() - fm.stringWidth(maxStr) - 
-                 jamView.getJspView().getVerticalScrollBar().getWidth();
+      int xpos = bamView.getJspView().getVisibleRect().width - fm.stringWidth(maxStr) - 
+                 bamView.getJspView().getVerticalScrollBar().getWidth();
       g2.drawString(maxStr, xpos, fm.getHeight());
     }
     
+    private void drawPlot(Graphics2D g2)
+    {
+      init();
+      max = 0;
+      plots = bamView.getCoveragePlotData();
+      Enumeration<String> plotEum = plots.keys();
+      while(plotEum.hasMoreElements())
+      {
+        String fileName = (String) plotEum.nextElement();
+        int[] thisPlot = plots.get(fileName);
+        for(int i=1; i<thisPlot.length; i++)
+          if(max < thisPlot[i])
+            max = thisPlot[i];
+      }
+
+      draw(g2, getWidth(), getHeight());
+    }
     
-    private int drawPlot(Graphics2D g2, int nBins, int windowSize)
+    protected void addRecord(SAMRecord thisRead)
     {
-      List<SAMRecord> readsInView = jamView.getReadsInView();
-      List<String> bamList = jamView.bamList;
-      final Hashtable<String, Integer[]> plots = new Hashtable<String, Integer[]>();
+      int offset = bamView.getSequenceOffset(thisRead.getReferenceName());
+      offset = offset - bamView.getBaseAtStartOfView();
+
+      String fileName;
+      if(bamView.bamList.size() > 1)
+        fileName = bamView.bamList.get((Integer) thisRead.getAttribute("FL"));
+      else
+        fileName = bamView.bamList.get(0);
+      int coverage[] = plots.get(fileName);
       
-      Integer combinedCoverage[] = null;
-      if(includeCombined)
+      if(coverage == null)
       {
-        combinedCoverage = new Integer[nBins];
-        for(int k=0; k<combinedCoverage.length; k++)
-          combinedCoverage[k] = 0;
-        plots.put("-1", combinedCoverage);
-      }
+        coverage = new int[nBins];
+        for(int k=0; k<coverage.length; k++)
+          coverage[k] = 0;
+        plots.put(fileName, coverage);
+      }         
       
-      int max = 0;
-      for(int i=0; i<readsInView.size(); i++)
+      List<AlignmentBlock> blocks = thisRead.getAlignmentBlocks();
+      for(int j=0; j<blocks.size(); j++)
       {
-        SAMRecord thisRead = readsInView.get(i);
-        int offset = jamView.getSequenceOffset(thisRead.getReferenceName());
-        offset = offset - jamView.getBaseAtStartOfView();
-
-        String fileName;
-        if(bamList.size() > 1)
-          fileName = bamList.get((Integer) thisRead.getAttribute("FL"));
-        else
-          fileName = bamList.get(0);
-        Integer coverage[] = plots.get(fileName);
-        
-        if(coverage == null)
-        {
-          coverage = new Integer[nBins];
-          for(int k=0; k<coverage.length; k++)
-            coverage[k] = 0;
-          plots.put(fileName, coverage);
-        }         
-        
-        List<AlignmentBlock> blocks = thisRead.getAlignmentBlocks();
-        for(int j=0; j<blocks.size(); j++)
+        AlignmentBlock block = blocks.get(j);
+        for(int k=0; k<block.getLength(); k++)
         {
-          AlignmentBlock block = blocks.get(j);
- 
-          for(int k=0; k<block.getLength(); k++)
+          int pos = block.getReferenceStart() + k + offset;
+          int bin = pos/windowSize;
+          if(bin < 0 || bin > nBins-1)
+            continue;
+          
+          coverage[bin]+=1;
+          if(coverage[bin] > max)
+            max = coverage[bin];
+          
+          if(includeCombined)
           {
-            int pos = block.getReferenceStart() + k + offset;
-            int bin = pos/windowSize;
-            if(bin < 0 || bin > nBins-1)
-              continue;
-            
-            coverage[bin]+=1;
-            if(coverage[bin] > max)
-              max = coverage[bin];
-            
-            if(includeCombined)
-            {
-              combinedCoverage[bin]+=1;
-              if(combinedCoverage[bin] > max)
-                max = combinedCoverage[bin];
-            }
-          } 
-        }
+            combinedCoverage[bin]+=1;
+            if(combinedCoverage[bin] > max)
+              max = combinedCoverage[bin];
+          }
+        } 
       }
-
-      int size = jamView.bamList.size();
+    }
+    
+    protected void draw(Graphics2D g2, int wid, int hgt)
+    {
+      int size = bamView.bamList.size();
       if(includeCombined)
       {
         lines = getLineAttributes(size+1);
@@ -200,13 +248,13 @@ import net.sf.samtools.SAMRecord;
       while(plotEum.hasMoreElements())
       {
         String fileName = (String) plotEum.nextElement();
-        Integer[] thisPlot = plots.get(fileName);
-        
+        int[] thisPlot = plots.get(fileName);
+
         int index;
         if(fileName.equals("-1"))
           index = lines.length-1;
         else
-          index = bamList.indexOf(fileName);
+          index = bamView.bamList.indexOf(fileName);
         
         g2.setColor(lines[index].getLineColour());
         
@@ -216,10 +264,10 @@ import net.sf.samtools.SAMRecord;
           for(int i=1; i<thisPlot.length; i++)
           {
             int x0 = (int) ((((i-1)*(windowSize)) - windowSize/2.f)*pixPerBase);
-            int y0 = (int) (getHeight() - (((float)thisPlot[i-1]/(float)max)*getHeight()));
+            int y0 = (int) (hgt - (((float)thisPlot[i-1]/(float)max)*hgt));
             int x1 = (int) (((i*(windowSize)) - windowSize/2.f)*pixPerBase);
-            int y1 = (int) (getHeight() - (((float)thisPlot[i]/(float)max)*getHeight()));
-            
+            int y1 = (int) (hgt - (((float)thisPlot[i]/(float)max)*hgt));
+
             g2.drawLine(x0, y0, x1, y1);
           }
         }
@@ -228,28 +276,26 @@ import net.sf.samtools.SAMRecord;
           g2.setComposite(makeComposite(0.75f));
 
           GeneralPath shape = new GeneralPath();
-          shape.moveTo(0,getHeight());
+          shape.moveTo(0,hgt);
           for(int i=0; i<thisPlot.length; i++)
           {
             float xpos = ((i*(windowSize)) - windowSize/2.f)*pixPerBase;
             shape.lineTo(xpos,
-                getHeight() - (((float)thisPlot[i]/(float)max)*getHeight()));
+                hgt - (((float)thisPlot[i]/(float)max)*hgt));
           }
 
-          shape.lineTo(getWidth(),getHeight());
+          shape.lineTo(wid,hgt);
           g2.fill(shape);
         }
       }
-      return max;
     }
     
     private AlphaComposite makeComposite(float alpha)
     {
       int type = AlphaComposite.SRC_OVER;
       return(AlphaComposite.getInstance(type, alpha));
-     }
-  
-    
+    }
+
     protected static LineAttributes[] getLineAttributes(int nsize)
     {
       if(lines == null)
@@ -263,4 +309,88 @@ import net.sf.samtools.SAMRecord;
       }
       return lines;
     }
+
+    /**
+     * @return the redraw
+     */
+    protected boolean isRedraw()
+    {
+      return redraw;
+    }
+    
+    private void defineOpts()
+    {
+      final JPanel opts = new JPanel(new GridBagLayout());
+      final GridBagConstraints c = new GridBagConstraints();
+
+      final JTextField newBaseMax = new JTextField(Integer.toString(bamView.getMaxBases()), 10);
+      c.gridy = 0;
+      if(setMaxBases)
+      {
+        final JLabel labMax1 = new JLabel("Zoom level before switching");
+        final JLabel labMax2 = new JLabel("to coverage view (in bases):");
+        c.anchor = GridBagConstraints.WEST;
+        opts.add(labMax1, c);
+        c.gridy = c.gridy+1;
+        opts.add(labMax2, c);
+        opts.add(newBaseMax, c);
+      }
+      
+      final JTextField newWinSize = new JTextField(Integer.toString(userWinSize), 10);
+      final JLabel lab = new JLabel("Window size:");
+      lab.setEnabled(!autoWinSize);
+      newWinSize.setEnabled(!autoWinSize);
+      
+      c.gridy = c.gridy+1;
+      c.anchor = GridBagConstraints.EAST;
+      opts.add(lab, c);
+      opts.add(newWinSize, c);
+
+      final JCheckBox autoSet = new JCheckBox("Automatically set window size", autoWinSize);
+      autoSet.addActionListener(new ActionListener()
+      {
+        public void actionPerformed(ActionEvent e)
+        {
+          lab.setEnabled(!autoSet.isSelected());
+          newWinSize.setEnabled(!autoSet.isSelected());
+        }
+      });
+      c.anchor = GridBagConstraints.WEST;
+      c.gridy = c.gridy+1;
+      c.gridwidth = GridBagConstraints.REMAINDER;
+      opts.add(autoSet, c);
+
+      final JCheckBox showCombined = new JCheckBox("Show Combined Plot", includeCombined);
+      if(bamView.bamList.size() == 1)
+        showCombined.setEnabled(false);
+      c.gridy = c.gridy+1;
+      opts.add(showCombined, c);
+          
+      String window_options[] = { "OK", "Cancel" };
+      int select = JOptionPane.showOptionDialog(null, opts, "Coverage Options",
+          JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null,
+          window_options, window_options[0]);
+
+      if(select == 1)
+        return;
+      
+      redraw = true;
+      autoWinSize = autoSet.isSelected();
+      includeCombined = showCombined.isSelected();
+      try
+      {
+        userWinSize = Integer.parseInt(newWinSize.getText().trim());
+        if(setMaxBases)
+          bamView.setMaxBases(Integer.parseInt(newBaseMax.getText().trim()));
+      }
+      catch (NumberFormatException nfe)
+      {
+        return;
+      }
+    }
+    
+    protected Hashtable<String, int[]> getPlotData()
+    {
+      return plots;
+    }
   }
\ No newline at end of file
diff --git a/uk/ac/sanger/artemis/components/alignment/SnpPanel.java b/uk/ac/sanger/artemis/components/alignment/SnpPanel.java
index 80b4349bd..453ae6fae 100644
--- a/uk/ac/sanger/artemis/components/alignment/SnpPanel.java
+++ b/uk/ac/sanger/artemis/components/alignment/SnpPanel.java
@@ -60,7 +60,7 @@ import net.sf.samtools.SAMRecord;
       setBackground(Color.white);
       this.bamView = bamView;
       this.bases = bases;
-      initPopupMenu(this);
+      initPopupMenu(this, popup);
       
       JMenuItem configure = new JMenuItem("Filter by Base Quality...");
       configure.addActionListener(new ActionListener()
-- 
GitLab