Merge branch 'bug/JAL-2784' into develop
authorJim Procter <jprocter@issues.jalview.org>
Fri, 17 Nov 2017 13:16:10 +0000 (13:16 +0000)
committerJim Procter <jprocter@issues.jalview.org>
Fri, 17 Nov 2017 13:16:10 +0000 (13:16 +0000)
1  2 
src/jalview/gui/SeqCanvas.java

@@@ -296,48 -296,47 +296,48 @@@ public class SeqCanvas extends JCompone
        int transX = 0;
        int transY = 0;
  
 -    gg.copyArea(horizontal * charWidth, vertical * charHeight,
 -            img.getWidth(), img.getHeight(), -horizontal * charWidth,
 -            -vertical * charHeight);
 +      gg.copyArea(horizontal * charWidth, vertical * charHeight,
 +              img.getWidth(), img.getHeight(), -horizontal * charWidth,
 +              -vertical * charHeight);
  
 -    if (horizontal > 0) // scrollbar pulled right, image to the left
 -    {
 -      transX = (endRes - startRes - horizontal) * charWidth;
 -      startRes = endRes - horizontal;
 -    }
 -    else if (horizontal < 0)
 -    {
 -      endRes = startRes - horizontal;
 -    }
 -    else if (vertical > 0) // scroll down
 -    {
 -      startSeq = endSeq - vertical;
 -
 -      if (startSeq < ranges.getStartSeq())
 -      { // ie scrolling too fast, more than a page at a time
 -        startSeq = ranges.getStartSeq();
 +      if (horizontal > 0) // scrollbar pulled right, image to the left
 +      {
 +        transX = (endRes - startRes - horizontal) * charWidth;
 +        startRes = endRes - horizontal;
        }
 -      else
 +      else if (horizontal < 0)
        {
 -        transY = img.getHeight() - ((vertical + 1) * charHeight);
 +        endRes = startRes - horizontal;
        }
 -    }
 -    else if (vertical < 0)
 -    {
 -      endSeq = startSeq - vertical;
  
 -      if (endSeq > ranges.getEndSeq())
 +      if (vertical > 0) // scroll down
        {
 -        endSeq = ranges.getEndSeq();
 +        startSeq = endSeq - vertical;
 +
 +        if (startSeq < ranges.getStartSeq())
 +        { // ie scrolling too fast, more than a page at a time
 +          startSeq = ranges.getStartSeq();
 +        }
 +        else
 +        {
 +          transY = img.getHeight() - ((vertical + 1) * charHeight);
 +        }
        }
 -    }
 +      else if (vertical < 0)
 +      {
 +        endSeq = startSeq - vertical;
  
 -    gg.translate(transX, transY);
 -    drawPanel(gg, startRes, endRes, startSeq, endSeq, 0);
 -    gg.translate(-transX, -transY);
 +        if (endSeq > ranges.getEndSeq())
 +        {
 +          endSeq = ranges.getEndSeq();
 +        }
 +      }
  
 -    repaint();
 +      gg.translate(transX, transY);
 +      drawPanel(gg, startRes, endRes, startSeq, endSeq, 0);
 +      gg.translate(-transX, -transY);
 +
 +      repaint();
      } finally
      {
        fastpainting = false;
        // lcimg is a local *copy* of img which we'll draw selectImage on top of
        BufferedImage lcimg = buildLocalImage(selectImage);
        g.drawImage(lcimg, 0, 0, this);
 +
 +    }
 +
 +    if (av.cursorMode)
 +    {
 +      drawCursor(g, ranges.getStartRes(), ranges.getEndRes(),
 +              ranges.getStartSeq(), ranges.getEndSeq());
      }
    }
    
                AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
        g2d.drawImage(selectImage, 0, 0, this);
      }
 +
      g2d.dispose();
  
      return lcimg;
       * white fill the region to be drawn (so incremental fast paint doesn't
       * scribble over an existing image)
       */
-     gg.setColor(Color.white);
-     gg.fillRect(0, ypos, (endx - startColumn + 1) * charWidth,
+     g.setColor(Color.white);
+     g.fillRect(0, ypos, (endx - startColumn + 1) * charWidth,
              wrappedRepeatHeightPx);
  
      drawPanel(g, startColumn, endx, 0, av.getAlignment().getHeight() - 1,
            int canvasWidth,
            int canvasHeight, int startRes)
    {
 -      int charHeight = av.getCharHeight();
 -      int charWidth = av.getCharWidth();
 -        
 +    int charHeight = av.getCharHeight();
 +    int charWidth = av.getCharWidth();
 +      
      // height gap above each panel
      int hgap = charHeight;
      if (av.getScaleAboveWrapped())
            }
          }
        }
 -
 -      if (av.cursorMode && cursorY == i && cursorX >= startRes
 -              && cursorX <= endRes)
 -      {
 -        seqRdr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth,
 -                offset + ((i - startSeq) * charHeight));
 -      }
      }
  
      if (av.getSelectionGroup() != null
      return selectionImage;
    }
  
 +  /**
 +   * Draw the cursor as a separate image and overlay
 +   * 
 +   * @param startRes
 +   *          start residue of area to draw cursor in
 +   * @param endRes
 +   *          end residue of area to draw cursor in
 +   * @param startSeq
 +   *          start sequence of area to draw cursor in
 +   * @param endSeq
 +   *          end sequence of are to draw cursor in
 +   * @return a transparent image of the same size as the sequence canvas, with
 +   *         the cursor drawn on it, if any
 +   */
 +  private void drawCursor(Graphics g, int startRes, int endRes,
 +          int startSeq,
 +          int endSeq)
 +  {
 +    // convert the cursorY into a position on the visible alignment
 +    int cursor_ypos = cursorY;
 +
 +    // don't do work unless we have to
 +    if (cursor_ypos >= startSeq && cursor_ypos <= endSeq)
 +    {
 +      int yoffset = 0;
 +      int xoffset = 0;
 +      int startx = startRes;
 +      int endx = endRes;
 +
 +      // convert the cursorX into a position on the visible alignment
 +      int cursor_xpos = av.getAlignment().getHiddenColumns()
 +              .findColumnPosition(cursorX);
 +
 +      if (av.getAlignment().getHiddenColumns().isVisible(cursorX))
 +      {
 +
 +        if (av.getWrapAlignment())
 +        {
 +          // work out the correct offsets for the cursor
 +          int charHeight = av.getCharHeight();
 +          int charWidth = av.getCharWidth();
 +          int canvasWidth = getWidth();
 +          int canvasHeight = getHeight();
 +
 +          // height gap above each panel
 +          int hgap = charHeight;
 +          if (av.getScaleAboveWrapped())
 +          {
 +            hgap += charHeight;
 +          }
 +
 +          int cWidth = (canvasWidth - labelWidthEast - labelWidthWest)
 +                  / charWidth;
 +          int cHeight = av.getAlignment().getHeight() * charHeight;
 +
 +          endx = startx + cWidth - 1;
 +          int ypos = hgap; // vertical offset
 +
 +          // iterate down the wrapped panels
 +          while ((ypos <= canvasHeight) && (endx < cursor_xpos))
 +          {
 +            // update vertical offset
 +            ypos += cHeight + getAnnotationHeight() + hgap;
 +
 +            // update horizontal offset
 +            startx += cWidth;
 +            endx = startx + cWidth - 1;
 +          }
 +          yoffset = ypos;
 +          xoffset = labelWidthWest;
 +        }
 +
 +        // now check if cursor is within range for x values
 +        if (cursor_xpos >= startx && cursor_xpos <= endx)
 +        {
 +          // get the character the cursor is drawn at
 +          SequenceI seq = av.getAlignment().getSequenceAt(cursorY);
 +          char s = seq.getCharAt(cursorX);
 +
 +          seqRdr.drawCursor(g, s,
 +                  xoffset + (cursor_xpos - startx) * av.getCharWidth(),
 +                  yoffset + (cursor_ypos - startSeq) * av.getCharHeight());
 +        }
 +      }
 +    }
 +  }
 +
 +
    /*
     * Set up graphics for selection group
     */
    private void drawUnwrappedSelection(Graphics2D g, SequenceGroup group,
            int startRes, int endRes, int startSeq, int endSeq, int offset)
    {
 -      int charWidth = av.getCharWidth();
 -        
 +    int charWidth = av.getCharWidth();
 +          
      if (!av.hasHiddenColumns())
      {
        drawPartialGroupOutline(g, group, startRes, endRes, startSeq, endSeq,
            int startRes, int endRes, int startSeq, int endSeq,
            int verticalOffset)
    {
 -      int charHeight = av.getCharHeight();
 -      int charWidth = av.getCharWidth();
 -        
 +    int charHeight = av.getCharHeight();
 +    int charWidth = av.getCharWidth();
      int visWidth = (endRes - startRes + 1) * charWidth;
  
      int oldY = -1;
      boolean inGroup = false;
      int top = -1;
      int bottom = -1;
 -
 -    int sx = -1;
      int sy = -1;
 -    int xwidth = -1;
  
 -    for (i = startSeq; i <= endSeq; i++)
 -    {
 -      // position of start residue of group relative to startRes, in pixels
 -      sx = (group.getStartRes() - startRes) * charWidth;
 +    List<SequenceI> seqs = group.getSequences(null);
  
 -      // width of group in pixels
 -      xwidth = (((group.getEndRes() + 1) - group.getStartRes()) * charWidth)
 -              - 1;
 +    // position of start residue of group relative to startRes, in pixels
 +    int sx = (group.getStartRes() - startRes) * charWidth;
  
 -      sy = verticalOffset + (i - startSeq) * charHeight;
 +    // width of group in pixels
 +    int xwidth = (((group.getEndRes() + 1) - group.getStartRes())
 +            * charWidth) - 1;
  
 -      if (sx + xwidth < 0 || sx > visWidth)
 -      {
 -        continue;
 -      }
 -
 -      if ((sx <= (endRes - startRes) * charWidth)
 -              && group.getSequences(null)
 -                      .contains(av.getAlignment().getSequenceAt(i)))
 +    if (!(sx + xwidth < 0 || sx > visWidth))
 +    {
 +      for (i = startSeq; i <= endSeq; i++)
        {
 -        if ((bottom == -1) && !group.getSequences(null)
 -                .contains(av.getAlignment().getSequenceAt(i + 1)))
 -        {
 -          bottom = sy + charHeight;
 -        }
 +        sy = verticalOffset + (i - startSeq) * charHeight;
  
 -        if (!inGroup)
 +        if ((sx <= (endRes - startRes) * charWidth)
 +                && seqs.contains(av.getAlignment().getSequenceAt(i)))
          {
 -          if (((top == -1) && (i == 0)) || !group.getSequences(null)
 -                  .contains(av.getAlignment().getSequenceAt(i - 1)))
 +          if ((bottom == -1)
 +                  && !seqs.contains(av.getAlignment().getSequenceAt(i + 1)))
            {
 -            top = sy;
 +            bottom = sy + charHeight;
            }
  
 -          oldY = sy;
 -          inGroup = true;
 -        }
 -      }
 -      else
 -      {
 -        if (inGroup)
 -        {
 -          // if start position is visible, draw vertical line to left of
 -          // group
 -          if (sx >= 0 && sx < visWidth)
 +          if (!inGroup)
            {
 -            g.drawLine(sx, oldY, sx, sy);
 -          }
 -
 -          // if end position is visible, draw vertical line to right of
 -          // group
 -          if (sx + xwidth < visWidth)
 -          {
 -            g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
 -          }
 -
 -          if (sx < 0)
 -          {
 -            xwidth += sx;
 -            sx = 0;
 -          }
 -
 -          // don't let width extend beyond current block, or group extent
 -          // fixes JAL-2672
 -          if (sx + xwidth >= (endRes - startRes + 1) * charWidth)
 -          {
 -            xwidth = (endRes - startRes + 1) * charWidth - sx;
 -          }
 -          
 -          // draw horizontal line at top of group
 -          if (top != -1)
 -          {
 -            g.drawLine(sx, top, sx + xwidth, top);
 -            top = -1;
 -          }
 -
 -          // draw horizontal line at bottom of group
 -          if (bottom != -1)
 -          {
 -            g.drawLine(sx, bottom, sx + xwidth, bottom);
 -            bottom = -1;
 +            if (((top == -1) && (i == 0)) || !seqs
 +                    .contains(av.getAlignment().getSequenceAt(i - 1)))
 +            {
 +              top = sy;
 +            }
 +
 +            oldY = sy;
 +            inGroup = true;
            }
 +        }
 +        else if (inGroup)
 +        {
 +          drawVerticals(g, sx, xwidth, visWidth, oldY, sy);
 +          drawHorizontals(g, sx, xwidth, visWidth, top, bottom);
  
 +          // reset top and bottom
 +          top = -1;
 +          bottom = -1;
            inGroup = false;
          }
        }
 -    }
 -
 -    if (inGroup)
 -    {
 -      sy = verticalOffset + ((i - startSeq) * charHeight);
 -      if (sx >= 0 && sx < visWidth)
 +      if (inGroup)
        {
 -        g.drawLine(sx, oldY, sx, sy);
 +        sy = verticalOffset + ((i - startSeq) * charHeight);
 +        drawVerticals(g, sx, xwidth, visWidth, oldY, sy);
 +        drawHorizontals(g, sx, xwidth, visWidth, top, bottom);
        }
 +    }
 +  }
  
 -      if (sx + xwidth < visWidth)
 -      {
 -        g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
 -      }
 +  /**
 +   * Draw horizontal selection group boundaries at top and bottom positions
 +   * 
 +   * @param g
 +   *          graphics object to draw on
 +   * @param sx
 +   *          start x position
 +   * @param xwidth
 +   *          width of gap
 +   * @param visWidth
 +   *          visWidth maximum available width
 +   * @param top
 +   *          position to draw top of group at
 +   * @param bottom
 +   *          position to draw bottom of group at
 +   */
 +  private void drawHorizontals(Graphics2D g, int sx, int xwidth,
 +          int visWidth, int top, int bottom)
 +  {
 +    int width = xwidth;
 +    int startx = sx;
 +    if (startx < 0)
 +    {
 +      width += startx;
 +      startx = 0;
 +    }
  
 -      if (sx < 0)
 -      {
 -        xwidth += sx;
 -        sx = 0;
 -      }
 +    // don't let width extend beyond current block, or group extent
 +    // fixes JAL-2672
 +    if (startx + width >= visWidth)
 +    {
 +      width = visWidth - startx;
 +    }
  
 -      if (sx + xwidth > visWidth)
 -      {
 -        xwidth = visWidth;
 -      }
 -      else if (sx + xwidth >= (endRes - startRes + 1) * charWidth)
 -      {
 -        xwidth = (endRes - startRes + 1) * charWidth;
 -      }
 +    if (top != -1)
 +    {
 +      g.drawLine(startx, top, startx + width, top);
 +    }
  
 -      if (top != -1)
 -      {
 -        g.drawLine(sx, top, sx + xwidth, top);
 -        top = -1;
 -      }
 +    if (bottom != -1)
 +    {
 +      g.drawLine(startx, bottom - 1, startx + width, bottom - 1);
 +    }
 +  }
  
 -      if (bottom != -1)
 -      {
 -        g.drawLine(sx, bottom - 1, sx + xwidth, bottom - 1);
 -        bottom = -1;
 -      }
 +  /**
 +   * Draw vertical lines at sx and sx+xwidth providing they lie within
 +   * [0,visWidth)
 +   * 
 +   * @param g
 +   *          graphics object to draw on
 +   * @param sx
 +   *          start x position
 +   * @param xwidth
 +   *          width of gap
 +   * @param visWidth
 +   *          visWidth maximum available width
 +   * @param oldY
 +   *          top y value
 +   * @param sy
 +   *          bottom y value
 +   */
 +  private void drawVerticals(Graphics2D g, int sx, int xwidth, int visWidth,
 +          int oldY, int sy)
 +  {
 +    // if start position is visible, draw vertical line to left of
 +    // group
 +    if (sx >= 0 && sx < visWidth)
 +    {
 +      g.drawLine(sx, oldY, sx, sy);
 +    }
  
 -      inGroup = false;
 +    // if end position is visible, draw vertical line to right of
 +    // group
 +    if (sx + xwidth < visWidth)
 +    {
 +      g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
      }
    }
    
     */
    protected boolean drawMappedPositions(SearchResultsI results)
    {
-     if (results == null)
+     if ((results == null) || (gg == null)) // JAL-2784 check gg is not null
      {
        return false;
      }
        repaint();
        return;
      }
 +    else if (eventName.equals(ViewportRanges.MOVE_VIEWPORT))
 +    {
 +      fastPaint = false;
 +      repaint();
 +      return;
 +    }
  
      int scrollX = 0;
 -    if (eventName.equals(ViewportRanges.STARTRES))
 +    if (eventName.equals(ViewportRanges.STARTRES)
 +            || eventName.equals(ViewportRanges.STARTRESANDSEQ))
      {
        // Make sure we're not trying to draw a panel
        // larger than the visible window
 +      if (eventName.equals(ViewportRanges.STARTRES))
 +      {
 +        scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
 +      }
 +      else
 +      {
 +        scrollX = ((int[]) evt.getNewValue())[0]
 +                - ((int[]) evt.getOldValue())[0];
 +      }
        ViewportRanges vpRanges = av.getRanges();
 -      scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
 -      int range = vpRanges.getViewportWidth();
 +
 +      int range = vpRanges.getEndRes() - vpRanges.getStartRes();
        if (scrollX > range)
        {
          scrollX = range;
        {
          scrollX = -range;
        }
 -    }
 -
 -    // Both scrolling and resizing change viewport ranges: scrolling changes
 -    // both start and end points, but resize only changes end values.
 -    // Here we only want to fastpaint on a scroll, with resize using a normal
 -    // paint, so scroll events are identified as changes to the horizontal or
 -    // vertical start value.
  
 -    // scroll - startres and endres both change
 -    if (eventName.equals(ViewportRanges.STARTRES))
 -    {
 -      if (av.getWrapAlignment())
 +      // Both scrolling and resizing change viewport ranges: scrolling changes
 +      // both start and end points, but resize only changes end values.
 +      // Here we only want to fastpaint on a scroll, with resize using a normal
 +      // paint, so scroll events are identified as changes to the horizontal or
 +      // vertical start value.
 +      if (eventName.equals(ViewportRanges.STARTRES))
        {
 -        fastPaintWrapped(scrollX);
 +        if (av.getWrapAlignment())
 +          {
 +            fastPaintWrapped(scrollX);
 +          }
 +          else
 +          {
 +            fastPaint(scrollX, 0);
 +          }
        }
 -      else
 +      else if (eventName.equals(ViewportRanges.STARTSEQ))
        {
 -        fastPaint(scrollX, 0);
 +        // scroll
 +        fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
 +      }
 +      else if (eventName.equals(ViewportRanges.STARTRESANDSEQ))
 +      {
 +        if (av.getWrapAlignment())
 +        {
 +          fastPaintWrapped(scrollX);
 +        }
 +        else
 +        {
 +          fastPaint(scrollX, 0);
 +        }
 +        // bizarrely, we only need to scroll on the x value here as fastpaint
 +        // copies the full height of the image anyway. Passing in the y value
 +        // causes nasty repaint artefacts, which only disappear on a full
 +        // repaint.
        }
 -    }
 -    else if (eventName.equals(ViewportRanges.STARTSEQ))
 -    {
 -      // scroll
 -      fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
      }
    }
  
    {
      ViewportRanges ranges = av.getRanges();
  
 -    if (Math.abs(scrollX) > ranges.getViewportWidth())
 +    // if (Math.abs(scrollX) > ranges.getViewportWidth())
 +    // JAL-2836, 2836 temporarily removed wrapped fastpaint for release 2.10.3
 +    if (true)
      {
        /*
         * shift of more than one view width is 
     */
    protected boolean drawMappedPositionsWrapped(SearchResultsI results)
    {
-     if (results == null)
+     if ((results == null) || (gg == null)) // JAL-2784 check gg is not null
      {
        return false;
      }