Skip to content
Snippets Groups Projects
BamView.java 69.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • tjc's avatar
    tjc committed
        drawSelectionRange(g2, pixPerBase,start, end);   
        BasicStroke stroke = new BasicStroke(
            1.3f,
            BasicStroke.CAP_BUTT, 
            BasicStroke.JOIN_MITER);
        
        int scaleHeight = 15;
        drawScale(g2, start, end, pixPerBase, ((getHeight()+scaleHeight)/2));
    
        int ymid = (getHeight()/ 2);
        // positive strand    
        drawStrand(g2, false, scaleHeight, ymid-(scaleHeight/2), -2, pixPerBase, stroke);
        
        // negative strand
        drawStrand(g2, true, scaleHeight, ymid+(scaleHeight/2), 2, pixPerBase, stroke);
      }
      
      private void drawStrand(Graphics2D g2, 
                              boolean isStrandNegative, 
                              int scaleHeight,
                              int ymid,
                              int ystep,
                              float pixPerBase,
                              Stroke stroke)
      {
        int ypos = (getHeight() - scaleHeight);
        int maxEnd = 0;
        int lstStart = 0;
        int lstEnd = 0;
    
        int baseAtStartOfView = getBaseAtStartOfView();
    
    tjc's avatar
    tjc committed
        g2.setColor(Color.blue);
    
        Rectangle r = jspView.getViewport().getViewRect();
    
    tjc's avatar
    tjc committed
        
        for(int i=0; i<readsInView.size(); i++)
        {
          SAMRecord samRecord = readsInView.get(i);
          
          if( samRecord.getReadNegativeStrandFlag() == isStrandNegative )
          {
            int offset = getSequenceOffset(samRecord.getReferenceName());
            int recordStart = samRecord.getAlignmentStart()+offset;
            int recordEnd   = samRecord.getAlignmentEnd()+offset;
          
            if(lstStart != recordStart || lstEnd != recordEnd)
            { 
              if (!samRecord.getReadPairedFlag() ||   // read is not paired in sequencing
                   samRecord.getMateUnmappedFlag() )  // mate is unmapped 
                g2.setColor(Color.black);
              else
                g2.setColor(Color.blue);
            
              if(maxEnd < recordStart)
              {
                ypos = ymid + ystep;
                maxEnd = recordEnd+2;
              }
              else
                ypos = ypos + ystep;
            }
            else
              g2.setColor(darkGreen);
    
            lstStart = recordStart;
            lstEnd   = recordEnd;
    
            if(ypos > r.getMaxY() || ypos < r.getMinY())
    
              continue;
            drawRead(g2, samRecord, pixPerBase, ypos, baseAtStartOfView);
    
    tjc's avatar
    tjc committed
          }
        }
      }
    
      
      /**
       * Draw the reads as lines in vertical stacks. The reads are colour 
       * coded as follows:
       * 
       * blue  - reads are unique and are paired with a mapped mate
       * black - reads are unique and are not paired or have an unmapped mate
       * green - reads are duplicates
       * 
       * @param g2
       * @param seqLength
       * @param pixPerBase
       * @param start
       * @param end
       */
      private void drawPairedStackView(Graphics2D g2, 
                                       int seqLength, 
                                       float pixPerBase, 
                                       int start, 
                                       int end)
      {
        drawSelectionRange(g2, pixPerBase,start, end);
        if(isShowScale())
    
    tjc's avatar
    tjc committed
          drawScale(g2, start, end, pixPerBase, getHeight());
    
        
        Vector<PairedRead> pairedReads = new Vector<PairedRead>();
        for(int i=0; i<readsInView.size(); i++)
        {
          SAMRecord samRecord = readsInView.get(i);
    
          if( !samRecord.getReadPairedFlag() ||  // read is not paired in sequencing
              samRecord.getMateUnmappedFlag() )  // mate is unmapped
            continue;
    
          SAMRecord samNextRecord = null;      
          if(i < readsInView.size()-1)
          {
            samNextRecord = readsInView.get(++i);
            PairedRead pr = new PairedRead();
            if(samRecord.getReadName().equals(samNextRecord.getReadName()))
            { 
              if(samRecord.getAlignmentStart() < samNextRecord.getAlignmentStart())
              {
                pr.sam1 = samRecord;
                pr.sam2 = samNextRecord;
              }
              else
              {
                pr.sam2 = samRecord;
                pr.sam1 = samNextRecord;
              }
              
            }
            else
            {
              --i;
              pr.sam1 = samRecord;
              pr.sam2 = null;
            }
            pairedReads.add(pr);
          }
        }
        Collections.sort(pairedReads, new PairedReadComparator());
        
        Stroke originalStroke = new BasicStroke (1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND); 
        Stroke stroke =
                new BasicStroke (1.3f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
    
        g2.setStroke(stroke);
        
    
        int scaleHeight;
        if(isShowScale())
          scaleHeight = 15;
        else
          scaleHeight = 0;
        
        int ypos = getHeight() - scaleHeight - 3;
        int lastEnd = 0;
    
        int baseAtStartOfView = getBaseAtStartOfView();
    
        Rectangle r = jspView.getViewport().getViewRect();
    
        
        for(int i=0; i<pairedReads.size(); i++)
        {
          PairedRead pr = pairedReads.get(i);
          
          if(pr.sam1.getAlignmentStart() > lastEnd)
          {
            ypos = getHeight() - scaleHeight - 3;
            
            if(pr.sam2 != null)
            {  
              lastEnd = pr.sam2.getAlignmentEnd();
            }
            else
              lastEnd = pr.sam1.getAlignmentEnd();
          }
          else
            ypos = ypos - 3;
          
    
          if(ypos > r.getMaxY() || ypos < r.getMinY())
    
          g2.setStroke(originalStroke);
          g2.setColor(Color.LIGHT_GRAY);
          
          if(pr.sam2 != null)
          {
            drawTranslucentJointedLine(g2, 
    
    tjc's avatar
    tjc committed
                    (int)((pr.sam1.getAlignmentEnd()-getBaseAtStartOfView())*pixPerBase),
                    (int)((pr.sam2.getAlignmentStart()-getBaseAtStartOfView())*pixPerBase), ypos);
    
          }
          else
          {
            if(!pr.sam1.getMateUnmappedFlag())
            {
              int prStart;
              
              if(pr.sam1.getAlignmentStart() > pr.sam1.getMateAlignmentStart())
                prStart = pr.sam1.getAlignmentEnd();
              else
                prStart = pr.sam1.getAlignmentStart();
                
              drawTranslucentJointedLine(g2, 
    
    tjc's avatar
    tjc committed
                  (int)( (prStart-getBaseAtStartOfView())*pixPerBase),
                  (int)( (pr.sam1.getMateAlignmentStart()-getBaseAtStartOfView())*pixPerBase), ypos);
    
            }
          }
          
          if( pr.sam1.getReadNegativeStrandFlag() && // strand of the query (1 for reverse)
              ( pr.sam2 != null && pr.sam2.getReadNegativeStrandFlag() ) )
            g2.setColor(Color.red);
          else
            g2.setColor(Color.blue);
    
          drawRead(g2, pr.sam1, pixPerBase, ypos, baseAtStartOfView);
    
            drawRead(g2, pr.sam2, pixPerBase, ypos, baseAtStartOfView);
    
    tjc's avatar
    tjc committed
      /**
       * Draw a read that apparently has a read mate that is not in view.
       * @param g2
       * @param thisRead
       * @param ypos
       * @param pixPerBase
       * @param originalStroke
       * @param stroke
       */
    
    tjc's avatar
    tjc committed
      private void drawLoneRead(Graphics2D g2, SAMRecord samRecord, int ypos, 
    
          float pixPerBase, int baseAtStartOfView)
    
    tjc's avatar
    tjc committed
      {
    
        boolean offTheTop = false;
    
    tjc's avatar
    tjc committed
        int offset = getSequenceOffset(samRecord.getReferenceName());
    
        int thisStart = samRecord.getAlignmentStart()+offset;
        int thisEnd   = thisStart + samRecord.getReadString().length() -1;
    
    tjc's avatar
    tjc committed
        
    
        if(ypos <= 0)
        {
          offTheTop = true;
          ypos = samRecord.getReadString().length();
        }
          
        if(Math.abs(samRecord.getMateAlignmentStart()-samRecord.getAlignmentEnd())*pixPerBase > 2.f)
    
    tjc's avatar
    tjc committed
        {
          g2.setColor(Color.LIGHT_GRAY);
          
          if(samRecord.getAlignmentEnd() < samRecord.getMateAlignmentStart())
          {
    
    tjc's avatar
    tjc committed
            int nextStart = 
    
              (int)((samRecord.getMateAlignmentStart()-getBaseAtStartOfView()+offset)*pixPerBase);
    
            drawTranslucentLine(g2, 
    
              (int)((thisEnd-getBaseAtStartOfView())*pixPerBase), nextStart, ypos);
    
    tjc's avatar
    tjc committed
          }
          else
          {
    
    tjc's avatar
    tjc committed
            int nextStart = 
                (int)((samRecord.getMateAlignmentStart()-getBaseAtStartOfView()+offset)*pixPerBase);
    
            drawTranslucentLine(g2, 
    
                (int)((thisStart-getBaseAtStartOfView())*pixPerBase), nextStart, ypos);
    
    tjc's avatar
    tjc committed
          }
        }
        
    
        if(offTheTop)
          g2.setColor(darkOrange); 
        else if(samRecord.getReadNegativeStrandFlag()) // strand of the query (1 for reverse)
    
    tjc's avatar
    tjc committed
          g2.setColor(Color.red);
        else
          g2.setColor(Color.blue);
    
        drawRead(g2, samRecord, pixPerBase, ypos, baseAtStartOfView);
    
    tjc's avatar
    tjc committed
        
    
    tjc's avatar
    tjc committed
        if (isSNPs)
    
    tjc's avatar
    tjc committed
          showSNPsOnReads(g2, samRecord, pixPerBase, ypos, offset);
    
    tjc's avatar
    tjc committed
      }
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
      
    
    tjc's avatar
    tjc committed
      private void drawScale(Graphics2D g2, int start, int end, float pixPerBase, int ypos)
    
    tjc's avatar
    tjc committed
      {
        g2.setColor(Color.black);
    
    tjc's avatar
    tjc committed
        g2.drawLine( 0, ypos-14,
                     (int)((end - getBaseAtStartOfView())*pixPerBase),   ypos-14);
    
    tjc's avatar
    tjc committed
        int interval = end-start;
        
        if(interval > 256000)
    
    tjc's avatar
    tjc committed
          drawTicks(g2, start, end, pixPerBase, 512000, ypos);
    
    tjc's avatar
    tjc committed
        else if(interval > 64000)
    
    tjc's avatar
    tjc committed
          drawTicks(g2, start, end, pixPerBase, 12800, ypos);
    
    tjc's avatar
    tjc committed
        else if(interval > 16000)
    
    tjc's avatar
    tjc committed
          drawTicks(g2, start, end, pixPerBase, 3200, ypos);
    
    tjc's avatar
    tjc committed
        else if(interval > 4000)
    
    tjc's avatar
    tjc committed
          drawTicks(g2, start, end, pixPerBase, 800, ypos);
    
    tjc's avatar
    tjc committed
        else if(interval > 1000)
    
    tjc's avatar
    tjc committed
          drawTicks(g2, start, end, pixPerBase, 400, ypos);
    
    tjc's avatar
    tjc committed
        else
    
    tjc's avatar
    tjc committed
          drawTicks(g2, start, end, pixPerBase, 100, ypos);
    
    tjc's avatar
    tjc committed
      }
      
    
    tjc's avatar
    tjc committed
      private void drawTicks(Graphics2D g2, int start, int end, float pixPerBase, int division, int ypos)
    
    tjc's avatar
    tjc committed
      {
    
    tjc's avatar
    tjc committed
        int markStart = (Math.round(start/division)*division);
        
        if(markStart < 1)
          markStart = 1;
        
    
    tjc's avatar
    tjc committed
        int sm = markStart-(division/2);
    
    tjc's avatar
    tjc committed
        float x;
    
    tjc's avatar
    tjc committed
        if(sm > start)
    
    tjc's avatar
    tjc committed
        {
          x = (sm-getBaseAtStartOfView())*pixPerBase;
    
    tjc's avatar
    tjc committed
          g2.drawLine((int)x, ypos-14,(int)x, ypos-12);
    
    tjc's avatar
    tjc committed
        }
    
    tjc's avatar
    tjc committed
        
        for(int m=markStart; m<end; m+=division)
        {
    
    tjc's avatar
    tjc committed
          x = (m-getBaseAtStartOfView())*pixPerBase;
    
    tjc's avatar
    tjc committed
          g2.drawString(Integer.toString(m), x, ypos-1);
          g2.drawLine((int)x, ypos-14,(int)x, ypos-11);
    
    tjc's avatar
    tjc committed
          
          sm = m+(division/2);
          
          if(sm < end)
    
    tjc's avatar
    tjc committed
          {
            x = (sm-getBaseAtStartOfView())*pixPerBase;
    
    tjc's avatar
    tjc committed
            g2.drawLine((int)x, ypos-14,(int)x, ypos-12);
    
    tjc's avatar
    tjc committed
          }
    
    tjc's avatar
    tjc committed
          
          if(m == 1)
            m = 0;
    
      /**
       * Draw a y-scale for inferred size (isize) of reads.
       * @param g2
       * @param xScaleHeight
       */
    
    tjc's avatar
    tjc committed
      private void drawYScale(Graphics2D g2, int xScaleHeight)
    
      {
        g2.setColor(Color.black);
    
        int maxY = getPreferredSize().height-xScaleHeight;
    
        
        for(int i=100; i<maxY; i+=100)
        {
    
          int ypos = getHeight()-i-xScaleHeight;
    
    tjc's avatar
    tjc committed
          g2.drawLine(0, ypos, 2, ypos);
          g2.drawString(Integer.toString(i), 3, ypos);
    
      /**
       * Draw a given read.
       * @param g2
       * @param thisRead
       * @param pixPerBase
       * @param ypos
    
       * @param baseAtStartOfView
    
    tjc's avatar
    tjc committed
      private void drawRead(Graphics2D g2, SAMRecord thisRead,
    
    		                float pixPerBase,
    		                int ypos,
    		                int baseAtStartOfView)
    
    tjc's avatar
    tjc committed
      {
    
    tjc's avatar
    tjc committed
        int offset = getSequenceOffset(thisRead.getReferenceName());
    
    
        int thisStart = thisRead.getAlignmentStart()+offset-baseAtStartOfView;
        int thisEnd   = thisRead.getAlignmentEnd()+offset-baseAtStartOfView;
    
        
        if(highlightSAMRecord != null && 
           highlightSAMRecord.getReadName().equals(thisRead.getReadName()))
         {
    
           Stroke originalStroke = g2.getStroke();
           Stroke stroke =
    
             new BasicStroke (3.f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
    
           g2.setStroke(stroke);
    
           Color c = g2.getColor();
           g2.setColor(Color.black);
           g2.drawLine((int)( thisStart * pixPerBase), ypos,
                       (int)( thisEnd * pixPerBase), ypos);
           g2.setColor(c);
    
           g2.setStroke(originalStroke);
    
    tjc's avatar
    tjc committed
        g2.drawLine((int)( thisStart * pixPerBase), ypos,
                    (int)( thisEnd * pixPerBase), ypos);
    
    tjc's avatar
    tjc committed
    
    
        // test if the mouse is over this read
    
        if(lastMousePoint != null)
        {
          if(lastMousePoint.getY()+2 > ypos && lastMousePoint.getY()-2 < ypos)
          if(lastMousePoint.getX() > thisStart * pixPerBase &&
             lastMousePoint.getX() < thisEnd * pixPerBase)
          {
            mouseOverSAMRecord = thisRead;
          }
        }
        
    
    tjc's avatar
    tjc committed
        if (isSNPs)
    
    tjc's avatar
    tjc committed
          showSNPsOnReads(g2, thisRead, pixPerBase, ypos, offset);
    
    tjc's avatar
    tjc committed
      }
      
    
      /**
       * Highlight a selected range
       * @param g2
       * @param pixPerBase
       * @param start
       * @param end
       */
      private void drawSelectionRange(Graphics2D g2, float pixPerBase, int start, int end)
      {
        if(getSelection() != null)
        {
          Range selectedRange = getSelection().getSelectionRange();
    
          if(selectedRange != null)
          {
            int rangeStart = selectedRange.getStart();
            int rangeEnd   = selectedRange.getEnd();
            
            if(end < rangeStart || start > rangeEnd)
              return;
            
    
    tjc's avatar
    tjc committed
            int x = (int) (pixPerBase*(rangeStart-getBaseAtStartOfView()));
    
            int width = (int) (pixPerBase*(rangeEnd-rangeStart+1));
            
            g2.setColor(Color.pink);
            g2.fillRect(x, 0, width, getHeight());
          }
        }
      }
      
    
      /**
       * Draw a translucent line
       * @param g2
       * @param start
       * @param end
       * @param ypos
       */
      private void drawTranslucentLine(Graphics2D g2, int start, int end, int ypos)
      {
        Composite origComposite = g2.getComposite();
        g2.setComposite(translucent);
        g2.drawLine(start, ypos, end, ypos);
        g2.setComposite(origComposite);
      }
      
      /**
       * Draw a translucent line
       * @param g2
       * @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)
        {
          e.printStackTrace();
    
    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
    
       * @param autohide automatically hide the top panel containing the buttons
    
       */
      public void addJamToPanel(final JPanel mainPanel,
    
    tjc's avatar
    tjc committed
                                final JFrame frame,
    
                                final boolean autohide,
                                final FeatureDisplay feature_display)
    
    tjc's avatar
    tjc committed
      {
    
        this.mainPanel = mainPanel;
    
    tjc's avatar
    tjc committed
        final JComponent topPanel;
    
        if(feature_display != null)
        {
          this.feature_display = feature_display;
          this.selection = feature_display.getSelection();
    
    tjc's avatar
    tjc committed
          topPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
        }
        else
        { 
          topPanel = new JMenuBar();
          frame.setJMenuBar((JMenuBar)topPanel);
    
    tjc's avatar
    tjc committed
        
        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
    
        final JCheckBox buttonAutoHide = new JCheckBox("Hide", autohide);
        buttonAutoHide.setToolTipText("Auto-Hide");
    
        final MouseMotionListener mouseMotionListener = new MouseMotionListener()
    
    tjc's avatar
    tjc committed
        {
    
          public void mouseDragged(MouseEvent event)
    
    tjc's avatar
    tjc committed
          {
    
    tjc's avatar
    tjc committed
            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)
    
    tjc's avatar
    tjc committed
              topPanel.setVisible(true);
    
    tjc's avatar
    tjc committed
              if (buttonAutoHide.isSelected())
                topPanel.setVisible(false);
    
            }
            mainPanel.repaint();
            mainPanel.revalidate();
          }
        };
        addMouseMotionListener(mouseMotionListener);
    
        combo = new JComboBox(seqNames);
        combo.setEditable(false);
        combo.addItemListener(new ItemListener()
        {
          public void itemStateChanged(ItemEvent e)
          {
            laststart = -1;
            lastend = -1;
    
            setZoomLevel(BamView.this.nbasesInView);
    
    tjc's avatar
    tjc committed
        topPanel.add(combo);
    
        if(feature_display == null)
    
    tjc's avatar
    tjc committed
        {
    
    tjc's avatar
    tjc committed
          final JTextField baseText = new JTextField(8);
    
          JButton goTo = new JButton("GoTo:");
    
    tjc's avatar
    tjc committed
          goTo.setToolTipText("Go to base position");
    
          goTo.addActionListener(new ActionListener()
    
    tjc's avatar
    tjc committed
          {
    
            public void actionPerformed(ActionEvent e)
    
    tjc's avatar
    tjc committed
            {
    
              try
              {
                int basePosition = Integer.parseInt(baseText.getText());
    
    tjc's avatar
    tjc committed
                scrollBar.setValue(basePosition);
    
              }
              catch (NumberFormatException nfe)
              {
    
                JOptionPane.showMessageDialog(BamView.this,
    
                    "Expecting a base number!", "Number Format",
                    JOptionPane.WARNING_MESSAGE);
              }
    
    tjc's avatar
    tjc committed
            }
    
    tjc's avatar
    tjc committed
          topPanel.add(goTo);
          topPanel.add(baseText);
    
          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) (BamView.this.nbasesInView * 1.1));
    
    tjc's avatar
    tjc committed
          topPanel.add(zoomIn);
    
          JButton zoomOut = new JButton("+");
          zoomOut.setMargin(ins);
    
          zoomOut.addActionListener(new ActionListener()
    
    tjc's avatar
    tjc committed
          {
    
            public void actionPerformed(ActionEvent e)
            {
              if (showBaseAlignment)
                return;
    
              setZoomLevel((int) (BamView.this.nbasesInView * .9));
    
    tjc's avatar
    tjc committed
          topPanel.add(zoomOut);
    
    tjc's avatar
    tjc committed
        topPanel.add(buttonAutoHide);
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        mainPanel.setPreferredSize(new Dimension(900, 400));
    
    tjc's avatar
    tjc committed
        
        jspView = new JScrollPane(this, 
            JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
    
    tjc's avatar
    tjc committed
            JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
    
        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);
    
        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();
            }
          });
          bottomPanel.add(scrollBar, BorderLayout.SOUTH);
        }
    
        mainPanel.add(bottomPanel, BorderLayout.SOUTH);
    
        coveragePanel.setPreferredSize(new Dimension(900, 100));
        coveragePanel.setVisible(false);
        
    
    tjc's avatar
    tjc committed
        jspView.getVerticalScrollBar().setValue(
            jspView.getVerticalScrollBar().getMaximum());
    
    tjc's avatar
    tjc committed
        jspView.getVerticalScrollBar().setUnitIncrement(maxUnitIncrement);
        
    
    tjc's avatar
    tjc committed
        if(feature_display == null)
    
    tjc's avatar
    tjc committed
        {
    
          addKeyListener(new KeyAdapter()
    
    tjc's avatar
    tjc committed
          {
    
            public void keyPressed(final KeyEvent event)
    
    tjc's avatar
    tjc committed
            {
    
              switch (event.getKeyCode())
              {
    
    tjc's avatar
    tjc committed
              case KeyEvent.VK_UP:
    
                setZoomLevel((int) (BamView.this.nbasesInView * 1.1));
    
    tjc's avatar
    tjc committed
                break;
              case KeyEvent.VK_DOWN:
    
                if (showBaseAlignment)
                  break;
    
                setZoomLevel((int) (BamView.this.nbasesInView * .9));
    
    tjc's avatar
    tjc committed
                break;
              default:
                break;
    
    tjc's avatar
    tjc committed
            }
    
        addMouseListener(new PopupListener());
    
    tjc's avatar
    tjc committed
        setFocusable(true);
        requestFocusInWindow();
    
        addFocusListener(new FocusListener()
    
    tjc's avatar
    tjc committed
        {
    
          public void focusGained(FocusEvent fe){}
          public void focusLost(FocusEvent fe){}
    
    tjc's avatar
    tjc committed
        });
    
    tjc's avatar
    tjc committed
    
    
      private void createMenus(JComponent view)
    
        JMenu showMenu = new JMenu("Show");
    
    tjc's avatar
    tjc committed
        JCheckBoxMenuItem checkBoxSingle = new JCheckBoxMenuItem("Single Reads");
        checkBoxSingle.addActionListener(new ActionListener()
    
    tjc's avatar
    tjc committed
          public void actionPerformed(ActionEvent e)
          {
            repaint();
            isSingle = !isSingle;
          }
        });
    
        showMenu.add(checkBoxSingle);
    
    tjc's avatar
    tjc committed
    
        JCheckBoxMenuItem checkBoxSNPs = new JCheckBoxMenuItem("SNPs");
        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;
            repaint();
          }
        });
    
        showMenu.add(checkBoxSNPs);
        
        JCheckBoxMenuItem checkBoxCoverage = new JCheckBoxMenuItem("Coverage");
        checkBoxCoverage.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent e)
          {
            isCoverage = !isCoverage;
            coveragePanel.setVisible(isCoverage);
            repaint();
          }
        });
        showMenu.add(checkBoxCoverage);
        view.add(showMenu);
    
        
        JMenu viewMenu = new JMenu("Views");
    
    tjc's avatar
    tjc committed
        ButtonGroup group = new ButtonGroup();
    
        final JCheckBoxMenuItem checkBoxPairedStackView = new JCheckBoxMenuItem("Paired Stack View");
    
    tjc's avatar
    tjc committed
        final JCheckBoxMenuItem checkBoxStrandStackView = new JCheckBoxMenuItem("Strand Stack View");
    
    tjc's avatar
    tjc committed
        final JCheckBoxMenuItem checkIsizeStackView = new JCheckBoxMenuItem("Inferred Size View", true);
    
        checkBoxStackView.setFont(checkIsizeStackView.getFont());
    
    tjc's avatar
    tjc committed
        group.add(checkBoxStackView);
        group.add(checkBoxPairedStackView);
        group.add(checkBoxStrandStackView);
        group.add(checkIsizeStackView);
        
        checkIsizeStackView.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent e)
          {
            laststart = -1;
            lastend = -1;
            
            if(checkIsizeStackView.isSelected())
            {
              isStackView = false;
              isPairedStackView = false;
              isStrandStackView = false;
            }
            repaint();
          }
        });
    
        viewMenu.add(checkIsizeStackView);
    
    tjc's avatar
    tjc committed
        checkBoxStackView.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent e)
          {
            laststart = -1;
            lastend = -1;
            isStackView = !isStackView;
    
            
            if(isStackView)
            {
              isPairedStackView = !isStackView;
    
    tjc's avatar
    tjc committed
              isStrandStackView = !isStackView;
    
              checkBoxPairedStackView.setSelected(!isStackView);
    
    tjc's avatar
    tjc committed
              checkBoxStrandStackView.setSelected(!isStackView);
    
    tjc's avatar
    tjc committed
            repaint();
          }
        });
    
        viewMenu.add(checkBoxStackView);
    
    
        checkBoxPairedStackView.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent e)
          {
            laststart = -1;
            lastend = -1;
            isPairedStackView = !isPairedStackView;
            
            if(isPairedStackView)
            {
              isStackView = !isPairedStackView;
    
    tjc's avatar
    tjc committed
              isStrandStackView = !isPairedStackView;
    
              checkBoxStackView.setSelected(!isPairedStackView);
    
    tjc's avatar
    tjc committed
              checkBoxStrandStackView.setSelected(!isPairedStackView);
    
        viewMenu.add(checkBoxPairedStackView);
    
    tjc's avatar
    tjc committed
        checkBoxStrandStackView.addActionListener(new ActionListener()
        {
          public void actionPerformed(ActionEvent e)
          {
            laststart = -1;
            lastend = -1;
            isStrandStackView = !isStrandStackView;
            
            if(isStrandStackView)
            {
              isStackView = !isStrandStackView;
              isPairedStackView = !isStrandStackView;
              checkBoxStackView.setSelected(!isStrandStackView);
              checkBoxPairedStackView.setSelected(!isStrandStackView);
              setViewportMidPoint();
            }
            repaint();
          }
        });
    
        viewMenu.add(checkBoxStrandStackView);
        view.add(viewMenu);
     
    
        if(feature_display != null)
    
          final JCheckBoxMenuItem checkBoxSync =
            new JCheckBoxMenuItem("Asynchronous", asynchronous);
          checkBoxSync.addActionListener(new ActionListener()
    
            public void actionPerformed(ActionEvent e)
            {
              asynchronous = checkBoxSync.isSelected();
            }
          });
          view.add(checkBoxSync);
        }
        
    
        view.add(new JSeparator());
        
        JMenu maxHeightMenu = new JMenu("Plot Height");
        view.add(maxHeightMenu);
        
        final String hgts[] = 
           {"500", "800", "1000", "1500", "2500", "5000"};
        
        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);
            }
          });
        }
    
      }
      
      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
      private int getBaseAtStartOfView()
      {
    
    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(pixPerBase*1.08f >= ALIGNMENT_PIX_PER_BASE)
    
    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;
        }
        else if(jspView != null)
        {
          jspView.setColumnHeaderView(null);
    
    tjc's avatar
    tjc committed
          
          if(!isStrandStackView)
            jspView.getVerticalScrollBar().setValue(
                jspView.getVerticalScrollBar().getMaximum());
    
    tjc's avatar
    tjc committed
          showBaseAlignment = false;
        }
        
    
    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);
        }
    
    
        if(pixPerBase*1.08f >= ALIGNMENT_PIX_PER_BASE)
    
    tjc's avatar
    tjc committed
        {
    
    tjc's avatar
    tjc committed
          pixPerBase = ALIGNMENT_PIX_PER_BASE;
    
    tjc's avatar
    tjc committed
          jspView.getVerticalScrollBar().setValue(0);
    
    tjc's avatar
    tjc committed
          jspView.setColumnHeaderView(ruler);
    
          showBaseAlignment = true;