Skip to content
Snippets Groups Projects
BamView.java 110 KiB
Newer Older
   * @param start
   * @param end
   * @param ypos
   */
  private void drawTranslucentJointedLine(Graphics2D g2, int start, int end, int ypos)
  {
    Composite origComposite = g2.getComposite();
    g2.setComposite(translucent);
    
    int mid = (int) ((end-start)/2.f)+start;
    //g2.drawLine(start, ypos, end, ypos);
    g2.drawLine(start, ypos, mid, ypos-5);
    g2.drawLine(mid, ypos-5, end, ypos);
    g2.setComposite(origComposite);
  }
  
tjc's avatar
tjc committed
  /**
   * Display the SNPs for the given read.
   * @param g2
   * @param thisRead
   * @param pixPerBase
   * @param ypos
   */
  private void showSNPsOnReads(Graphics2D g2, SAMRecord thisRead,
tjc's avatar
tjc committed
                               float pixPerBase, int ypos, int offset)
tjc's avatar
tjc committed
  {
    int thisStart = thisRead.getAlignmentStart();
    int thisEnd   = thisRead.getAlignmentEnd();
    
    // use alignment blocks of the contiguous alignment of
    // subsets of read bases to a reference sequence
    List<AlignmentBlock> blocks = thisRead.getAlignmentBlocks();
tjc's avatar
tjc committed
    try
tjc's avatar
tjc committed
    {
tjc's avatar
tjc committed
      char[] refSeq = bases.getSubSequenceC(
tjc's avatar
tjc committed
          new Range(thisStart+offset, thisEnd+offset), Bases.FORWARD);
tjc's avatar
tjc committed
      byte[] readSeq = thisRead.getReadBases();
tjc's avatar
tjc committed

tjc's avatar
tjc committed
      Color col = g2.getColor();
      g2.setColor(Color.red);
      offset = offset - getBaseAtStartOfView();
      for(int i=0; i<blocks.size(); i++)
tjc's avatar
tjc committed
      {
        AlignmentBlock block = blocks.get(i);
        for(int j=0; j<block.getLength(); j++)
tjc's avatar
tjc committed
        {
          int readPos = block.getReadStart()-1+j;
          int refPos  = block.getReferenceStart()+j;

          if (Character.toUpperCase(refSeq[refPos-thisStart]) != readSeq[readPos])
          {
            g2.drawLine((int) ((refPos+offset) * pixPerBase), ypos + 2,
                        (int) ((refPos+offset) * pixPerBase), ypos - 2);
tjc's avatar
tjc committed
        }
tjc's avatar
tjc committed
      }
tjc's avatar
tjc committed
      g2.setColor(col);
    }
    catch (OutOfRangeException e)
    {
      System.err.println(thisRead.getReadName()+" "+e.getMessage());
tjc's avatar
tjc committed
    }
tjc's avatar
tjc committed
  }
  
  /**
   * Add the alignment view to the supplied <code>JPanel</code> in
   * a <code>JScrollPane</code>.
   * @param mainPanel  panel to add the alignment to
tjc's avatar
tjc committed
   * @param frame
   * @param autohide automatically hide the top panel containing the buttons
tjc's avatar
tjc committed
   * @param feature_display
tjc's avatar
tjc committed
  private void addBamToPanel(final JFrame frame)
tjc's avatar
tjc committed
  {
tjc's avatar
tjc committed
    final JComponent topPanel = bamTopPanel(frame);
tjc's avatar
tjc committed
    mainPanel.setPreferredSize(new Dimension(900, 400));
tjc's avatar
tjc committed
    
    setDisplay(1, nbasesInView, null);
    mainPanel.setLayout(new BorderLayout());
tjc's avatar
tjc committed
    if(topPanel instanceof JPanel)
      mainPanel.add(topPanel, BorderLayout.NORTH);
    mainPanel.add(jspView, BorderLayout.CENTER);
tjc's avatar
tjc committed
    JPanel bottomPanel = new JPanel(new BorderLayout());
    coveragePanel = new CoveragePanel(this);
    bottomPanel.add(coveragePanel, BorderLayout.CENTER);

tjc's avatar
tjc committed
    //
    snpPanel = new SnpPanel(this, bases);
    bottomPanel.add(snpPanel, BorderLayout.NORTH);
    
tjc's avatar
tjc committed
    if(feature_display == null)
    {
      scrollBar = new JScrollBar(JScrollBar.HORIZONTAL, 1, nbasesInView, 1,
          getMaxBasesInPanel(getSequenceLength()));
      scrollBar.setUnitIncrement(nbasesInView/20);
tjc's avatar
tjc committed
      scrollBar.addAdjustmentListener(new AdjustmentListener()
      {
        public void adjustmentValueChanged(AdjustmentEvent e)
        {
          repaint();

          if(isSNPplot)
            snpPanel.repaint();
          if(isCoverage)
            coveragePanel.repaint();
tjc's avatar
tjc committed
        }
      });
      bottomPanel.add(scrollBar, BorderLayout.SOUTH);
    }
      if(!isConcatSequences())
      {
        int seqLen = seqLengths.get((String) combo.getSelectedItem());
        int artemisSeqLen = feature_display.getSequenceLength();
        if(seqLen != artemisSeqLen)
        {
          int newIndex = -1;
          for(int i=0; i<seqNames.size(); i++)
          {
            if(seqLengths.get(seqNames.get(i)) == artemisSeqLen)
            {
              // this looks like the correct sequence
              combo.setSelectedIndex(i);
              newIndex = i;
            }
          }

          if(!Options.isBlackBeltMode())
          {
            String label[] = {
                "The length of the sequence loaded does not match the length of",
                "the default reference sequence in the BAM ("+seqNames.get(0)+").",
                (newIndex == -1 ? "" : "The length does match the reference "+
tcarver's avatar
tcarver committed
                    seqNames.get(newIndex)+" so this has been set as the default.") 
            };
            new NonModalDialog(frame, label);
          }
tjc's avatar
tjc committed

    mainPanel.add(bottomPanel, BorderLayout.SOUTH);
    coveragePanel.setPreferredSize(new Dimension(900, 100));
    coveragePanel.setVisible(false);
tjc's avatar
tjc committed
    snpPanel.setPreferredSize(new Dimension(900, 100));
    snpPanel.setVisible(false);

tjc's avatar
tjc committed
    mainPanel.revalidate();
tjc's avatar
tjc committed
    jspView.getVerticalScrollBar().setValue(
        jspView.getVerticalScrollBar().getMaximum());
  
  private void addToViewMenu(final int thisBamIndex)
  {
    File f = new File(bamList.get(thisBamIndex));
    final JCheckBoxMenuItem cbBam = new JCheckBoxMenuItem(
                                     f.getName(), true);
    bamFilesMenu.add(cbBam);
    cbBam.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        if(cbBam.isSelected())
          hideBamList.remove(new Integer(thisBamIndex));
        else
          hideBamList.add(new Integer(thisBamIndex));
        laststart = -1;
        repaint();
      } 
    });
  }
tjc's avatar
tjc committed

  private void createMenus(JComponent menu)
    final JMenuItem addBam = new JMenuItem("Add BAM ...");
    menu.add(addBam);
    addBam.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        FileSelectionDialog bamFileSelection = new FileSelectionDialog(
            null, false, "BamView", "BAM");
        List<String> bamFiles = bamFileSelection.getFiles(".*\\.(bam|cram)$");
        int count = bamList.size();
       
        bamList.addAll(bamFileSelection.getFiles(".*\\.(bam|cram)$"));
        
        for(int i=0; i<bamFiles.size(); i++)
          addToViewMenu(i+count);
tjc's avatar
tjc committed
        laststart = -1; 
        repaint();
      } 
    });
    
    bamFilesMenu.setFont(addBam.getFont());
    menu.add(bamFilesMenu);
    
    final JMenu analyse = new JMenu("Analyse");
    menu.add(analyse);
tjc's avatar
tjc committed
    final JMenuItem readCount = new JMenuItem("Read count of selected features ...");
    analyse.add(readCount);
    if(feature_display == null)
      readCount.setEnabled(false);
    readCount.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        FeatureVector features = feature_display.getSelection().getAllFeatures();
        JCheckBox overlap = new JCheckBox("Include all overlapping reads", true);
        overlap.setToolTipText("Include reads that partially overlap the feature");
        JCheckBox spliced = new JCheckBox("Introns included", true);
        Box yBox = Box.createVerticalBox();
        yBox.add(overlap);
        yBox.add(spliced);
tcarver's avatar
tcarver committed
        
        final ReadCountDialog opts = new ReadCountDialog(new JFrame(),
            "Read Count Options", feature_display, yBox);
        if(opts.getStatus() == -1)
          return;
        //JOptionPane.showMessageDialog(null, yBox, "Read Count Option", JOptionPane.INFORMATION_MESSAGE);
        new MappedReads(features, (String)combo.getSelectedItem(), samFileReaderHash, bamList,
            seqNames, offsetLengths, concatSequences, seqLengths, 
            samRecordFlagPredicate, samRecordMapQPredicate,
            !overlap.isSelected(), spliced.isSelected());
tjc's avatar
tjc committed
      } 
    });
    
    final JMenuItem rpkm = new JMenuItem("RPKM value of selected features ...");
    analyse.add(rpkm);
    if(feature_display == null)
      rpkm.setEnabled(false);
tjc's avatar
tjc committed
    rpkm.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        FeatureVector features = feature_display.getSelection().getAllFeatures();
tjc's avatar
tjc committed
        JCheckBox overlap = new JCheckBox("Include all overlapping reads", true);
        overlap.setToolTipText("Include reads that partially overlap the feature");
        JCheckBox spliced = new JCheckBox("Introns included", true);
        JCheckBox allRefSeqs = new JCheckBox("Use reads mapped to all reference sequences", false);
        
tjc's avatar
tjc committed
        Box yBox = Box.createVerticalBox();
        yBox.add(overlap);
        yBox.add(spliced);
        
        if(seqLengths.size() > 1)
          yBox.add(allRefSeqs);
tcarver's avatar
tcarver committed
     
        final ReadCountDialog opts = new ReadCountDialog(new JFrame(),
            "RPKM Options", feature_display, yBox);
        if(opts.getStatus() == -1)
          return;

tjc's avatar
tjc committed
        int seqlen = 0;
        if(feature_display != null)
          seqlen = feature_display.getSequenceLength();
        else if(bases != null)
          seqlen = bases.getLength();
        
        new MappedReads(features, (String)combo.getSelectedItem(),
            samFileReaderHash, bamList, seqNames, offsetLengths, concatSequences, 
            seqLengths, seqlen, samRecordFlagPredicate, samRecordMapQPredicate,
            !overlap.isSelected(), spliced.isSelected(), allRefSeqs.isSelected());
    for(int i=0; i<bamList.size(); i++)
      addToViewMenu(i);
    
    menu.add(new JSeparator());
    
    JMenu viewMenu = new JMenu("Views");
tjc's avatar
tjc committed
    cbStackView.setFont(viewMenu.getFont());
    cbIsizeStackView.setFont(viewMenu.getFont());
    cbPairedStackView.setFont(viewMenu.getFont());
    cbStrandStackView.setFont(viewMenu.getFont());
    cbCoverageView.setFont(viewMenu.getFont());
    
    baseQualityColour.setFont(viewMenu.getFont());
    colourByCoverageColour.setFont(viewMenu.getFont());
    markInsertions.setFont(viewMenu.getFont());
    
    cbIsizeStackView.addActionListener(new ActionListener()
tjc's avatar
tjc committed
    {
      public void actionPerformed(ActionEvent e)
      {
        laststart = -1;
        logMenuItem.setEnabled(isIsizeStackView());
tjc's avatar
tjc committed
        repaint();
      }
    });
    viewMenu.add(cbIsizeStackView);
    cbStackView.addActionListener(new ActionListener()
tjc's avatar
tjc committed
    {
      public void actionPerformed(ActionEvent e)
      {
        laststart = -1;
        logMenuItem.setEnabled(isIsizeStackView());
tjc's avatar
tjc committed
        repaint();
      }
    });
    viewMenu.add(cbStackView);
    cbPairedStackView.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        laststart = -1;
        logMenuItem.setEnabled(isIsizeStackView());
    viewMenu.add(cbPairedStackView);
    cbStrandStackView.addActionListener(new ActionListener()
tjc's avatar
tjc committed
    {
      public void actionPerformed(ActionEvent e)
      {
        laststart = -1;
        if(isStrandStackView())
tjc's avatar
tjc committed
        {
          setViewportMidPoint();
        }
        logMenuItem.setEnabled(isIsizeStackView());
tjc's avatar
tjc committed
        repaint();
      }
    });
    viewMenu.add(cbStrandStackView);
    
    cbCoverageView.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        laststart = -1;
        logMenuItem.setEnabled(isIsizeStackView());
        repaint();
      }
    });
    viewMenu.add(cbCoverageView);
    
    menu.add(viewMenu);
tjc's avatar
tjc committed
    final JCheckBoxMenuItem checkBoxSNPs = new JCheckBoxMenuItem("SNP marks");
    // 
    JMenu colourMenu = new JMenu("Colour By");
    colourMenu.add(colourByCoverageColour);
    
    baseQualityColour.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        if(baseQualityColour.isSelected())
        {
          checkBoxSNPs.setSelected(false);
          isSNPs = false;
        }
        repaint();
      }
    });
    colourMenu.add(baseQualityColour);
    menu.add(colourMenu);
    
    //
    JMenu showMenu = new JMenu("Show");
    JCheckBoxMenuItem checkBoxOrientation = new JCheckBoxMenuItem("Orientation");
    checkBoxOrientation.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        isOrientation = !isOrientation;
        repaint();
      }
    });
    showMenu.add(checkBoxOrientation);
    
    JCheckBoxMenuItem checkBoxSingle = new JCheckBoxMenuItem("Single Reads");
    checkBoxSingle.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        repaint();
        isSingle = !isSingle;
      }
    });
    showMenu.add(checkBoxSingle);
    checkBoxSNPs.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        if (isSNPs && bases == null)
        {
          JOptionPane.showMessageDialog(null,
              "No reference sequence supplied to identify SNPs.", "SNPs",
              JOptionPane.INFORMATION_MESSAGE);
        }
        isSNPs = !isSNPs;
        
        if(isSNPs)
          baseQualityColour.setSelected(false);
        repaint();
      }
    });
    showMenu.add(checkBoxSNPs);
    
    markInsertions.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        repaint();
      }
    });
    showMenu.add(markInsertions);
tjc's avatar
tjc committed
    menu.add(showMenu);
tjc's avatar
tjc committed
    //
    JMenu graphMenu = new JMenu("Graph");
    JCheckBoxMenuItem checkBoxCoverage = new JCheckBoxMenuItem("Coverage");
    checkBoxCoverage.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        isCoverage = !isCoverage;
        coveragePanel.setVisible(isCoverage);
tjc's avatar
tjc committed
        
        if(isCoverage && !cbCoverageView.isSelected())
          laststart = -1;
        repaint();
      }
    });
tjc's avatar
tjc committed
    graphMenu.add(checkBoxCoverage);
    
    JCheckBoxMenuItem checkBoxSNP = new JCheckBoxMenuItem("SNP");
    checkBoxSNP.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        isSNPplot = !isSNPplot;
        snpPanel.setVisible(isSNPplot);
        laststart = -1;
tjc's avatar
tjc committed
        repaint();
      }
    });
    graphMenu.add(checkBoxSNP);
    menu.add(graphMenu);
    
    if(feature_display != null)
      final JCheckBoxMenuItem checkBoxSync =
        new JCheckBoxMenuItem("Asynchronous", asynchronous);
      checkBoxSync.addActionListener(new ActionListener()
        public void actionPerformed(ActionEvent e)
        {
          asynchronous = checkBoxSync.isSelected();
        }
      });
      menu.add(checkBoxSync);
    menu.add(new JSeparator());
    JMenu maxHeightMenu = new JMenu("BamView Height");
    menu.add(maxHeightMenu);
    final String hgts[] =
       {"500", "800", "1000", "1500", "2500", "5000", "50000"};
    
    ButtonGroup bgroup = new ButtonGroup();
    for(int i=0; i<hgts.length; i++)
    {
      final String hgt = hgts[i];
      final JCheckBoxMenuItem maxHeightMenuItem = new JCheckBoxMenuItem(hgt);
      bgroup.add(maxHeightMenuItem);
      maxHeightMenuItem.setSelected(hgts[i].equals(Integer.toString(maxHeight)));
      maxHeightMenu.add(maxHeightMenuItem);
      maxHeightMenuItem.addActionListener(new ActionListener()
      {
        public void actionPerformed(ActionEvent e)
        {
          if(maxHeightMenuItem.isSelected())
            maxHeight = Integer.parseInt(hgt);
          int start = getBaseAtStartOfView();
          setDisplay(start, nbasesInView+start, null);
        }
      });
    }
    menu.add(new JSeparator());
tjc's avatar
tjc committed
    logMenuItem.setFont(menu.getFont());
    menu.add(logMenuItem);
    logMenuItem.setEnabled(isIsizeStackView());
    
    logMenuItem.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        logScale = logMenuItem.isSelected();
        repaint();
      }
    });
    
tjc's avatar
tjc committed
    JMenuItem filter = new JMenuItem("Filter Reads ...");
    menu.add(filter);
    filter.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
tjc's avatar
tjc committed
        if(filterFrame == null)
          filterFrame = new SAMRecordFilter(BamView.this);
        else
          filterFrame.setVisible(true);
tcarver's avatar
tcarver committed
    
tcarver's avatar
tcarver committed
    JMenuItem readList = new JMenuItem("List Reads ...");
tcarver's avatar
tcarver committed
    menu.add(readList);
    readList.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
tcarver's avatar
tcarver committed
        new SAMRecordList(BamView.this);
tcarver's avatar
tcarver committed
      }
tcarver's avatar
tcarver committed
    });
tjc's avatar
tjc committed
    final JMenuItem bamSplitter = new JMenuItem("Clone window");
    bamSplitter.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        BamView bamView = new BamView(new Vector<String>(bamList), 
            null, nbasesInView, entry_edit,
tjc's avatar
tjc committed
            feature_display, bases, (JPanel) mainPanel.getParent(), null);
        bamView.getJspView().getVerticalScrollBar().setValue(
            bamView.getJspView().getVerticalScrollBar().getMaximum());

        int start = getBaseAtStartOfView();
        setDisplay(start, nbasesInView+start, null);
        if(feature_display != null)
        {
          feature_display.addDisplayAdjustmentListener(bamView);
          feature_display.getSelection().addSelectionChangeListener(bamView);
        }
      } 
    });
    menu.add(new JSeparator());
    menu.add(bamSplitter);

    //
    JMenu coverageMenu = new JMenu("Coverage Options");
    coverageView.init(this, 0.f, 0, 0);
    coverageView.createMenus(coverageMenu);
    viewMenu.add(new JSeparator());
    viewMenu.add(coverageMenu);
tjc's avatar
tjc committed
  private JComponent bamTopPanel(final JFrame frame)
  {
    final JComponent topPanel;
tjc's avatar
tjc committed
    if(frame == null)
tjc's avatar
tjc committed
    {
      topPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
      if(feature_display != null)
        this.selection = feature_display.getSelection(); 
tjc's avatar
tjc committed
    }
    else
    { 
      topPanel = new JMenuBar();
      frame.setJMenuBar((JMenuBar)topPanel);
      
      JMenu fileMenu = new JMenu("File");
      topPanel.add(fileMenu);

      JMenuItem readBam = new JMenuItem("Open new BamView ...");
      fileMenu.add(readBam);
      readBam.addActionListener(new ActionListener()
      {
        public void actionPerformed(ActionEvent e)
        {
          String[] s = { "NEW-BAMVIEW" };
tjc's avatar
tjc committed
          BamView.main(s);
        } 
      });
      
      JMenuItem close = new JMenuItem("Close");
      fileMenu.add(close);
      close.addActionListener(new ActionListener()
      {
        public void actionPerformed(ActionEvent e)
        {
          BamView.this.setVisible(false);
          Component comp = BamView.this;
          
          while( !(comp instanceof JFrame) )
            comp = comp.getParent();
          ((JFrame)comp).dispose();
        } 
      });
      
      JMenuItem exit = new JMenuItem("Exit");
      fileMenu.add(new JSeparator());
      fileMenu.add(exit);
      exit.addActionListener(new ActionListener()
      {
        public void actionPerformed(ActionEvent e)
        {
          int status = JOptionPane.showConfirmDialog(BamView.this, 
              "Exit BamView?", "Exit", 
              JOptionPane.OK_CANCEL_OPTION);
          if(status != JOptionPane.OK_OPTION)
            return;
          System.exit(0);
        } 
      });
      
      addKeyListener(new KeyAdapter()
      {
        public void keyPressed(final KeyEvent event)
        {
          switch (event.getKeyCode())
          {
            case KeyEvent.VK_UP:
              setZoomLevel((int) (BamView.this.nbasesInView * 1.1));
              break;
            case KeyEvent.VK_DOWN:
              if (showBaseAlignment)
                break;
              setZoomLevel((int) (BamView.this.nbasesInView * .9));
              break;
            default:
              break;
          }
        }
      });
    }
    
    if(seqNames.size() > 1)
    {
      int len = 0;
      for(int i=0; i<seqNames.size(); i++)
        len += seqLengths.get(seqNames.get(i));
      
      if(feature_display != null &&
         len == feature_display.getSequenceLength())
        concatSequences = true;
      else if(bases != null &&
          len == bases.getLength() )
        concatSequences = true;
    }

    // auto hide top panel
tjc's avatar
tjc committed
    final JCheckBox buttonAutoHide = new JCheckBox("Hide", (frame == null));
tjc's avatar
tjc committed
    buttonAutoHide.setToolTipText("Auto-Hide");
    final MouseMotionListener mouseMotionListener = new MouseMotionListener()
    {
      public void mouseDragged(MouseEvent event)
      {
        handleCanvasMouseDrag(event);
      }
      
      public void mouseMoved(MouseEvent e)
      {
        lastMousePoint = 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);
        }
        mainPanel.repaint();
        mainPanel.revalidate();
      }
    };
    addMouseMotionListener(mouseMotionListener);

    combo = new JComboBox(seqNames);
    JTextComponent editor = (JTextComponent) combo.getEditor().getEditorComponent();
    editor.setDocument(new AutoCompleteComboDocument(combo));
    combo.setEditable(true);
tjc's avatar
tjc committed
    combo.setMaximumRowCount(20);
    
    combo.addItemListener(new ItemListener()
    {
      public void itemStateChanged(ItemEvent e)
      {
        laststart = -1;
        if(feature_display != null)
          setZoomLevel(feature_display.getMaxVisibleBases());
        else
          setZoomLevel(BamView.this.nbasesInView);
      }
    });
    topPanel.add(combo);

    if(feature_display == null)
    {
      final JTextField baseText = new JTextField(8);
      JButton goTo = new JButton("GoTo:");
      goTo.setToolTipText("Go to base position");
      goTo.addActionListener(new ActionListener()
      {
        public void actionPerformed(ActionEvent e)
        {
          try
          {
            int basePosition = Integer.parseInt(baseText.getText());
            scrollBar.setValue(basePosition);
          }
          catch (NumberFormatException nfe)
          {
            JOptionPane.showMessageDialog(BamView.this,
                "Expecting a base number!", "Number Format",
                JOptionPane.WARNING_MESSAGE);
          }
        }
      });
      topPanel.add(goTo);
      topPanel.add(baseText);

      JButton zoomIn = new JButton("-");
      zoomIn.setToolTipText("Zoom out (up arrow)");
tjc's avatar
tjc committed
      Insets ins = new Insets(1,1,1,1);
      zoomIn.setMargin(ins);
      zoomIn.addActionListener(new ActionListener()
      {
        public void actionPerformed(ActionEvent e)
        {
          setZoomLevel((int) (BamView.this.nbasesInView * 1.1));
        }
      });
      topPanel.add(zoomIn);

      JButton zoomOut = new JButton("+");
      zoomOut.setToolTipText("Zoom in (down arrow)");
tjc's avatar
tjc committed
      zoomOut.setMargin(ins);
      zoomOut.addActionListener(new ActionListener()
      {
        public void actionPerformed(ActionEvent e)
        {
          if (showBaseAlignment)
            return;
          setZoomLevel((int) (BamView.this.nbasesInView * .9));
        }
      });
      topPanel.add(zoomOut);
    }
    
    topPanel.add(buttonAutoHide);
    
    if(feature_display != null)
    {
      JButton close = new JButton("Close");
      topPanel.add(close);
      close.addActionListener(new ActionListener()
      {
        public void actionPerformed(ActionEvent e)
        {
          final JPanel containerPanel = (JPanel) mainPanel.getParent();
          feature_display.removeDisplayAdjustmentListener(BamView.this);
          feature_display.getSelection().removeSelectionChangeListener(BamView.this);
          containerPanel.remove(mainPanel);
          
          if(containerPanel.getComponentCount() > 0)
            containerPanel.revalidate();
          else
          {
            if(entry_edit != null)
              entry_edit.setNGDivider();
            else
              containerPanel.setVisible(false);
          }
        }
      });
    }
tjc's avatar
tjc committed
    return topPanel;
  }
  
  public void setVisible(boolean visible)
  {
    super.setVisible(visible);
    mainPanel.setVisible(visible);
tjc's avatar
tjc committed
  }
  
tjc's avatar
tjc committed
  private void setViewportMidPoint()
  {
    Point p = jspView.getViewport().getLocation();
    p.y = (getHeight() - jspView.getViewport().getViewRect().height)/2;
    jspView.getViewport().setViewPosition(p);
  }
  
tjc's avatar
tjc committed
  protected int getBaseAtStartOfView()
tjc's avatar
tjc committed
  {
tjc's avatar
tjc committed
    if(feature_display != null)
      return feature_display.getForwardBaseAtLeftEdge();
    else
      return scrollBar.getValue();
tjc's avatar
tjc committed
  }
  
  /**
   * Set the panel size based on the number of bases visible
   * and repaint.
   * @param nbasesInView
   */
tjc's avatar
tjc committed
  private void setZoomLevel(final int nbasesInView)
  {
tjc's avatar
tjc committed
    int startValue = getBaseAtStartOfView();
tjc's avatar
tjc committed
    this.nbasesInView = nbasesInView;
tjc's avatar
tjc committed
    float pixPerBase = getPixPerBaseByWidth(); 
tjc's avatar
tjc committed

    if(isBaseAlignmentView(pixPerBase))
tjc's avatar
tjc committed
    {
      pixPerBase = ALIGNMENT_PIX_PER_BASE;
      this.nbasesInView = (int)(mainPanel.getWidth()/pixPerBase);
tjc's avatar
tjc committed
      jspView.getVerticalScrollBar().setValue(0);
      
      if(ruler == null)
        ruler = new Ruler();
tjc's avatar
tjc committed
      jspView.setColumnHeaderView(ruler);
      showBaseAlignment = true;
      baseQualityColour.setEnabled(true);
      markInsertions.setEnabled(true);
tjc's avatar
tjc committed
    }
    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;
      }
      
tjc's avatar
tjc committed
      jspView.setColumnHeaderView(null);
tjc's avatar
tjc committed
      
      if(!isStrandStackView())
tjc's avatar
tjc committed
        jspView.getVerticalScrollBar().setValue(
            jspView.getVerticalScrollBar().getMaximum());
tjc's avatar
tjc committed
      showBaseAlignment = false;
      baseQualityColour.setEnabled(false);
      markInsertions.setEnabled(false);
tjc's avatar
tjc committed
    }
    
tjc's avatar
tjc committed
    if(scrollBar != null)
tjc's avatar
tjc committed
      scrollBar.setValues(startValue, nbasesInView, 1, 
             getMaxBasesInPanel(getSequenceLength()));
      scrollBar.setUnitIncrement(nbasesInView/20);
      scrollBar.setBlockIncrement(nbasesInView);
tjc's avatar
tjc committed
  }
tjc's avatar
tjc committed

tjc's avatar
tjc committed
  /**
   * Set the start and end base positions to display.
   * @param start
   * @param end
   * @param event
tjc's avatar
tjc committed
   */
  public void setDisplay(int start,
                         int end,
                         DisplayAdjustmentEvent event)
tjc's avatar
tjc committed
  {
    this.startBase = start;
    this.endBase   = end;
tjc's avatar
tjc committed
    this.nbasesInView = end-start+1;
    lastMousePoint = null;
tjc's avatar
tjc committed
    float pixPerBase;
    if(jspView.getViewport().getViewRect().width > 0)
tjc's avatar
tjc committed
      pixPerBase = getPixPerBaseByWidth();
tjc's avatar
tjc committed
    else
    {
      if(feature_display == null)
        pixPerBase = 1000.f/(float)(end-start+1);
      else
        pixPerBase = feature_display.getWidth()/(float)(end-start+1);
    }
tjc's avatar
tjc committed
    Dimension d = new Dimension();
tjc's avatar
tjc committed
    d.setSize(nbasesInView*pixPerBase, maxHeight);
tjc's avatar
tjc committed
    setPreferredSize(d);
tjc's avatar
tjc committed
    if(event == null)
    {
      this.startBase = -1;
      this.endBase   = -1;
    }
tjc's avatar
tjc committed
  /**
   * 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);
tjc's avatar
tjc committed
    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
    {
      if(entryGroup.getSequenceEntry() != null)
        bases = entryGroup.getSequenceEntry().getBases();
tjc's avatar
tjc committed
      
tjc's avatar
tjc committed
      if(bases == null)
tjc's avatar
tjc committed
      {
tjc's avatar
tjc committed
        entry = new Entry(new_embl_entry);
tjc's avatar
tjc committed
        bases = entry.getBases();
      }
tjc's avatar
tjc committed
      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;
  }
  
  private boolean isShowScale()
  {
tjc's avatar
tjc committed
    return (feature_display == null ? true : false);
  }

  public JScrollPane getJspView()
  {
    return jspView;
  }
  
  /**
   *  Handle a mouse drag event on the drawing canvas.
   **/
tjc's avatar
tjc committed
  private void handleCanvasMouseDrag(final MouseEvent event)
    if(event.getButton() == MouseEvent.BUTTON3 || bases == null) 
    if(event.getClickCount() > 1)
    {
      getSelection().clear();
      repaint();
      return;  
    }
    
    highlightRange(event, 
        MouseEvent.BUTTON1_DOWN_MASK & MouseEvent.BUTTON2_DOWN_MASK);
  }
  
  /**
   * 
   * @param event
   * @param onmask