diff --git a/uk/ac/sanger/artemis/components/alignment/JamView.java b/uk/ac/sanger/artemis/components/alignment/JamView.java
new file mode 100644
index 0000000000000000000000000000000000000000..e73eaf3171c5020bd8b485f0098686d824e6c81b
--- /dev/null
+++ b/uk/ac/sanger/artemis/components/alignment/JamView.java
@@ -0,0 +1,473 @@
+
+package uk.ac.sanger.artemis.components.alignment;
+
+import java.awt.BasicStroke;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Stroke;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Vector;
+
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+
+import uk.ac.sanger.artemis.Entry;
+import uk.ac.sanger.artemis.EntryGroup;
+import uk.ac.sanger.artemis.Options;
+import uk.ac.sanger.artemis.SimpleEntryGroup;
+import uk.ac.sanger.artemis.components.EntryFileDialog;
+import uk.ac.sanger.artemis.components.MessageDialog;
+import uk.ac.sanger.artemis.io.EntryInformation;
+import uk.ac.sanger.artemis.sequence.Bases;
+import uk.ac.sanger.artemis.sequence.NoSequenceException;
+import uk.ac.sanger.artemis.util.Document;
+import uk.ac.sanger.artemis.util.DocumentFactory;
+import uk.ac.sanger.artemis.util.OutOfRangeException;
+
+public class JamView extends JPanel
+{
+  private static final long serialVersionUID = 1L;
+  private List<Read> readsInView;
+  private Hashtable<String, Integer> seqLengths = new Hashtable<String, Integer>();
+  private Vector<String> seqNames = new Vector<String>();
+  private String bam;
+  private String sam;
+  private EntryGroup entryGroup;
+  private JScrollPane jspView;
+  private JComboBox combo;
+  private int nbasesInView;
+ 
+  public JamView(String bam, 
+                 String reference,
+                 int nbasesInView)
+  {
+    super();
+    setBackground(Color.white);
+    this.bam = bam;
+    this.nbasesInView = nbasesInView;
+    
+    if(reference != null)
+    {
+      entryGroup = new SimpleEntryGroup();
+      try
+      {
+        getEntry(reference,entryGroup);
+      }
+      catch (NoSequenceException e)
+      {
+        e.printStackTrace();
+      }
+    }
+    readHeader();
+  }
+  
+  private void readHeader()
+  {
+	String cmd[] = { "/Users/tjc/BAM/samtools-0.1.5c/samtools",  
+			     "view", "-H", bam };
+	
+    RunSamTools samtools = new RunSamTools(cmd, null, null);
+	
+    if(samtools.getProcessStderr() != null)
+      System.out.println(samtools.getProcessStderr());
+ 
+    String header = samtools.getProcessStdout();
+    
+    StringReader samReader = new StringReader(header);
+	BufferedReader buff = new BufferedReader(samReader);
+    
+	String line;
+	try 
+	{
+	  while((line = buff.readLine()) != null)
+	  {
+	    if(line.indexOf("LN:") > -1)
+	    {
+	      String parts[] = line.split("\t");
+	      String name = "";
+	      int seqLength = 0;
+	      for(int i=0; i<parts.length; i++)
+	      {
+	        if(parts[i].startsWith("LN:"))
+	          seqLength = Integer.parseInt( parts[i].substring(3) );
+	        else if(parts[i].startsWith("SN:"))
+	          name = parts[i].substring(3);
+	      }
+	      seqLengths.put(name, seqLength);
+	      seqNames.add(name);
+	    }
+	  }
+	} 
+	catch (IOException e) 
+	{
+	  e.printStackTrace();
+	}
+  }
+  
+  private void readFromBam(int start, int end)
+  {
+    String refName = (String) combo.getSelectedItem();
+	String cmd[] = { "/Users/tjc/BAM/samtools-0.1.5c/samtools",  
+				     "view", bam, refName+":"+start+"-"+end };
+		
+	for(int i=0; i<cmd.length;i++)
+	  System.out.print(cmd[i]+" ");
+	System.out.println();
+	RunSamTools samtools = new RunSamTools(cmd, null, null);
+		
+	if(samtools.getProcessStderr() != null)
+      System.out.println(samtools.getProcessStderr());
+	    
+	sam = samtools.getProcessStdout();
+
+	StringReader samReader = new StringReader(sam);
+	BufferedReader buff = new BufferedReader(samReader);
+	String line;
+	try 
+	{
+	  if(readsInView == null)
+	    readsInView = new Vector<Read>();
+	  else
+		readsInView.clear();
+	  while((line = buff.readLine()) != null)
+	  {
+		String fields[] = line.split("\t");
+
+		Read pread = new Read();
+		pread.qname = fields[0];
+		pread.flag  = Integer.parseInt(fields[1]);
+		pread.rname = fields[2];
+		pread.pos   = Integer.parseInt(fields[3]);
+		pread.mapq  = Short.parseShort(fields[4]);
+		pread.mpos  = Integer.parseInt(fields[7]);
+		pread.isize = Integer.parseInt(fields[8]);
+		pread.seq   = fields[9];
+		readsInView.add(pread);
+	  }
+	   	  
+	  Collections.sort(readsInView, new ReadComparator());
+	} 
+	catch (IOException e) 
+	{
+	  e.printStackTrace();
+	}
+  }
+  
+  /**
+   * Override
+   */
+  public void paintComponent(Graphics g)
+  {
+	super.paintComponent(g);
+	Graphics2D g2 = (Graphics2D)g;
+
+	String refName = (String) combo.getSelectedItem();
+    int seqLength = seqLengths.get(refName);
+
+	float pixPerBase = ((float)getWidth())/(float)(seqLength);
+	double x = jspView.getViewport().getViewRect().getX();
+	
+	int start = (int) (seqLength * ( x / getWidth()));
+	int end   = (int) (start + (jspView.getViewport().getWidth() / pixPerBase));
+	
+	int scaleHeight = 15;
+	readFromBam(start, end);
+	
+	drawScale(g2, start, end, pixPerBase);
+	
+	Stroke originalStroke = g2.getStroke();
+	Stroke stroke =
+		    new BasicStroke (2.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
+
+	for(int i=0; i<readsInView.size(); i++)
+	{
+      Read thisRead = readsInView.get(i);
+      Read nextRead = null;
+      
+      if( (thisRead.flag & 0x0001) != 0x0001 ||
+    	  (thisRead.flag & 0x0008) == 0x0008 )  // not single read
+    	continue;
+      
+      if(i < readsInView.size()-1)
+      {
+    	nextRead = readsInView.get(i+1);
+    	i++;
+      }
+      
+      if(nextRead != null &&
+         (nextRead.flag & 0x0001) == 0x0001 &&
+         (nextRead.flag & 0x0008) != 0x0008 )
+      {         
+    	if(thisRead.qname.equals(nextRead.qname))
+    	{
+          if( (thisRead.flag & 0x0010) == 0x0010 &&
+              (nextRead.flag & 0x0010) == 0x0010 )
+            g2.setColor(Color.red);
+          else
+            g2.setColor(Color.blue);
+          
+          int ypos = (getHeight() - scaleHeight) - ( Math.abs(thisRead.isize) );
+          int thisStart = drawRead(g2, thisRead, pixPerBase, stroke, ypos);
+    	  int nextStart = drawRead(g2, nextRead, pixPerBase, stroke, ypos);
+    	  g2.setStroke(originalStroke);
+    	  g2.setColor(Color.LIGHT_GRAY);
+    	  g2.drawLine(thisStart, ypos, nextStart, ypos);
+    	}
+    	else
+    	  i--;
+      }
+	}
+  }
+  
+  private int getBaseAtStartOfView()
+  {
+    String refName = (String) combo.getSelectedItem();
+    int seqLength = seqLengths.get(refName);
+    double x = jspView.getViewport().getViewRect().getX();
+    return (int) (seqLength * ( x / getWidth()));
+  }
+  
+  private void drawScale(Graphics2D g2, int start, int end, float pixPerBase)
+  {
+    g2.setColor(Color.black);
+    g2.drawLine( (int)(start*pixPerBase), getHeight()-14, (int)(end*pixPerBase), getHeight()-14);
+    int interval = end-start;
+    
+    if(interval > 256000)
+      drawTicks(g2, start, end, pixPerBase, 512000);
+    else if(interval > 64000)
+      drawTicks(g2, start, end, pixPerBase, 12800);
+    else if(interval > 16000)
+      drawTicks(g2, start, end, pixPerBase, 3200);
+    else if(interval > 4000)
+      drawTicks(g2, start, end, pixPerBase, 800);
+    else if(interval > 1000)
+      drawTicks(g2, start, end, pixPerBase, 200);
+  }
+  
+  private void drawTicks(Graphics2D g2, int start, int end, float pixPerBase, int division)
+  {
+    int markStart = (Math.round(start/division)*division)+division;
+    int sm = markStart-(division/2);
+    
+    if(sm > start)
+      g2.drawLine((int)(sm*pixPerBase), getHeight()-14,(int)(sm*pixPerBase), getHeight()-12);
+    
+    for(int m=markStart; m<end; m+=division)
+    {
+      g2.drawString(Integer.toString(m), m*pixPerBase, getHeight()-1);
+      g2.drawLine((int)(m*pixPerBase), getHeight()-14,(int)(m*pixPerBase), getHeight()-11);
+      
+      sm = m+(division/2);
+      
+      if(sm < end)
+        g2.drawLine((int)(sm*pixPerBase), getHeight()-14,(int)(sm*pixPerBase), getHeight()-12);
+    }
+  }
+  
+  private int drawRead(Graphics2D g2, Read read,
+		                float pixPerBase, Stroke stroke, int ypos)
+  {
+    int thisStart = (int) (read.pos*pixPerBase);
+    int thisEnd   = (int) (thisStart + (read.seq.length()*pixPerBase));
+    g2.setStroke(stroke);
+    g2.drawLine(thisStart, ypos, thisEnd, ypos);
+    return thisStart;
+  }
+
+
+
+  
+  public void showJFrame()
+  {
+    this.nbasesInView = nbasesInView;
+    JFrame frame = new JFrame("JamTool");
+
+    combo = new JComboBox(seqNames);
+    combo.setEditable(false);
+    combo.addItemListener(new ItemListener()
+    {
+      public void itemStateChanged(ItemEvent e)
+      {
+        setZoomLevel(JamView.this.nbasesInView);
+      }
+    });
+    
+    frame.setPreferredSize(new Dimension(1000,800));
+    setLength(nbasesInView);
+    
+    jspView = new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_NEVER,
+                                    JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+        
+    JPanel panel = (JPanel) frame.getContentPane();
+
+    panel.setLayout(new BorderLayout());
+    panel.add(combo, BorderLayout.NORTH);
+    panel.add(jspView, BorderLayout.CENTER);
+    frame.pack();
+    
+    setFocusable(true);
+    requestFocusInWindow();
+    
+    addKeyListener(new KeyAdapter()
+    {
+      public void keyPressed(final KeyEvent event)
+      {
+        switch(event.getKeyCode())
+        {
+          case KeyEvent.VK_UP:
+            setZoomLevel( (int) (JamView.this.nbasesInView*1.1) );
+            repaint();
+            break;
+          case KeyEvent.VK_DOWN:
+            setZoomLevel( (int) (JamView.this.nbasesInView*.9) );
+            repaint();
+            break;
+          default:
+            break;
+        }
+      }
+    });
+    
+    addFocusListener(new FocusListener() 
+    {
+      public void focusGained(FocusEvent fe) {}
+      public void focusLost(FocusEvent fe) {}
+    });
+
+    frame.setVisible(true);
+  }
+  
+  private void setZoomLevel(final int nbasesInView)
+  {
+    int startBase = getBaseAtStartOfView();
+   
+    this.nbasesInView = nbasesInView;
+    setLength(this.nbasesInView);
+    revalidate();
+    repaint();
+    System.out.println(startBase+"  "+getBaseAtStartOfView());
+    
+    String refName = (String) combo.getSelectedItem();
+    int seqLength = seqLengths.get(refName);
+
+
+    float pixPerBase = ((float)getWidth())/(float)(seqLength);
+    Point p = jspView.getViewport().getViewPosition();
+    p.x = (int)(startBase*pixPerBase);
+    jspView.getViewport().setViewPosition(p);
+    revalidate();
+    repaint();
+    
+    System.out.println(startBase+"  "+getBaseAtStartOfView());
+  }
+
+  private void setLength(int basesToShow)
+  {
+    String refName = (String) combo.getSelectedItem();
+    int seqLength = seqLengths.get(refName);
+    float pixPerBase = 1000.f/(float)(basesToShow);
+    setPreferredSize( new Dimension((int) (seqLength*pixPerBase), 
+                                            getSize().height) );
+  }
+  
+  /**
+   * Return an Artemis entry from a file 
+   * @param entryFileName
+   * @param entryGroup
+   * @return
+   * @throws NoSequenceException
+   */
+  private Entry getEntry(final String entryFileName, final EntryGroup entryGroup) 
+                   throws NoSequenceException
+  {
+    final Document entry_document = DocumentFactory.makeDocument(entryFileName);
+    final EntryInformation artemis_entry_information =
+      Options.getArtemisEntryInformation();
+    
+    System.out.println(entryFileName);
+    final uk.ac.sanger.artemis.io.Entry new_embl_entry =
+      EntryFileDialog.getEntryFromFile(null, entry_document,
+                                       artemis_entry_information,
+                                       false);
+
+    if(new_embl_entry == null)  // the read failed
+      return null;
+
+    Entry entry = null;
+    try
+    {
+      Bases bases = null;
+      if(entryGroup.getSequenceEntry() != null)
+        bases = entryGroup.getSequenceEntry().getBases();
+      if(bases == null)
+        entry = new Entry(new_embl_entry);
+      else
+        entry = new Entry(bases,new_embl_entry);
+      
+      entryGroup.add(entry);
+    } 
+    catch(OutOfRangeException e) 
+    {
+      new MessageDialog(null, "read failed: one of the features in " +
+          entryFileName + " has an out of range " +
+                        "location: " + e.getMessage());
+    }
+    return entry;
+  }
+  
+  class Read
+  {
+    String qname; // query name
+    int flag;     // bitwise flag
+    String rname; // reference name
+    int pos;      // leftmost coordinate
+    short mapq;   // MAPping Quality
+    String cigar; // extended CIGAR string
+    String mrnm;  // Mate Reference sequence NaMe; Ò=Ó if the same as rname
+    int mpos;     // leftmost Mate POSition
+    int isize;    // inferred Insert SIZE
+    String seq;   // query SEQuence; Ò=Ó for a match to the reference; n/N/. for ambiguity
+  }
+  
+  class ReadComparator implements Comparator
+  {
+    public int compare(Object o1, Object o2) 
+    {
+      Read pr1 = (Read) o1;
+      Read pr2 = (Read) o2;
+      
+      return pr1.qname.compareTo(pr2.qname);
+    }
+  }
+  
+  public static void main(String[] args)
+  {
+    String bam = args[0];
+    int nbasesInView = Integer.parseInt(args[1]);
+    String reference = null;
+    if(args.length > 2)
+      reference = args[2];
+    
+	JamView view = new JamView(bam, reference, nbasesInView);
+	view.showJFrame();
+  }
+}
diff --git a/uk/ac/sanger/artemis/components/alignment/RunSamTools.java b/uk/ac/sanger/artemis/components/alignment/RunSamTools.java
new file mode 100644
index 0000000000000000000000000000000000000000..2ac1284d4116bb25aee4456eaa61a74cd737fdd3
--- /dev/null
+++ b/uk/ac/sanger/artemis/components/alignment/RunSamTools.java
@@ -0,0 +1,317 @@
+
+package uk.ac.sanger.artemis.components.alignment;
+
+import java.io.*;
+import javax.swing.JTextArea;
+
+/**
+* Used to run an samtools process this reads stdout and 
+* stderr in separate threads.
+*/
+public class RunSamTools
+{
+
+  /** running process */
+  private Process p;
+  /** standard out */
+  private StringBuffer stdout = new StringBuffer();
+  /** standard error */
+  private StringBuffer stderr = new StringBuffer();
+  
+  private String initialIOError = null;
+  
+  /** running directory */
+  //private File project;
+  /** process status */
+  private String status;
+  private StdoutHandler stdouth;
+  private StderrHandler stderrh;
+  private JTextArea textArea;
+
+
+  /**
+  * @param cmd              command to run
+  * @param env              environment
+  * @param project          running directory
+  */
+  public RunSamTools(String cmd[], 
+                     String[] envp, File project)
+  {
+    //this.project = project;
+    status = "0";
+
+    Runtime run = Runtime.getRuntime();
+    try
+    {
+      p = run.exec(cmd,envp,project);
+
+      // 2 threads to read in stdout & stderr buffers 
+      // to prevent blocking
+      stdouth = new StdoutHandler(this);
+      stderrh = new StderrHandler(this);
+      stdouth.start();
+      stderrh.start();
+    }
+    catch(IOException ioe)
+    {
+      System.err.println("Error executing: "+
+                          cmd[0]);
+      initialIOError = ioe.getMessage();
+      status = "1";
+    }
+  }
+
+  /**
+  * Read in the process stderr.
+  */
+  private void readProcessStderr()
+  {
+    BufferedInputStream stderrStream = null;
+    BufferedReader stderrRead = null;
+    try
+    {
+      //String line;
+      stderrStream =
+         new BufferedInputStream(p.getErrorStream());
+      stderrRead =
+         new BufferedReader(new InputStreamReader(stderrStream));
+      char c[] = new char[100];
+      int nc = 0;
+
+      while((nc = stderrRead.read(c,0,100)) != -1)
+        stderr = stderr.append(new String(c,0,nc));
+    }
+    catch (IOException io)
+    {
+      System.err.println("RunEmbossApplication2: Error in "+
+                                "collecting standard out");
+    }
+    finally
+    {
+      try
+      {
+        if(stderrStream!=null)
+          stderrStream.close();
+      }
+      catch(IOException ioe)
+      {
+        System.err.println("RunEmbossApplication2: Error closing stream");
+      }
+      try
+      {
+        if(stderrRead!=null)
+          stderrRead.close();
+      }
+      catch(IOException ioe)
+      {
+        System.err.println("RunEmbossApplication2: Error closing reader");
+      }
+    }
+
+    return;
+  }
+
+  /**
+  * Read in the process stdout.
+  */
+  private void readProcessStdout()
+  {
+    BufferedInputStream stdoutStream = null;
+    BufferedReader stdoutRead = null;
+    try
+    {
+      //String line;
+      stdoutStream =
+         new BufferedInputStream(p.getInputStream());
+      stdoutRead =
+         new BufferedReader(new InputStreamReader(stdoutStream));
+    
+      char c[] = new char[200];
+      int nc = 0;
+      String chunk;
+
+      while((nc = stdoutRead.read(c,0,200)) != -1)
+      {
+        chunk  = new String(c,0,nc);
+        stdout = stdout.append(chunk);
+        if(textArea != null)
+        {
+          textArea.append(chunk);
+          textArea.setCaretPosition(textArea.getDocument().getLength());
+        }
+      }
+    }
+    catch (IOException io)
+    {
+      System.err.println("RunEmbossApplication2: Error in "+ 
+                                "collecting standard out");
+    }
+    finally
+    {
+      try
+      {
+        if(stdoutStream!=null)
+          stdoutStream.close();
+      }
+      catch(IOException ioe)
+      {
+        System.err.println("RunEmbossApplication2: Error closing stream");
+      } 
+      try
+      {
+        if(stdoutRead!=null)
+          stdoutRead.close();
+      }
+      catch(IOException ioe)
+      {
+        System.err.println("RunEmbossApplication2: Error closing reader");
+      }
+    }
+ 
+    return;
+  }
+
+  /**
+  * This method can be called after the process has completed
+  * to write the stdout to the project directory.
+  */
+//  private void writeStdout()
+//  {
+//    if(project != null)
+//    {
+//      PrintWriter out = null;
+//      String fname = "";
+//      try
+//      {
+//        fname = project.getCanonicalPath() + 
+//                             "/application_stdout";
+//        File so = new File(fname);
+//
+//        if(!so.exists())
+//          so.createNewFile();
+//
+//        out = new PrintWriter(new FileWriter(fname,true));
+//        out.println(stdout);
+//      }
+//      catch(IOException ioe)
+//      {
+//        System.err.println("RunEmbossApplication2: Error writing" +
+//                            fname);
+//      }
+//      finally
+//      {
+//        if(out!=null)
+//          out.close();
+//      }
+//    }
+//  }
+
+  /**
+  * @return standard out
+  */
+  public String getProcessStdout()
+  {
+    try
+    {
+      // make sure we hang around for stdout
+      while(stdouth.isAlive())
+        Thread.sleep(10);
+    }
+    catch(InterruptedException ie)
+    {
+      ie.printStackTrace();
+    }
+                                                                                
+    return stdout.toString();
+  }
+
+  /**
+  * @return stderr
+  */
+  public String getProcessStderr()
+  {
+    try
+    {
+      // make sure we hang around for stdout
+      while(stderrh.isAlive())
+        Thread.sleep(10);
+    }
+    catch(InterruptedException ie)
+    {
+      ie.printStackTrace();
+    }
+                                                                                
+    return new String(stderr.toString().trim());
+  }
+
+  /**
+  * Wait for the process to end
+  */
+  public void waitFor()
+  {
+	try
+	{
+	  int exitVal = p.waitFor();
+	  
+	  if(exitVal != 0)
+	    System.out.println("Exit value:: "+exitVal);
+	}
+	catch(InterruptedException ie)
+	{
+	  ie.printStackTrace();
+	}
+  }
+  
+  /**
+  * @return process
+  */
+  public Process getProcess()
+  {
+    return p;
+  }
+
+  /**
+  * @return status
+  */
+  public String getStatus()
+  {
+    return status;
+  }
+
+  class StdoutHandler extends Thread
+  {
+    RunSamTools rea;
+
+    protected StdoutHandler(RunSamTools rea)
+    {
+      this.rea = rea;
+    }
+
+    public void run()
+    {
+      rea.readProcessStdout();
+    }
+  }
+
+  class StderrHandler extends Thread
+  {
+    RunSamTools rea;
+
+    protected StderrHandler(RunSamTools rea)
+    {
+      this.rea = rea;
+    }
+
+    public void run()
+    {
+      rea.readProcessStderr();
+    }
+  }
+
+  public String getInitialIOError() 
+  {
+    return initialIOError;
+  }
+
+}
+