JAL-2665 Changes for review comments; printing with selgroup working
[jalview.git] / src / jalview / gui / SeqCanvas.java
index 3626383..955a9e6 100755 (executable)
@@ -55,16 +55,12 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
 {
   final FeatureRenderer fr;
 
-  final SequenceRenderer sr;
+  final SequenceRenderer seqRdr;
 
   BufferedImage img;
 
   Graphics2D gg;
 
-  int imgWidth;
-
-  int imgHeight;
-
   AlignViewport av;
 
   boolean fastPaint = false;
@@ -77,6 +73,14 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
 
   int cursorY = 0;
 
+  int charHeight = 0;
+
+  int charWidth = 0;
+
+  boolean fastpainting = false;
+
+  AnnotationPanel annotations;
+
   /**
    * Creates a new SeqCanvas object.
    * 
@@ -88,7 +92,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     this.av = ap.av;
     updateViewport();
     fr = new FeatureRenderer(ap);
-    sr = new SequenceRenderer(av);
+    seqRdr = new SequenceRenderer(av);
     setLayout(new BorderLayout());
     PaintRefresher.Register(this, av.getSequenceSetId());
     setBackground(Color.white);
@@ -98,7 +102,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
 
   public SequenceRenderer getSequenceRenderer()
   {
-    return sr;
+    return seqRdr;
   }
 
   public FeatureRenderer getFeatureRenderer()
@@ -106,8 +110,6 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     return fr;
   }
 
-  int charHeight = 0, charWidth = 0;
-
   private void updateViewport()
   {
     charHeight = av.getCharHeight();
@@ -267,7 +269,6 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     }
   }
 
-  boolean fastpainting = false;
 
   /**
    * need to make this thread safe move alignment rendering in response to
@@ -296,8 +297,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     int transX = 0;
     int transY = 0;
 
-    gg.copyArea(horizontal * charWidth, vertical * charHeight, imgWidth,
-            imgHeight, -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
     {
@@ -318,7 +320,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
       }
       else
       {
-        transY = imgHeight - ((vertical + 1) * charHeight);
+        transY = img.getHeight() - ((vertical + 1) * charHeight);
       }
     }
     else if (vertical < 0)
@@ -356,53 +358,91 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
 
     updateViewport();
 
-    // img is a cached version of the last view we drew
-    // selectImage will hold any selection we have
-    // lcimg is a local *copy* of img which we'll draw selectImage on top of
+    ViewportRanges ranges = av.getRanges();
 
-    if (img == null)
-    {
-      setupImage();
-    }
-    if (img == null)
-    {
-      return;
-    }
+    int width = getWidth();
+    int height = getHeight();
 
-    BufferedImage selectImage = drawSelectionGroup();
+    width -= (width % charWidth);
+    height -= (height % charHeight);
 
-    if (fastPaint || (getVisibleRect().width != g.getClipBounds().width)
-            || (getVisibleRect().height != g.getClipBounds().height))
+    // 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)))
     {
       BufferedImage lcimg = buildLocalImage(selectImage);
       g.drawImage(lcimg, 0, 0, this);
-
       fastPaint = false;
-      return;
-    }
-    
-    if (av.antiAlias)
-    {
-      gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
-              RenderingHints.VALUE_ANTIALIAS_ON);
     }
-    
-    gg.setColor(Color.white);
-    gg.fillRect(0, 0, imgWidth, imgHeight);
-    
-    ViewportRanges ranges = av.getRanges();
-    if (av.getWrapAlignment())
+    else if ((width > 0) && (height > 0))
     {
-      drawWrappedPanel(gg, getWidth(), getHeight(), ranges.getStartRes());
-    }
-    else
-    {
-      drawPanel(gg, ranges.getStartRes(), ranges.getEndRes(),
-              ranges.getStartSeq(), ranges.getEndSeq(), 0);
+      // img is a cached version of the last view we drew, if any
+      // if we have no img or the size has changed, make a new one
+      if (img == null || width != img.getWidth()
+              || height != img.getHeight())
+      {
+        img = setupImage();
+        if (img == null)
+        {
+          return;
+        }
+        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());
+      }
+      else
+      {
+        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);
     }
+  }
 
-    BufferedImage lcimg = buildLocalImage(selectImage);
-    g.drawImage(lcimg, 0, 0, this);
+  /**
+   * Draw an alignment panel for printing
+   * 
+   * @param g1
+   *          Graphics object to draw with
+   * @param startRes
+   *          start residue of print area
+   * @param endRes
+   *          end residue of print area
+   * @param startSeq
+   *          start sequence of print area
+   * @param endSeq
+   *          end sequence of print area
+   */
+  public void drawPanelForPrint(Graphics g1, int startRes, int endRes,
+          int startSeq, int endSeq)
+  {
+    BufferedImage selectImage = drawSelectionGroup(startRes, endRes,
+            startSeq, endSeq);
+    drawPanel(g1, startRes, endRes, startSeq, endSeq, 0);
+    ((Graphics2D) g1).setComposite(
+            AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
+    g1.drawImage(selectImage, 0, 0, this);
   }
 
   /*
@@ -429,45 +469,10 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     return lcimg;
   }
 
-  private void paintSeqGroup()
-  {
-    fastPaint = true;
-    repaint();
-  }
-
-  private void setupImage()
-  {
-    // this draws the whole of the alignment
-    imgWidth = getWidth();
-    imgHeight = getHeight();
-
-    imgWidth -= (imgWidth % charWidth);
-    imgHeight -= (imgHeight % charHeight);
-
-    if ((imgWidth < 1) || (imgHeight < 1))
-    {
-      return;
-    }
-
-
-      try
-      {
-      img = new BufferedImage(imgWidth, imgHeight,
-                BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works
-        gg = (Graphics2D) img.getGraphics();
-        gg.setFont(av.getFont());
-      } catch (OutOfMemoryError er)
-      {
-        System.gc();
-        System.err.println("SeqCanvas OutOfMemory Redraw Error.\n" + er);
-        new OOMWarning("Creating alignment image for display", er);
-
-      return;
-      }
-
-  }
-
-  private BufferedImage setupSelectionImage()
+  /*
+   * Set up a buffered image of the correct height and size for the sequence canvas
+   */
+  private BufferedImage setupImage()
   {
     BufferedImage lcimg = null;
 
@@ -490,7 +495,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     {
       System.gc();
       System.err.println(
-              "Selection Group image OutOfMemory Redraw Error.\n" + er);
+              "Group image OutOfMemory Redraw Error.\n" + er);
       new OOMWarning("Creating alignment image for display", er);
 
       return null;
@@ -702,7 +707,61 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     }
   }
 
-  AnnotationPanel annotations;
+  /*
+   * Draw a selection group over a wrapped alignment
+   */
+  private void drawWrappedSelection(Graphics2D g, SequenceGroup group,
+          int canvasWidth,
+          int canvasHeight, int startRes)
+  {
+    // height gap above each panel
+    int hgap = charHeight;
+    if (av.getScaleAboveWrapped())
+    {
+      hgap += charHeight;
+    }
+
+    int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / charWidth;
+    int cHeight = av.getAlignment().getHeight() * charHeight;
+
+    int startx = startRes;
+    int endx;
+    int ypos = hgap; // vertical offset
+    int maxwidth = av.getAlignment().getWidth();
+
+    if (av.hasHiddenColumns())
+    {
+      maxwidth = av.getAlignment().getHiddenColumns()
+              .findColumnPosition(maxwidth);
+    }
+
+    // chop the wrapped alignment extent up into panel-sized blocks and treat
+    // each block as if it were a block from an unwrapped alignment
+    while ((ypos <= canvasHeight) && (startx < maxwidth))
+    {
+      // set end value to be start + width, or maxwidth, whichever is smaller
+      endx = startx + cWidth - 1;
+
+      if (endx > maxwidth)
+      {
+        endx = maxwidth;
+      }
+
+      g.translate(LABEL_WEST, 0);
+
+      drawUnwrappedSelection(g, group, startx, endx, 0,
+              av.getAlignment().getHeight() - 1,
+              ypos);
+
+      g.translate(-LABEL_WEST, 0);
+
+      // update vertical offset
+      ypos += cHeight + getAnnotationHeight() + hgap;
+
+      // update horizontal offset
+      startx += cWidth;
+    }
+  }
 
   int getAnnotationHeight()
   {
@@ -719,23 +778,23 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     return annotations.adjustPanelHeight();
   }
 
-  /**
-   * DOCUMENT ME!
+  /*
+   * Draw an alignment panel for printing
    * 
    * @param g1
-   *          DOCUMENT ME!
+   *          Graphics object to draw with
    * @param startRes
-   *          DOCUMENT ME!
+   *          start residue of print area
    * @param endRes
-   *          DOCUMENT ME!
+   *          end residue of print area
    * @param startSeq
-   *          DOCUMENT ME!
+   *          start sequence of print area
    * @param endSeq
-   *          DOCUMENT ME!
+   *          end sequence of print area
    * @param offset
-   *          DOCUMENT ME!
+   *          vertical offset
    */
-  public void drawPanel(Graphics g1, int startRes, int endRes,
+  private void drawPanel(Graphics g1, int startRes, int endRes,
           int startSeq, int endSeq, int offset)
   {
     updateViewport();
@@ -800,13 +859,11 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
 
   }
 
-  // int startRes, int endRes, int startSeq, int endSeq, int x, int y,
-  // int x1, int x2, int y1, int y2, int startx, int starty,
   private void draw(Graphics g, int startRes, int endRes, int startSeq,
           int endSeq, int offset)
   {
     g.setFont(av.getFont());
-    sr.prepare(g, av.isRenderGaps());
+    seqRdr.prepare(g, av.isRenderGaps());
 
     SequenceI nextSeq;
 
@@ -821,7 +878,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
         // empty
         continue;
       }
-      sr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq),
+      seqRdr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq),
               startRes, endRes, offset + ((i - startSeq) * charHeight));
 
       if (av.isShowSequenceFeatures())
@@ -840,7 +897,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
         {
           for (int r = 0; r < visibleResults.length; r += 2)
           {
-            sr.drawHighlightedText(nextSeq, visibleResults[r],
+            seqRdr.drawHighlightedText(nextSeq, visibleResults[r],
                     visibleResults[r + 1], (visibleResults[r] - startRes)
                             * charWidth, offset
                             + ((i - startSeq) * charHeight));
@@ -851,7 +908,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
       if (av.cursorMode && cursorY == i && cursorX >= startRes
               && cursorX <= endRes)
       {
-        sr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth,
+        seqRdr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth,
                 offset + ((i - startSeq) * charHeight));
       }
     }
@@ -874,12 +931,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     // ///////////////////////////////////
 
     SequenceGroup group = null;
-
-    int sx = -1;
-    int sy = -1;
-    int ex = -1;
     int groupIndex = -1;
-    int visWidth = (endRes - startRes + 1) * charWidth;
 
     if (av.getAlignment().getGroups().size() > 0)
     {
@@ -889,150 +941,14 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
 
     if (group != null)
     {
+      g.setStroke(new BasicStroke());
+      g.setColor(group.getOutlineColour());
+      
       do
       {
-        int oldY = -1;
-        int i = 0;
-        boolean inGroup = false;
-        int top = -1;
-        int bottom = -1;
-
-        for (i = startSeq; i <= endSeq; i++)
-        {
-          // position of start residue of group relative to startRes, in pixels
-          sx = (group.getStartRes() - startRes) * charWidth;
-          sy = offset + ((i - startSeq) * charHeight);
-          // width of group in pixels
-          ex = (((group.getEndRes() + 1) - group.getStartRes()) * charWidth) - 1;
-
-          if (sx + ex < 0 || sx > visWidth)
-          {
-            continue;
-          }
-
-          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)))
-            {
-              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;
-
-              g.setStroke(new BasicStroke());
-              g.setColor(group.getOutlineColour());
-            }
-          }
-          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 + ex < visWidth)
-              {
-                g.drawLine(sx + ex, oldY, sx + ex, sy);
-              }
-
-              if (sx < 0)
-              {
-                // ex += sx;
-                // sx = 0;
-              }
-
-              if (sx + ex > visWidth)
-              {
-                ex = visWidth;
-              }
-
-              else if (sx + ex >= (endRes - startRes + 1) * charWidth)
-              {
-                ex = (endRes - startRes + 1) * charWidth;
-              }
-
-              // draw horizontal line at top of group
-              if (top != -1)
-              {
-                g.drawLine(sx, top, sx + ex, top);
-                top = -1;
-              }
-
-              // draw horizontal line at bottom of group
-              if (bottom != -1)
-              {
-                g.drawLine(sx, bottom, sx + ex, bottom);
-                bottom = -1;
-              }
-
-              inGroup = false;
-            }
-          }
-        }
-
-        if (inGroup)
-        {
-          sy = offset + ((i - startSeq) * charHeight);
-          if (sx >= 0 && sx < visWidth)
-          {
-            g.drawLine(sx, oldY, sx, sy);
-          }
-
-          if (sx + ex < visWidth)
-          {
-            g.drawLine(sx + ex, oldY, sx + ex, sy);
-          }
-
-          if (sx < 0)
-          {
-            ex += sx;
-            sx = 0;
-          }
-
-          if (sx + ex > visWidth)
-          {
-            ex = visWidth;
-          }
-          else if (sx + ex >= (endRes - startRes + 1) * charWidth)
-          {
-            ex = (endRes - startRes + 1) * charWidth;
-          }
-
-          if (top != -1)
-          {
-            g.drawLine(sx, top, sx + ex, top);
-            top = -1;
-          }
-
-          if (bottom != -1)
-          {
-            g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
-            bottom = -1;
-          }
-
-          inGroup = false;
-        }
-
+        drawPartialGroupOutline(g, group, startRes, endRes, startSeq,
+                endSeq, offset);
+        
         groupIndex++;
 
         g.setStroke(new BasicStroke());
@@ -1050,13 +966,15 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
 
   }
 
+
   /*
    * Draw the selection group as a separate image and overlay
    */
-  private BufferedImage drawSelectionGroup()
+  private BufferedImage drawSelectionGroup(int startRes, int endRes,
+          int startSeq, int endSeq)
   {
     // get a new image of the correct size
-    BufferedImage selectionImage = setupSelectionImage();
+    BufferedImage selectionImage = setupImage();
 
     if (selectionImage == null)
     {
@@ -1072,23 +990,108 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
 
     // set up drawing colour
     Graphics2D g = (Graphics2D) selectionImage.getGraphics();
+
     // set background to transparent
     g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
     g.fillRect(0, 0, selectionImage.getWidth(), selectionImage.getHeight());
 
+    // set up foreground to draw red dashed line
     g.setComposite(AlphaComposite.Src);
     g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT,
             BasicStroke.JOIN_ROUND, 3f, new float[]
     { 5f, 3f }, 0f));
     g.setColor(Color.RED);
 
-    int visWidth = av.getRanges().getViewportWidth() * charWidth;
+    if (!av.getWrapAlignment())
+    {
+      drawUnwrappedSelection(g, group, startRes, endRes, startSeq, endSeq,
+              0);
+    }
+    else
+    {
+      drawWrappedSelection(g, group, getWidth(), getHeight(),
+              av.getRanges().getStartRes());
+    }
+
+    g.dispose();
+    return selectionImage;
+  }
+
+  /*
+   * Draw a selection group over an unwrapped alignment
+   * @param g graphics object to draw with
+   * @param group selection group
+   * @param startRes start residue of area to draw
+   * @param endRes end residue of area to draw
+   * @param startSeq start sequence of area to draw
+   * @param endSeq end sequence of area to draw
+   * @param offset vertical offset (used when called from wrapped alignment code)
+   */
+  private void drawUnwrappedSelection(Graphics2D g, SequenceGroup group,
+          int startRes, int endRes, int startSeq, int endSeq, int offset)
+  {
+    if (!av.hasHiddenColumns())
+    {
+      drawPartialGroupOutline(g, group, startRes, endRes, startSeq, endSeq,
+              offset);
+    }
+    else
+    {
+      // package into blocks of visible columns
+      int screenY = 0;
+      int blockStart = startRes;
+      int blockEnd = endRes;
+
+      for (int[] region : av.getAlignment().getHiddenColumns()
+              .getHiddenColumnsCopy())
+      {
+        int hideStart = region[0];
+        int hideEnd = region[1];
+
+        if (hideStart <= blockStart)
+        {
+          blockStart += (hideEnd - hideStart) + 1;
+          continue;
+        }
 
-    int startRes = av.getRanges().getStartRes();
+        blockEnd = hideStart - 1;
+
+        g.translate(screenY * charWidth, 0);
+        drawPartialGroupOutline(g, group,
+                blockStart, blockEnd, startSeq, endSeq, offset);
+
+        g.translate(-screenY * charWidth, 0);
+        screenY += blockEnd - blockStart + 1;
+        blockStart = hideEnd + 1;
+
+        if (screenY > (endRes - startRes))
+        {
+          // already rendered last block
+          break;
+        }
+      }
+
+      if (screenY <= (endRes - startRes))
+      {
+        // remaining visible region to render
+        blockEnd = blockStart + (endRes - startRes) - screenY;
+        g.translate(screenY * charWidth, 0);
+        drawPartialGroupOutline(g, group,
+                blockStart, blockEnd, startSeq, endSeq, offset);
+        
+        g.translate(-screenY * charWidth, 0);
+      }
+    }
+  }
 
-    // set x start and end positions of group
-    int startx = (group.getStartRes() - startRes) * charWidth;
-    int endx = (group.getEndRes() - startRes + 1) * charWidth;
+  /*
+   * Draw the selection group as a separate image and overlay
+   */
+  private void drawPartialGroupOutline(Graphics2D g, SequenceGroup group,
+          int startRes, int endRes, int startSeq, int endSeq,
+          int verticalOffset)
+  {
+    int visWidth = (endRes - startRes + 1) * charWidth;
 
     int oldY = -1;
     int i = 0;
@@ -1096,14 +1099,29 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     int top = -1;
     int bottom = -1;
 
-    // get sequences to determine y positions of group
-    int startSeq = av.getRanges().getStartSeq();
-    for (i = startSeq; i <= av.getRanges().getEndSeq(); ++i)
+    int sx = -1;
+    int sy = -1;
+    int xwidth = -1;
+
+    for (i = startSeq; i <= endSeq; i++)
     {
-      int sy = (i - startSeq) * charHeight;
+      // position of start residue of group relative to startRes, in pixels
+      sx = (group.getStartRes() - startRes) * charWidth;
+
+      // width of group in pixels
+      xwidth = (((group.getEndRes() + 1) - group.getStartRes()) * charWidth)
+              - 1;
 
-      if (group.getSequences(null)
-              .contains(av.getAlignment().getSequenceAt(i)))
+      sy = verticalOffset + (i - startSeq) * charHeight;
+
+      if (sx + xwidth < 0 || sx > visWidth)
+      {
+        continue;
+      }
+
+      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)))
@@ -1129,34 +1147,42 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
         {
           // if start position is visible, draw vertical line to left of
           // group
-          if (startx >= 0 && startx < visWidth * charWidth)
+          if (sx >= 0 && sx < visWidth)
           {
-            g.drawLine(startx, oldY, startx, sy);
+            g.drawLine(sx, oldY, sx, sy);
           }
 
           // if end position is visible, draw vertical line to right of
           // group
-          if (endx <= visWidth * charWidth)
+          if (sx + xwidth < visWidth)
           {
-            g.drawLine(endx, oldY, endx, sy);
+            g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
           }
 
-          if (endx > visWidth * charWidth)
+          if (sx < 0)
           {
-            endx = visWidth * charWidth;
+            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(startx, top, endx, top);
+            g.drawLine(sx, top, sx + xwidth, top);
             top = -1;
           }
 
           // draw horizontal line at bottom of group
           if (bottom != -1)
           {
-            g.drawLine(startx, bottom, endx, bottom);
+            g.drawLine(sx, bottom, sx + xwidth, bottom);
             bottom = -1;
           }
 
@@ -1164,42 +1190,51 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
         }
       }
     }
+
     if (inGroup)
     {
-      int sy = (i - startSeq) * charHeight;
-      if (startx >= 0 && startx < visWidth)
+      sy = verticalOffset + ((i - startSeq) * charHeight);
+      if (sx >= 0 && sx < visWidth)
       {
-        g.drawLine(startx, oldY, startx, sy);
+        g.drawLine(sx, oldY, sx, sy);
       }
 
-      if (endx < visWidth)
+      if (sx + xwidth < visWidth)
       {
-        g.drawLine(endx, oldY, endx, sy);
+        g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
       }
 
-      if (endx > visWidth)
+      if (sx < 0)
       {
-        endx = visWidth;
+        xwidth += sx;
+        sx = 0;
+      }
+
+      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, endx, top);
+        g.drawLine(sx, top, sx + xwidth, top);
         top = -1;
       }
 
       if (bottom != -1)
       {
-        g.drawLine(startx, bottom - 1, endx, bottom - 1);
+        g.drawLine(sx, bottom - 1, sx + xwidth, bottom - 1);
         bottom = -1;
       }
 
       inGroup = false;
     }
-
-    return selectionImage;
   }
-
+  
   /**
    * DOCUMENT ME!
    * 
@@ -1222,7 +1257,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
 
     if (eventName.equals(SequenceGroup.SEQ_GROUP_CHANGED))
     {
-      paintSeqGroup();
+      fastPaint = true;
+      repaint();
     }
     else if (av.getWrapAlignment())
     {