Skip to content
Snippets Groups Projects
VCFview.java 51.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • tjc's avatar
    tjc committed
    /* VCFview
     *
     * created: July 2010
     *
     * This file is part of Artemis
     *
     * Copyright(C) 2010  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.
     *
     */
    
    package uk.ac.sanger.artemis.components.variant;
    
    tjc's avatar
    tjc committed
    import java.awt.AlphaComposite;
    
    tjc's avatar
    tjc committed
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Component;
    
    tjc's avatar
    tjc committed
    import java.awt.Composite;
    
    import java.awt.Container;
    
    tjc's avatar
    tjc committed
    import java.awt.Cursor;
    
    tjc's avatar
    tjc committed
    import java.awt.Dimension;
    
    import java.awt.FlowLayout;
    
    tjc's avatar
    tjc committed
    import java.awt.FontMetrics;
    
    import java.awt.Frame;
    
    tjc's avatar
    tjc committed
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Insets;
    import java.awt.Point;
    
    tjc's avatar
    tjc committed
    import java.awt.Rectangle;
    
    tjc's avatar
    tjc committed
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.AdjustmentEvent;
    import java.awt.event.AdjustmentListener;
    import java.awt.event.ItemEvent;
    import java.awt.event.ItemListener;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseMotionListener;
    import java.io.File;
    import java.io.FileInputStream;
    
    import java.io.FileOutputStream;
    
    tjc's avatar
    tjc committed
    import java.io.IOException;
    
    import java.io.InputStream;
    import java.net.URL;
    
    import java.util.Hashtable;
    
    tjc's avatar
    tjc committed
    import java.util.List;
    import java.util.Vector;
    
    import java.util.regex.Pattern;
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
    import javax.swing.BorderFactory;
    
    tjc's avatar
    tjc committed
    import javax.swing.ButtonGroup;
    
    tjc's avatar
    tjc committed
    import javax.swing.JButton;
    
    import javax.swing.JCheckBox;
    
    import javax.swing.JCheckBoxMenuItem;
    
    tjc's avatar
    tjc committed
    import javax.swing.JComboBox;
    
    import javax.swing.JComponent;
    
    tjc's avatar
    tjc committed
    import javax.swing.JFrame;
    import javax.swing.JMenu;
    import javax.swing.JMenuBar;
    import javax.swing.JMenuItem;
    
    tjc's avatar
    tjc committed
    import javax.swing.JOptionPane;
    
    tjc's avatar
    tjc committed
    import javax.swing.JPanel;
    import javax.swing.JPopupMenu;
    
    tjc's avatar
    tjc committed
    import javax.swing.JRadioButtonMenuItem;
    
    tjc's avatar
    tjc committed
    import javax.swing.JScrollBar;
    import javax.swing.JScrollPane;
    
    tjc's avatar
    tjc committed
    import javax.swing.JSeparator;
    
    import javax.swing.border.Border;
    import javax.swing.border.EmptyBorder;
    
    import javax.swing.text.JTextComponent;
    
    tjc's avatar
    tjc committed
    
    import net.sf.samtools.util.BlockCompressedInputStream;
    
    import org.apache.log4j.Level;
    
    import uk.ac.sanger.artemis.Entry;
    import uk.ac.sanger.artemis.EntryGroup;
    
    import uk.ac.sanger.artemis.Feature;
    import uk.ac.sanger.artemis.FeatureKeyPredicate;
    import uk.ac.sanger.artemis.FeatureVector;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.Options;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.Selection;
    
    import uk.ac.sanger.artemis.SelectionChangeEvent;
    import uk.ac.sanger.artemis.SelectionChangeListener;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.SimpleEntryGroup;
    
    import uk.ac.sanger.artemis.components.DisplayAdjustmentEvent;
    import uk.ac.sanger.artemis.components.DisplayAdjustmentListener;
    
    import uk.ac.sanger.artemis.components.EntryEdit;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.components.EntryFileDialog;
    
    import uk.ac.sanger.artemis.components.FeatureDisplay;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.components.FileViewer;
    import uk.ac.sanger.artemis.components.MessageDialog;
    
    import uk.ac.sanger.artemis.components.MultiComparator;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.components.alignment.FileSelectionDialog;
    
    import uk.ac.sanger.artemis.components.genebuilder.AutoCompleteComboDocument;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.editor.MultiLineToolTipUI;
    import uk.ac.sanger.artemis.io.EntryInformation;
    import uk.ac.sanger.artemis.io.Key;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.io.Range;
    
    import uk.ac.sanger.artemis.io.RangeVector;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.sequence.Bases;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.sequence.MarkerRange;
    
    tjc's avatar
    tjc committed
    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.FTPSeekableStream;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.util.OutOfRangeException;
    
    
    public class VCFview extends JPanel
    
                 implements DisplayAdjustmentListener, SelectionChangeListener
    
    tjc's avatar
    tjc committed
    {
      private static final long serialVersionUID = 1L;
      private JScrollBar scrollBar;
    
      private JPanel vcfPanel;
    
    tjc's avatar
    tjc committed
      private AbstractVCFReader vcfReaders[];
    
    tjc's avatar
    tjc committed
      private List<String> vcfFiles;
    
      private List<Integer> hideVcfList = new Vector<Integer>();
      
    
    tjc's avatar
    tjc committed
      private String header[];
    
      private FeatureDisplay feature_display;
    
    tjc's avatar
    tjc committed
      private Selection selection;
    
    tjc's avatar
    tjc committed
      private int nbasesInView;
    
      protected int seqLength;
    
      private EntryGroup entryGroup;
    
    tjc's avatar
    tjc committed
      private String chr;
    
      private VCFRecord mouseVCF;
    
    tjc's avatar
    tjc committed
      private int mouseOverIndex = -1;
    
    tjc's avatar
    tjc committed
      
      private GraphPanel graphPanel;
    
    tjc's avatar
    tjc committed
    //record of where a mouse drag starts
      private int dragStart = -1;
    
    tjc's avatar
    tjc committed
      private JPopupMenu popup;
    
      private JMenu vcfFilesMenu = new JMenu("VCF files");
    
    tjc's avatar
    tjc committed
      private int LINE_HEIGHT = 15;
    
    tjc's avatar
    tjc committed
      protected boolean showSynonymous = true;
      protected boolean showNonSynonymous = true;
      protected boolean showDeletions = true;
      protected boolean showInsertions = true;
      protected boolean showMultiAlleles = true;
      // show variants that do not overlap CDS
      protected boolean showNonOverlappings = true;
    
      protected boolean showNonVariants = false;
    
    tjc's avatar
    tjc committed
      private boolean markAsNewStop = false;
    
    tjc's avatar
    tjc committed
      
      private boolean showLabels = false;
      
    
    tjc's avatar
    tjc committed
      private JCheckBoxMenuItem markNewStops =
    
    tjc's avatar
    tjc committed
        new JCheckBoxMenuItem("Mark new stops within CDS features", true);
    
    tjc's avatar
    tjc committed
      private static int VARIANT_COLOUR_SCHEME = 0;
      private static int SYN_COLOUR_SCHEME     = 1;
      private static int QUAL_COLOUR_SCHEME    = 2;
      
      private int colourScheme = 0;
      private Color colMap[] = makeColours(Color.RED, 255);
    
    tjc's avatar
    tjc committed
      private Color lighterGrey = new Color(220,220,220);
    
      Hashtable<String, Integer> offsetLengths = null;
      private boolean concatSequences = false;
    
    tjc's avatar
    tjc committed
      protected static Pattern tabPattern = Pattern.compile("\t");
    
    tjc's avatar
    tjc committed
      
      public static String VCFFILE_SUFFIX = ".*\\.[bv]{1}cf(\\.gz)*$";
    
    tjc's avatar
    tjc committed
      private static String FILE_SUFFIX = "\\.[bv]{1}cf(\\.gz)*$";
    
    
      private List<Integer> cacheVariantLines;
    
    
    tjc's avatar
    tjc committed
      public static org.apache.log4j.Logger logger4j = 
        org.apache.log4j.Logger.getLogger(VCFview.class);
    
    tjc's avatar
    tjc committed
    
    
      public VCFview(final JFrame frame,
                     final JPanel vcfPanel,
    
    tjc's avatar
    tjc committed
                     final List<String> vcfFiles, 
                     final int nbasesInView,
                     final int seqLength,
                     final String chr,
    
                     final String reference,
                     final FeatureDisplay feature_display)
    
    tjc's avatar
    tjc committed
      {
        super();
        
        this.nbasesInView = nbasesInView;
        this.seqLength = seqLength;
        this.chr = chr;
    
        this.feature_display = feature_display;
        this.vcfPanel = vcfPanel;
    
    tjc's avatar
    tjc committed
        this.vcfFiles = vcfFiles;
    
    tjc's avatar
    tjc committed
        setBackground(Color.white);
        MultiLineToolTipUI.initialize();
        setToolTipText("");
    
    tjc's avatar
    tjc committed
        vcfPanel.setPreferredSize(new Dimension(900, 
            (vcfFiles.size()+1)*(LINE_HEIGHT+5)));
    
    tjc's avatar
    tjc committed
        
    
        if(feature_display != null)
    
          this.entryGroup = feature_display.getEntryGroup();
    
        else if(reference != null)
    
          this.entryGroup = getReference(reference);
        if(entryGroup != null)
          this.seqLength = entryGroup.getSequenceEntry().getBases().getLength();
    
    tjc's avatar
    tjc committed
        try
        {
    
    tjc's avatar
    tjc committed
          vcfReaders = new AbstractVCFReader[vcfFiles.size()];
    
    tjc's avatar
    tjc committed
          header = new String[vcfFiles.size()];
          
          for(int i=0; i<vcfFiles.size(); i++)
          {
    
    tjc's avatar
    tjc committed
            header[i] = readHeader(vcfFiles.get(i), i);
    
    tjc's avatar
    tjc committed
          }
        }
    
    tjc's avatar
    tjc committed
        catch(java.lang.UnsupportedClassVersionError err)
        {
          JOptionPane.showMessageDialog(null, 
              "This requires Java 1.6 or higher.", 
              "Check Java Version", JOptionPane.WARNING_MESSAGE);
        }
    
    tjc's avatar
    tjc committed
        final JScrollPane jspView = new JScrollPane(this, 
            JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
            JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        
    
        vcfPanel.setLayout(new BorderLayout());
        vcfPanel.add(jspView, BorderLayout.CENTER);
    
    tjc's avatar
    tjc committed
        
    
        JPanel bottomPanel = new JPanel(new BorderLayout());
    
    tjc's avatar
    tjc committed
        graphPanel = new GraphPanel(this);
        graphPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 1, 0, Color.gray));
    
        graphPanel.setPreferredSize(new Dimension(900, 100));
        graphPanel.setVisible(false);
        
        bottomPanel.add(graphPanel, BorderLayout.CENTER);
        vcfPanel.add(bottomPanel, BorderLayout.SOUTH);
        
    
    tjc's avatar
    tjc committed
        if(this.nbasesInView > this.seqLength)
          this.nbasesInView = this.seqLength/2;
    
        scrollBar = new JScrollBar(JScrollBar.HORIZONTAL, 1, this.nbasesInView, 1, this.seqLength);
        scrollBar.setUnitIncrement(nbasesInView/20);
        scrollBar.addAdjustmentListener(new AdjustmentListener()
        {
          public void adjustmentValueChanged(AdjustmentEvent e)
          {
            repaint();
          }
        });
        
        //
        //
        addMouseListener(new PopupListener());
        
        //
    
        createMenus(frame, jspView);
    
        setDisplay();
        
        if(feature_display == null)
        {
    
          bottomPanel.add(scrollBar, BorderLayout.SOUTH);
    
    tjc's avatar
    tjc committed
          selection = new Selection(null);
    
        }
        else
        {
          Border empty = new EmptyBorder(0,0,0,0);
          jspView.setBorder(empty);
    
    tjc's avatar
    tjc committed
          selection = feature_display.getSelection();
    
      private void createMenus(final JFrame frame, final JScrollPane jspView)
    
        final JComponent topPanel;
    
        if(feature_display != null)
          topPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
        else
    
    tjc's avatar
    tjc committed
        {
          markNewStops.setSelected(false);
          markNewStops.setEnabled(false);
    
          topPanel = new JMenuBar();
    
    tjc's avatar
    tjc committed
          
          if(frame != null)
            frame.setJMenuBar((JMenuBar)topPanel);
    
          
          JMenu fileMenu = new JMenu("File");
          topPanel.add(fileMenu);
    
    tjc's avatar
    tjc committed
        
    
          JMenuItem printImage = new JMenuItem("Save As Image Files (png/jpeg)...");
          printImage.addActionListener(new ActionListener()
    
    tjc's avatar
    tjc committed
          {
    
            public void actionPerformed(ActionEvent e)
            {
              PrintVCFview part = new PrintVCFview(VCFview.this);
              part.print();
            }
          });
          fileMenu.add(printImage);
          
          JMenuItem printPS = new JMenuItem("Print...");
          printPS.addActionListener(new ActionListener()
    
    tjc's avatar
    tjc committed
          {
    
            public void actionPerformed(ActionEvent e)
            {
              PrintVCFview part = new PrintVCFview(VCFview.this);
              part.validate();
              part.doPrintActions();
            }
          });
          fileMenu.add(printPS);
          
    
    tjc's avatar
    tjc committed
    
    
          JMenuItem close = new JMenuItem("Close");
          fileMenu.add(close);
          close.addActionListener(new ActionListener()
    
    tjc's avatar
    tjc committed
          {
    
            public void actionPerformed(ActionEvent e)
            {
              VCFview.this.setVisible(false);
              Component comp = VCFview.this;
              
              while( !(comp instanceof JFrame) )
                comp = comp.getParent();
              ((JFrame)comp).dispose();
            } 
          });
          
          JButton zoomIn = new JButton("-");
          Insets ins = new Insets(1,1,1,1);
          zoomIn.setMargin(ins);
          zoomIn.addActionListener(new ActionListener()
    
    tjc's avatar
    tjc committed
          {
    
            public void actionPerformed(ActionEvent e)
            {
              setZoomLevel((int) (VCFview.this.nbasesInView * 1.1));
            }
          });
          topPanel.add(zoomIn);
    
    tjc's avatar
    tjc committed
    
    
          JButton zoomOut = new JButton("+");
          zoomOut.setMargin(ins);
          zoomOut.addActionListener(new ActionListener()
    
    tjc's avatar
    tjc committed
          {
    
            public void actionPerformed(ActionEvent e)
            {
              setZoomLevel((int) (VCFview.this.nbasesInView * .9));
            }
          });
          topPanel.add(zoomOut);
        }
    
    tjc's avatar
    tjc committed
        
    
    tjc's avatar
    tjc committed
        final JComboBox combo = new JComboBox(vcfReaders[0].getSeqNames());
    
        combo.setEditable(true);
        JTextComponent editor = (JTextComponent) combo.getEditor().getEditorComponent();
        editor.setDocument(new AutoCompleteComboDocument(combo));
    
    tjc's avatar
    tjc committed
        if(vcfReaders[0].getSeqNames().length > 1)
    
    tjc's avatar
    tjc committed
          combo.addItem("Combine References");
    
    tjc's avatar
    tjc committed
        if(chr == null)
    
    tjc's avatar
    tjc committed
          this.chr = vcfReaders[0].getSeqNames()[0];
    
    tjc's avatar
    tjc committed
        combo.setSelectedItem(this.chr);
        combo.setMaximumRowCount(20);
        
        combo.addItemListener(new ItemListener()
        {
          public void itemStateChanged(ItemEvent e)
          {
    
    tjc's avatar
    tjc committed
            if(combo.getSelectedItem().equals("Combine References"))
    
              concatSequences = true;
            else 
            {
              VCFview.this.chr = (String) combo.getSelectedItem();
              concatSequences = false;
            }
    
    tjc's avatar
    tjc committed
            repaint();
          }
        });
        topPanel.add(combo);
    
        if(topPanel instanceof JPanel)
          vcfPanel.add(topPanel, BorderLayout.NORTH);
        
        // auto hide top panel
        final JCheckBox buttonAutoHide = new JCheckBox("Hide", true);
        buttonAutoHide.setToolTipText("Auto-Hide");
        topPanel.add(buttonAutoHide);
        final MouseMotionListener mouseMotionListener = new MouseMotionListener()
        {
          public void mouseDragged(MouseEvent event)
          {
            handleCanvasMouseDrag(event);
          }
          
          public void mouseMoved(MouseEvent e)
          {
            findVariantAtPoint(e.getPoint());
    
            int thisHgt = HEIGHT;
            if (thisHgt < 5)
              thisHgt = 15;
    
            int y = (int) (e.getY() - jspView.getViewport().getViewRect().getY());
            if (y < thisHgt)
              topPanel.setVisible(true);
            else
            {
              if (buttonAutoHide.isSelected())
                topPanel.setVisible(false);
            }
    
          }
        };
        addMouseMotionListener(mouseMotionListener);
    
    
        
        // popup menu
        popup = new JPopupMenu();
    
    tjc's avatar
    tjc committed
        JMenuItem addVCFMenu = new JMenuItem("Add VCF ...");
        addVCFMenu.addActionListener(new ActionListener() 
        {
          public void actionPerformed(ActionEvent e)
          {
            FileSelectionDialog fileSelection = new FileSelectionDialog(
                null, true, "VCFview", "VCF");
    
    tjc's avatar
    tjc committed
            List<String> vcfFileList = fileSelection.getFiles(VCFFILE_SUFFIX);
    
    tjc's avatar
    tjc committed
            vcfFiles.addAll(vcfFileList);
    
    tjc's avatar
    tjc committed
    
            int count = vcfFileList.size();
    
    tjc's avatar
    tjc committed
            int oldSize = vcfReaders.length;
    
    tjc's avatar
    tjc committed
            AbstractVCFReader[] trTmp = new AbstractVCFReader[count + vcfReaders.length];
            System.arraycopy(vcfReaders, 0, trTmp, 0, vcfReaders.length);
            vcfReaders = trTmp;
    
    tjc's avatar
    tjc committed
            String[] hdTmp = new String[count + vcfReaders.length];
    
    tjc's avatar
    tjc committed
            System.arraycopy(header, 0, hdTmp, 0, header.length);
            header = hdTmp;
            
    
    tjc's avatar
    tjc committed
            for (int i = 0; i < vcfFileList.size(); i++)
              header[i+oldSize] = readHeader(vcfFileList.get(i), i+oldSize);
    
            
            for(int i=0; i<vcfFileList.size(); i++)
              addToViewMenu(i+oldSize);
    
    tjc's avatar
    tjc committed
    
            setDisplay();
            repaint();
            jspView.revalidate();
          }
        });
        popup.add(addVCFMenu);
    
        popup.add(vcfFilesMenu);
        
        for(int i=0; i<vcfFiles.size(); i++)
          addToViewMenu(i);
        
    
    tjc's avatar
    tjc committed
        popup.addSeparator();
        
    
    tjc's avatar
    tjc committed
    
    
        markNewStops.addActionListener(new ActionListener(){
          public void actionPerformed(ActionEvent e)
          {
            if(!markNewStops.isSelected())
              markAsNewStop = false;
            repaint();
          }
        });
        popup.add(markNewStops);
        
    
        
        final JMenuItem byQuality = new JMenuItem("Filter ...");
        byQuality.addActionListener(new ActionListener(){
    
    tjc's avatar
    tjc committed
          public void actionPerformed(ActionEvent e)
          {
    
            new VCFFilter(VCFview.this);
    
    tjc's avatar
    tjc committed
          }
        });
    
        popup.add(byQuality);
    
    tjc's avatar
    tjc committed
        
        final JMenu colourBy = new JMenu("Colour By");
        popup.add(colourBy);
        ButtonGroup group = new ButtonGroup();
        final JRadioButtonMenuItem colByAlt   = new JRadioButtonMenuItem("Variant");
        colByAlt.addActionListener(new ActionListener(){
          public void actionPerformed(ActionEvent e)
          {
            if(colByAlt.isSelected())
              colourScheme = 0;
            repaint();
          }
        });
        colourBy.add(colByAlt);
        
        final JRadioButtonMenuItem colBySyn   = new JRadioButtonMenuItem("Synonymous/Non-synonymous");
        colBySyn.addActionListener(new ActionListener(){
          public void actionPerformed(ActionEvent e)
          {
            if(colBySyn.isSelected())
              colourScheme = 1;
            repaint();
          }
        });
        colourBy.add(colBySyn);
        
        final JRadioButtonMenuItem colByScore = new JRadioButtonMenuItem("Score");
        colByScore.addActionListener(new ActionListener(){
          public void actionPerformed(ActionEvent e)
          {
            if(colByScore.isSelected())
              colourScheme = 2;
            repaint();
          }
        });
        colourBy.add(colByScore);
        
        group.add(colByAlt);
        group.add(colBySyn);
        group.add(colByScore);
        colByAlt.setSelected(true);
        
    
        popup.addSeparator();
    
    
        if (feature_display != null)
        {
          final JMenu create = new JMenu("Create");
          final JMenuItem createTab = new JMenuItem(
              "Features from variants");
          popup.add(create);
          create.add(createTab);
          createTab.addActionListener(new ActionListener()
          {
            public void actionPerformed(ActionEvent e)
            {
              Container f = getVcfContainer();
              try
              {
                f.setCursor(new Cursor(Cursor.WAIT_CURSOR));
                IOUtils.createFeatures(VCFview.this, entryGroup);
              }
              finally
              {
                f.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
              }
            }
          });
        }
    
    
        final JMenu export = new JMenu("Write");
    
        final JMenuItem exportVCF = new JMenuItem("Filtered VCF");
    
    tjc's avatar
    tjc committed
        exportVCF.addActionListener(new ActionListener(){
          public void actionPerformed(ActionEvent e)
          {
    
            Container f = getVcfContainer();
            try
            {
              f.setCursor(new Cursor(Cursor.WAIT_CURSOR));
              IOUtils.export(vcfFiles, VCFview.this);
            }
            finally
            {
              f.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            }
    
    tjc's avatar
    tjc committed
          }
        });
    
    tjc's avatar
    tjc committed
        export.add(new JSeparator());
    
        final JMenuItem exportFastaSelected = new JMenuItem("FASTA of selected feature(s) ...");
    
        exportFastaSelected.addActionListener(new ActionListener(){
          public void actionPerformed(ActionEvent e)
          {
    
            Container f = getVcfContainer();
            try
            {
              f.setCursor(new Cursor(Cursor.WAIT_CURSOR));
              IOUtils.exportFasta(VCFview.this, selection.getAllFeatures(), false, null);
            }
            finally
            {
              f.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            }
    
          }
        });
        export.add(exportFastaSelected);
        
    
        final JMenuItem exportFasta = new JMenuItem("FASTA of selected base range ...");
    
        exportFasta.addActionListener(new ActionListener(){
    
    tjc's avatar
    tjc committed
          public void actionPerformed(ActionEvent e)
          {
    
            Container f = getVcfContainer();
            try
            {
              f.setCursor(new Cursor(Cursor.WAIT_CURSOR));
              IOUtils.exportFastaByRange(VCFview.this, selection, false, null);
            }
            finally
            {
              f.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            }
    
    tjc's avatar
    tjc committed
          }
        });
    
        export.add(exportFasta);
    
        final JMenu view = new JMenu("View");
        popup.add(view);
    
        final JMenuItem viewFastaSelected = new JMenuItem("FASTA of selected feature(s) ...");
    
        viewFastaSelected.addActionListener(new ActionListener(){
    
          public void actionPerformed(ActionEvent e)
          {
    
            Container f = getVcfContainer();
            try
            {
              f.setCursor(new Cursor(Cursor.WAIT_CURSOR));
              IOUtils.exportFasta(VCFview.this, selection.getAllFeatures(), true, null);
            }
            finally
            {
              f.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            }
    
        view.add(viewFastaSelected);
    
        final JMenuItem viewFasta = new JMenuItem("FASTA of selected base range ...");
    
        viewFasta.addActionListener(new ActionListener(){
          public void actionPerformed(ActionEvent e)
          {
    
            Container f = getVcfContainer();
            try
            {
              f.setCursor(new Cursor(Cursor.WAIT_CURSOR));
              IOUtils.exportFastaByRange(VCFview.this, selection, true, null);
            }
            finally
            {
              f.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            }
    
          }
        });
        view.add(viewFasta);
    
    tjc's avatar
    tjc committed
        
        JMenu graph = new JMenu("Graph");
        popup.add(graph);
        
        final JCheckBoxMenuItem graphSNP = new JCheckBoxMenuItem("SNP");
        final JCheckBoxMenuItem graphDP = new JCheckBoxMenuItem("Depth (DP)");
        graph.add(graphSNP);
        graphSNP.addActionListener(new ActionListener(){
          public void actionPerformed(ActionEvent e)
          {
            graphPanel.setVisible(graphSNP.isSelected());
            graphDP.setSelected(false);
            graphPanel.setType(0);
            graphPanel.repaint();
          }
        });
        
        graph.add(graphDP);
        graphDP.addActionListener(new ActionListener(){
          public void actionPerformed(ActionEvent e)
          {
            graphPanel.setVisible(graphDP.isSelected());
            graphSNP.setSelected(false);
            graphPanel.setType(1);
            graphPanel.repaint();
          }
        });
    
        final JMenuItem snpOverview = new JMenuItem("Overview for selected features");
    
        popup.add(snpOverview);
        snpOverview.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent e)
          {
    
            Container f = getVcfContainer();
    
              f.setCursor(new Cursor(Cursor.WAIT_CURSOR));
    
              IOUtils.countVariants(VCFview.this, selection.getAllFeatures());
            }
            catch (IOException e1)
            {
    
              JOptionPane.showMessageDialog(null, e1.getMessage());
            }
            finally
            {
              f.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
    
    tjc's avatar
    tjc committed
        final JCheckBoxMenuItem labels = new JCheckBoxMenuItem("Show Labels", showLabels);
        labels.addActionListener(new ActionListener(){
          public void actionPerformed(ActionEvent e)
          {
            showLabels = labels.isSelected();
            repaint();
          }
        });
        popup.add(new JSeparator());
        popup.add(labels);
    
    tjc's avatar
    tjc committed
      }
    
      
      
      private void addToViewMenu(final int thisBamIndex)
      {
        final JCheckBoxMenuItem cbBam = new JCheckBoxMenuItem(
            getLabel(thisBamIndex), true);
        vcfFilesMenu.add(cbBam);
        cbBam.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent e)
          {
            if(cbBam.isSelected())
              hideVcfList.remove(new Integer(thisBamIndex));
            else
              hideVcfList.add(new Integer(thisBamIndex));
            repaint();
          } 
        });
      }
    
    tjc's avatar
    tjc committed
      private static EntryGroup getReference(String reference)
      {
        EntryGroup entryGroup = new SimpleEntryGroup();
        final Document entry_document = DocumentFactory.makeDocument(reference);
        final EntryInformation artemis_entry_information =
          Options.getArtemisEntryInformation();
    
        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
        {
          Entry entry = null;
          Bases bases = null;
          try
          {
            if (entryGroup.getSequenceEntry() != null)
              bases = entryGroup.getSequenceEntry().getBases();
            if (bases == null)
            {
              entry = new Entry(new_embl_entry);
              bases = entry.getBases();
            }
            else
              entry = new Entry(bases, new_embl_entry);
            entryGroup.add(entry);
          }
          catch (OutOfRangeException e)
          {
            new MessageDialog(null, "read failed: one of the features in "
                + reference + " has an out of range " + "location: "
                + e.getMessage());
          }
          catch (NoSequenceException e)
          {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
        }
        return entryGroup;
      }
      
    
      /**
       * Test and download if on a http server
       * @param fileName
       * @return
       */
    
    tjc's avatar
    tjc committed
      private String testForURL(String fileName, boolean isBCF)
    
        if(!fileName.startsWith("http:") && !fileName.startsWith("ftp:"))
    
          return fileName;
    
    
        return download(fileName+".tbi", ".tbi");
    
      protected String download(String f, String suffix)
    
      {
        try
        {
          final URL urlFile = new URL(f);
          InputStream is = urlFile.openStream();
    
          // Create temp file.
    
          String name = urlFile.getFile();
          int ind = name.lastIndexOf('/');
          if(ind > -1)
            name = name.substring(ind+1);
          File bcfFile = File.createTempFile(name.replaceAll("[\\/\\s]", "_"), suffix);
          
    
          bcfFile.deleteOnExit();
    
          FileOutputStream out = new FileOutputStream(bcfFile);
          int c;
          while ((c = is.read()) != -1)
            out.write(c);
          out.flush();
          out.close();
          is.close();
          return bcfFile.getAbsolutePath();
        }
        catch(IOException ioe)
        {
          JOptionPane.showMessageDialog(null, 
              "Problem downloading\n"+f, 
              "Problem", JOptionPane.WARNING_MESSAGE);
        }
        return null;
      }
      
    
    tjc's avatar
    tjc committed
      /**
       * Read the vcf header
       * @param fileName
       * @return
       */
    
      private String readHeader(String fileName, int index)
    
    tjc's avatar
    tjc committed
      {
        StringBuffer buff = new StringBuffer();
        buff.append(fileName+"\n");
        try
        {
    
    tjc's avatar
    tjc committed
          if(IOUtils.isBCF(fileName))
          {
    
    tjc's avatar
    tjc committed
            vcfReaders[index] = new BCFReader(fileName);
    
            String hdr = ((BCFReader)vcfReaders[index]).headerToString();
            if(hdr.indexOf("VCFv4") > -1)
    
              vcfReaders[index].setVcf_v4(true);
    
            return hdr;
    
    tjc's avatar
    tjc committed
          }
    
    tjc's avatar
    tjc committed
    
    
          String indexfileName = testForURL(fileName, false);
          BlockCompressedInputStream is;
          if(fileName.startsWith("http")|| fileName.startsWith("ftp"))
          {
            URL url = new URL(fileName);
            if(fileName.startsWith("ftp"))
              vcfReaders[index] = new TabixReader(indexfileName.substring(0, indexfileName.length()-4), new FTPSeekableStream(url));
            else
              vcfReaders[index] = new TabixReader(indexfileName.substring(0, indexfileName.length()-4), url);
            is = new BlockCompressedInputStream(url);
          }
          else
          {
            vcfReaders[index] = new TabixReader(fileName);
            is = new BlockCompressedInputStream(new FileInputStream(fileName));
          }
    
    
    tjc's avatar
    tjc committed
          String line;
    
    tjc's avatar
    tjc committed
          while( (line = TabixReader.readLine(is) ) != null )
    
    tjc's avatar
    tjc committed
          {
            if(!line.startsWith("##"))
              break;
    
    tjc's avatar
    tjc committed
            
            if(line.indexOf("VCFv4") > -1)
    
              vcfReaders[index].setVcf_v4(true);
    
    tjc's avatar
    tjc committed
            buff.append(line+"\n");
          }
    
    tjc's avatar
    tjc committed
        }
        catch (IOException e)
        {
          e.printStackTrace();
        }
    
    tjc's avatar
    tjc committed
        
    
    tjc's avatar
    tjc committed
        return buff.toString();
      }
      
      /**
       * Set the number of bases being displayed
       * @param nbasesInView
       */
      private void setZoomLevel(final int nbasesInView)
      {
        int startValue = scrollBar.getValue();
        this.nbasesInView = nbasesInView;
        float pixPerBase = getPixPerBaseByWidth(); 
        this.nbasesInView = (int)(getWidth()/pixPerBase);
        
        if(scrollBar != null)
        {
          scrollBar.setValues(startValue, nbasesInView, 1, seqLength);
          scrollBar.setUnitIncrement(nbasesInView/20);
          scrollBar.setBlockIncrement(nbasesInView);
        }
      }
      
      public String getToolTipText()
      {
    
        if(mouseVCF == null)
    
    tjc's avatar
    tjc committed
          return null;
    
    tjc's avatar
    tjc committed
        String msg = 
    
               "Seq: "+mouseVCF.getChrom()+"\n";
        msg += "Pos: "+mouseVCF.getPos()+"\n";
        msg += "ID:  "+mouseVCF.getID()+"\n";
        msg += "Variant: "+mouseVCF.getRef()+" -> "+mouseVCF.getAlt().toString()+"\n";
        msg += "Qual: "+mouseVCF.getQuality()+"\n";
        String pl;
        if((pl = mouseVCF.getFormatValue("PL")) != null && pl.split(",").length > 1)
    
          msg += "Genotype likelihood (PL): "+pl+"\n";
    
    tjc's avatar
    tjc committed
        return msg;
      }
      
    
      
      
      /**
       * For VCF files with multiple references sequences, calculate
       * the offset from the start of the concatenated sequence for 
       * a given reference.
       * @param refName
       * @return
       */
      protected int getSequenceOffset(String refName)
      {
        if(!concatSequences)
          return 0;
        
        if(offsetLengths == null)
        {   
    
    tjc's avatar
    tjc committed
          String[] contigs = vcfReaders[0].getSeqNames();
    
          FeatureVector features = entryGroup.getAllFeatures();
          offsetLengths = new Hashtable<String, Integer>(contigs.length);
          for(int i=0; i<contigs.length; i++)
          {
            FeatureContigPredicate predicate = new FeatureContigPredicate(contigs[i]);
            for(int j=0; j<features.size(); j++)
            {
              if(predicate.testPredicate(features.elementAt(j)))
              {
                offsetLengths.put(contigs[i], features.elementAt(j).getFirstBase()-1);
                break;
              }
            }
          }
    
          
          if(offsetLengths.size() != contigs.length)
    
    tjc's avatar
    tjc committed
          {
    
            System.err.println("Number of contigs found : "+offsetLengths.size() +
                             "\nNumber of contigs in VCF: "+contigs.length);
    
            JOptionPane.showMessageDialog(this, 
                "There is a problem matching the reference sequences\n"+
                "to the names in the VCF file. This may mean the labels\n"+
                "on the reference features do not match those in the in\n"+
                "the VCF file.", 
                "Problem Found", JOptionPane.WARNING_MESSAGE);
    
    tjc's avatar
    tjc committed
            concatSequences = false;
            return 0;
          }
    
        }
        return offsetLengths.get(refName);
      }
      
    
    tjc's avatar
    tjc committed
      public void repaint()
      {
        super.repaint();
        if(graphPanel != null && graphPanel.isVisible())
          graphPanel.repaint();
      }
      
    
    tjc's avatar
    tjc committed
      protected void paintComponent(Graphics g)
      {
        super.paintComponent(g);
    
    tjc's avatar
    tjc committed
        
        Graphics2D g2d = (Graphics2D)g;
    
    tjc's avatar
    tjc committed
    
        float pixPerBase = getPixPerBaseByWidth();
    
        int start = getBaseAtStartOfView();
    
    tjc's avatar
    tjc committed
        int end   = start+nbasesInView;
    
    tjc's avatar
    tjc committed
        drawSelectionRange((Graphics2D)g, pixPerBase, start, end);
    
        FeatureVector features = getCDSFeaturesInRange(start, end);
    
    tjc's avatar
    tjc committed
        for (int i = 0; i < vcfReaders.length; i++)
    
    tjc's avatar
    tjc committed
        {
    
          if(hideVcfList.contains(i))
            continue;
          
    
          if(concatSequences) 
    
    tjc's avatar
    tjc committed
          {
    
    tjc's avatar
    tjc committed
            String[] contigs = vcfReaders[0].getSeqNames();
    
            for(int j=0; j<contigs.length; j++)
            {