Merge branch 'bug/JAL-2778again' into releases/Release_2_10_4_Branch
authorJim Procter <jprocter@issues.jalview.org>
Wed, 21 Feb 2018 17:19:01 +0000 (17:19 +0000)
committerJim Procter <jprocter@issues.jalview.org>
Wed, 21 Feb 2018 17:19:01 +0000 (17:19 +0000)
1  2 
src/jalview/datamodel/Alignment.java
src/jalview/gui/AnnotationPanel.java
src/jalview/gui/Desktop.java
src/jalview/gui/IdCanvas.java
src/jalview/gui/ScalePanel.java
src/jalview/gui/SeqCanvas.java

@@@ -28,7 -28,6 +28,7 @@@ import jalview.util.LinkedIdentityHashS
  import jalview.util.MessageManager;
  
  import java.util.ArrayList;
 +import java.util.Arrays;
  import java.util.Collections;
  import java.util.Enumeration;
  import java.util.HashSet;
@@@ -49,7 -48,7 +49,7 @@@ public class Alignment implements Align
  {
    private Alignment dataset;
  
-   protected List<SequenceI> sequences;
+   private List<SequenceI> sequences;
  
    protected List<SequenceGroup> groups;
  
          return sequences.get(i);
        }
      }
      return null;
    }
  
    public int getWidth()
    {
      int maxLength = -1;
+   
      for (int i = 0; i < sequences.size(); i++)
      {
        if (getSequenceAt(i).getLength() > maxLength)
          maxLength = getSequenceAt(i).getLength();
        }
      }
+   
      return maxLength;
    }
+   /*
+   @Override
+   public int getWidth()
+   {
+     final Wrapper temp = new Wrapper();
+   
+     forEachSequence(new Consumer<SequenceI>()
+     {
+       @Override
+       public void accept(SequenceI s)
+       {
+         if (s.getLength() > temp.inner)
+         {
+           temp.inner = s.getLength();
+         }
+       }
+     }, 0, sequences.size() - 1);
+   
+     return temp.inner;
+   }
+   
+   public static class Wrapper
+   {
+     public int inner;
+   }*/
  
    /**
     * DOCUMENT ME!
      AlignmentAnnotation annot = new AlignmentAnnotation(name, name,
              new Annotation[1], 0f, 0f, AlignmentAnnotation.BAR_GRAPH);
      annot.hasText = false;
 -    annot.setCalcId(new String(calcId));
 +    if (calcId != null)
 +    {
 +      annot.setCalcId(new String(calcId));
 +    }
      annot.autoCalculated = autoCalc;
      if (seqRef != null)
      {
    @Override
    public Iterable<AlignmentAnnotation> findAnnotation(String calcId)
    {
      AlignmentAnnotation[] alignmentAnnotation = getAlignmentAnnotation();
      if (alignmentAnnotation != null)
      {
 -      for (AlignmentAnnotation a : alignmentAnnotation)
 -      {
 -        if (a.getCalcId() == calcId || (a.getCalcId() != null
 -                && calcId != null && a.getCalcId().equals(calcId)))
 -        {
 -          aa.add(a);
 -        }
 -      }
 +      return AlignmentAnnotation.findAnnotation(
 +              Arrays.asList(getAlignmentAnnotation()), calcId);
      }
 -    return aa;
 +    return Arrays.asList(new AlignmentAnnotation[] {});
    }
  
    @Override
    public Iterable<AlignmentAnnotation> findAnnotations(SequenceI seq,
            String calcId, String label)
    {
 -    ArrayList<AlignmentAnnotation> aa = new ArrayList<>();
 -    for (AlignmentAnnotation ann : getAlignmentAnnotation())
 -    {
 -      if ((calcId == null || (ann.getCalcId() != null
 -              && ann.getCalcId().equals(calcId)))
 -              && (seq == null || (ann.sequenceRef != null
 -                      && ann.sequenceRef == seq))
 -              && (label == null
 -                      || (ann.label != null && ann.label.equals(label))))
 -      {
 -        aa.add(ann);
 -      }
 -    }
 -    return aa;
 +    return AlignmentAnnotation.findAnnotations(
 +            Arrays.asList(getAlignmentAnnotation()), seq, calcId, label);
    }
  
    @Override
@@@ -904,6 -904,8 +904,8 @@@ public class AnnotationPanel extends JP
    @Override
    public void paintComponent(Graphics g)
    {
+     super.paintComponent(g);
      g.setColor(Color.white);
      g.fillRect(0, 0, getWidth(), getHeight());
  
        gg.fillRect(0, 0, imgWidth, image.getHeight());
        imageFresh = true;
      }
+     
      drawComponent(gg, av.getRanges().getStartRes(),
              av.getRanges().getEndRes() + 1);
      imageFresh = false;
      int er = av.getRanges().getEndRes() + 1;
      int transX = 0;
  
-     long stime = System.currentTimeMillis();
      gg.copyArea(0, 0, imgWidth, getHeight(),
              -horizontal * av.getCharWidth(), 0);
-     long mtime = System.currentTimeMillis();
  
      if (horizontal > 0) // scrollbar pulled right, image to the left
      {
      drawComponent(gg, sr, er);
  
      gg.translate(-transX, 0);
-     long dtime = System.currentTimeMillis();
      fastPaint = true;
-     repaint();
-     long rtime = System.currentTimeMillis();
-     if (debugRedraw)
-     {
-       System.err.println("Scroll:\t" + horizontal + "\tCopyArea:\t"
-               + (mtime - stime) + "\tDraw component:\t" + (dtime - mtime)
-               + "\tRepaint call:\t" + (rtime - dtime));
-     }
  
+     // Call repaint on alignment panel so that repaints from other alignment
+     // panel components can be aggregated. Otherwise performance of the overview
+     // window and others may be adversely affected.
+     av.getAlignPanel().repaint();
    }
  
    private volatile boolean lastImageGood = false;
        fastPaint(((int[]) evt.getNewValue())[0]
                - ((int[]) evt.getOldValue())[0]);
      }
 +    else if (evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT))
 +    {
 +      repaint();
 +    }
    }
  }
@@@ -849,6 -849,7 +849,7 @@@ public class Desktop extends jalview.jb
      frame.setResizable(resizable);
      frame.setMaximizable(resizable);
      frame.setIconifiable(resizable);
+     frame.setOpaque(false);
  
      if (frame.getX() < 1 && frame.getY() < 1)
      {
            {
              String link = li.next();
              if (link.contains(SEQUENCE_ID)
 -                    && !link.equals(UrlConstants.DEFAULT_STRING))
 +                    && !UrlConstants.isDefaultString(link))
              {
                check = true;
                int barPos = link.indexOf("|");
@@@ -83,7 -83,7 +83,7 @@@ public class IdCanvas extends JPanel im
      this.av = av;
      PaintRefresher.Register(this, av.getSequenceSetId());
      av.getRanges().addPropertyChangeListener(this);
-   }
+     }
  
    /**
     * DOCUMENT ME!
      gg.translate(0, -transY);
  
      fastPaint = true;
-     repaint();
+     // Call repaint on alignment panel so that repaints from other alignment
+     // panel components can be aggregated. Otherwise performance of the overview
+     // window and others may be adversely affected.
+     av.getAlignPanel().repaint();
    }
  
    /**
    @Override
    public void paintComponent(Graphics g)
    {
+     super.paintComponent(g);
      g.setColor(Color.white);
      g.fillRect(0, 0, getWidth(), getHeight());
+     
      if (fastPaint)
      {
        fastPaint = false;
        g.drawImage(image, 0, 0, this);
+     
        return;
      }
+     
      int oldHeight = imgHeight;
+     
      imgHeight = getHeight();
      imgHeight -= (imgHeight % av.getCharHeight());
+     
      if (imgHeight < 1)
      {
        return;
      }
+     
      if (oldHeight != imgHeight || image.getWidth(this) != getWidth())
      {
-       image = new BufferedImage(getWidth(), imgHeight,
-               BufferedImage.TYPE_INT_RGB);
+       image = new BufferedImage(getWidth(), imgHeight,
+                 BufferedImage.TYPE_INT_RGB);
      }
+     
      gg = (Graphics2D) image.getGraphics();
+     
      // Fill in the background
      gg.setColor(Color.white);
      gg.fillRect(0, 0, getWidth(), imgHeight);
+     
      drawIds(av.getRanges().getStartSeq(), av.getRanges().getEndSeq());
+     
      g.drawImage(image, 0, 0, this);
    }
  
        fastPaint(((int[]) evt.getNewValue())[1]
                - ((int[]) evt.getOldValue())[1]);
      }
 +    else if (propertyName.equals(ViewportRanges.MOVE_VIEWPORT))
 +    {
 +      repaint();
 +    }
    }
  }
@@@ -409,6 -409,8 +409,8 @@@ public class ScalePanel extends JPane
    @Override
    public void paintComponent(Graphics g)
    {
+     super.paintComponent(g);
      /*
       * shouldn't get called in wrapped mode as the scale above is
       * drawn instead by SeqCanvas.drawNorthScale
      // paint, so scroll events are identified as changes to the horizontal or
      // vertical start value.
      if (evt.getPropertyName().equals(ViewportRanges.STARTRES)
 -            || evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ))
 +            || evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ)
 +            || evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT))
      {
        // scroll event, repaint panel
-       repaint();
+       
+       // Call repaint on alignment panel so that repaints from other alignment
+     // panel components can be aggregated. Otherwise performance of the overview
+     // window and others may be adversely affected.
+       av.getAlignPanel().repaint();
      }
    }
  
@@@ -295,7 -295,7 +295,7 @@@ public class SeqCanvas extends JCompone
        int endSeq = ranges.getEndSeq();
        int transX = 0;
        int transY = 0;
+       
        gg.copyArea(horizontal * charWidth, vertical * charHeight,
                img.getWidth(), img.getHeight(), -horizontal * charWidth,
                -vertical * charHeight);
        drawPanel(gg, startRes, endRes, startSeq, endSeq, 0);
        gg.translate(-transX, -transY);
  
-       repaint();
+       // Call repaint on alignment panel so that repaints from other alignment
+       // panel components can be aggregated. Otherwise performance of the
+       // overview window and others may be adversely affected.
+       av.getAlignPanel().repaint();
      } finally
      {
        fastpainting = false;
      
      int charHeight = av.getCharHeight();
      int charWidth = av.getCharWidth();
+     
      ViewportRanges ranges = av.getRanges();
+     
      int width = getWidth();
      int height = getHeight();
+     
      width -= (width % charWidth);
      height -= (height % charHeight);
+     
      // selectImage is the selection group outline image
      BufferedImage selectImage = drawSelectionGroup(
              ranges.getStartRes(), ranges.getEndRes(),
              ranges.getStartSeq(), ranges.getEndSeq());
+     
      if ((img != null) && (fastPaint
              || (getVisibleRect().width != g.getClipBounds().width)
              || (getVisibleRect().height != g.getClipBounds().height)))
          gg = (Graphics2D) img.getGraphics();
          gg.setFont(av.getFont());
        }
+     
        if (av.antiAlias)
        {
          gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                  RenderingHints.VALUE_ANTIALIAS_ON);
        }
+     
        gg.setColor(Color.white);
        gg.fillRect(0, 0, img.getWidth(), img.getHeight());
+     
        if (av.getWrapAlignment())
        {
          drawWrappedPanel(gg, getWidth(), getHeight(), ranges.getStartRes());
          drawPanel(gg, ranges.getStartRes(), ranges.getEndRes(),
                  ranges.getStartSeq(), ranges.getEndSeq(), 0);
        }
+     
        // 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());
      }
    }
    
    private BufferedImage buildLocalImage(BufferedImage selectImage)
    {
      // clone the cached image
-     BufferedImage lcimg = new BufferedImage(img.getWidth(), img.getHeight(),
-             img.getType());
+         BufferedImage lcimg = new BufferedImage(img.getWidth(), img.getHeight(),
+                   img.getType());
+     // BufferedImage lcimg = new BufferedImage(img.getWidth(), img.getHeight(),
+     // img.getType());
      Graphics2D g2d = lcimg.createGraphics();
      g2d.drawImage(img, 0, 0, null);
  
                AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
        g2d.drawImage(selectImage, 0, 0, this);
      }
 +
      g2d.dispose();
  
      return lcimg;
  
      try
      {
-       lcimg = new BufferedImage(width, height,
-               BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works
+       lcimg = new BufferedImage(width, height,
+                 BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works
      } catch (OutOfMemoryError er)
      {
        System.gc();
       * 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,
        if (av.hasSearchResults())
        {
          SearchResultsI searchResults = av.getSearchResults();
-         int[] visibleResults = searchResults.getResults(nextSeq,
-                 startRes, endRes);
+         int[] visibleResults = searchResults.getResults(nextSeq, startRes,
+                 endRes);
          if (visibleResults != null)
          {
            for (int r = 0; r < visibleResults.length; r += 2)
            {
              seqRdr.drawHighlightedText(nextSeq, visibleResults[r],
-                     visibleResults[r + 1], (visibleResults[r] - startRes)
-                             * charWidth, offset
-                             + ((i - startSeq) * charHeight));
+                     visibleResults[r + 1],
+                     (visibleResults[r] - startRes) * charWidth,
+                     offset + ((i - startSeq) * charHeight));
            }
          }
        }
 -
 -      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
     */
            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)
 +    if (!(sx + xwidth < 0 || sx > visWidth))
 +    {
 +      for (i = startSeq; i <= endSeq; i++)
        {
 -        continue;
 -      }
 +        sy = verticalOffset + (i - startSeq) * charHeight;
  
 -      if ((sx <= (endRes - startRes) * charWidth)
 -              && group.getSequences(null)
 -                      .contains(av.getAlignment().getSequenceAt(i)))
 -      {
 -        if ((bottom == -1) && !group.getSequences(null)
 -                .contains(av.getAlignment().getSequenceAt(i + 1)))
 +        if ((sx <= (endRes - startRes) * charWidth)
 +                && seqs.contains(av.getAlignment().getSequenceAt(i)))
          {
 -          bottom = sy + charHeight;
 -        }
 -
 -        if (!inGroup)
 -        {
 -          if (((top == -1) && (i == 0)) || !group.getSequences(null)
 -                  .contains(av.getAlignment().getSequenceAt(i - 1)))
 -          {
 -            top = sy;
 -          }
 -
 -          oldY = sy;
 -          inGroup = true;
 -        }
 -      }
 -      else
 -      {
 -        if (inGroup)
 -        {
 -          // if start position is visible, draw vertical line to left of
 -          // group
 -          if (sx >= 0 && sx < visWidth)
 -          {
 -            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)
 +          if ((bottom == -1)
 +                  && !seqs.contains(av.getAlignment().getSequenceAt(i + 1)))
            {
 -            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;
 +            bottom = sy + charHeight;
            }
  
 -          // draw horizontal line at bottom of group
 -          if (bottom != -1)
 +          if (!inGroup)
            {
 -            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)
          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
      if (eventName.equals(ViewportRanges.STARTRES))
      {
        if (av.getWrapAlignment())
 -      {
 -        fastPaintWrapped(scrollX);
 -      }
 -      else
 -      {
 -        fastPaint(scrollX, 0);
 -      }
 +        {
 +          fastPaintWrapped(scrollX);
 +        }
 +        else
 +        {
 +          fastPaint(scrollX, 0);
 +        }
      }
      else if (eventName.equals(ViewportRanges.STARTSEQ))
      {
      }
      else if (eventName.equals(ViewportRanges.STARTRESANDSEQ))
      {
 -      fastPaint(scrollX, 0);
 +      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
    {
      ViewportRanges ranges = av.getRanges();
  
-     // if (Math.abs(scrollX) > ranges.getViewportWidth())
-     // JAL-2836, 2836 temporarily removed wrapped fastpaint for release 2.10.3
-     if (true)
+     if (Math.abs(scrollX) > ranges.getViewportWidth())
      {
        /*
         * 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;
      }