JAL-2609 first try
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Fri, 28 Jul 2017 13:12:39 +0000 (15:12 +0200)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Fri, 28 Jul 2017 13:12:39 +0000 (15:12 +0200)
1  2 
src/jalview/gui/SeqCanvas.java
src/jalview/gui/SeqPanel.java

@@@ -27,6 -27,6 +27,7 @@@ import jalview.datamodel.SequenceGroup
  import jalview.datamodel.SequenceI;
  import jalview.renderer.ScaleRenderer;
  import jalview.renderer.ScaleRenderer.ScaleMark;
++import jalview.util.Comparison;
  import jalview.viewmodel.ViewportListenerI;
  import jalview.viewmodel.ViewportRanges;
  
@@@ -70,6 -70,6 +71,8 @@@ public class SeqCanvas extends JCompone
  
    boolean fastPaint = false;
  
++  boolean fastpainting = false;
++
    int labelWidthWest;
  
    int labelWidthEast;
    }
  
    /**
--   * DOCUMENT ME!
++   * Draw the scale to the left or right of a wrapped alignment
     * 
     * @param g
--   *          DOCUMENT ME!
     * @param startx
--   *          DOCUMENT ME!
++   *          first column of wrapped width (0.. excluding any hidden columns)
     * @param endx
--   *          DOCUMENT ME!
++   *          last column of wrapped width (0.. excluding any hidden columns)
     * @param ypos
--   *          DOCUMENT ME!
++   *          vertical offset at which to begin the scale
++   * @param left
++   *          if true, scale is left of residues, if false, scale is right
     */
--  void drawWestScale(Graphics g, int startx, int endx, int ypos)
++  void drawVerticalScale(Graphics g, int startx, int endx, int ypos,
++          boolean left)
    {
--    FontMetrics fm = getFontMetrics(av.getFont());
      ypos += charHeight;
  
      if (av.hasHiddenColumns())
      {
--      startx = av.getAlignment().getHiddenColumns()
--              .adjustForHiddenColumns(startx);
--      endx = av.getAlignment().getHiddenColumns()
--              .adjustForHiddenColumns(endx);
--    }
--
--    int maxwidth = av.getAlignment().getWidth();
--    if (av.hasHiddenColumns())
--    {
--      maxwidth = av.getAlignment().getHiddenColumns()
--              .findColumnPosition(maxwidth) - 1;
++      HiddenColumns hiddenColumns = av.getAlignment().getHiddenColumns();
++      startx = hiddenColumns.adjustForHiddenColumns(startx);
++      endx = hiddenColumns.adjustForHiddenColumns(endx);
      }
++    FontMetrics fm = getFontMetrics(av.getFont());
  
      for (int i = 0; i < av.getAlignment().getHeight(); i++)
      {
        SequenceI seq = av.getAlignment().getSequenceAt(i);
--      int index = startx;
--      int value = -1;
  
--      while (index < endx)
++      /*
++       * find sequence position of first non-gapped position -
++       * to the right if scale left, to the left if scale right
++       */
++      int index = left ? startx : endx;
++      int value = -1;
++      while (index >= startx && index <= endx)
        {
--        if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
++        if (!Comparison.isGap(seq.getCharAt(index)))
++        {
++          value = seq.findPosition(index);
++          break;
++        }
++        if (left)
          {
            index++;
--
--          continue;
          }
--
--        value = av.getAlignment().getSequenceAt(i).findPosition(index);
--
--        break;
--      }
--
--      if (value != -1)
--      {
--        int x = labelWidthWest - fm.stringWidth(String.valueOf(value))
--                - charWidth / 2;
--        g.drawString(value + "", x, (ypos + (i * charHeight))
--                - (charHeight / 5));
--      }
--    }
--  }
--
--  /**
--   * DOCUMENT ME!
--   * 
--   * @param g
--   *          DOCUMENT ME!
--   * @param startx
--   *          DOCUMENT ME!
--   * @param endx
--   *          DOCUMENT ME!
--   * @param ypos
--   *          DOCUMENT ME!
--   */
--  void drawEastScale(Graphics g, int startx, int endx, int ypos)
--  {
--    ypos += charHeight;
--
--    if (av.hasHiddenColumns())
--    {
--      endx = av.getAlignment().getHiddenColumns()
--              .adjustForHiddenColumns(endx);
--    }
--
--    SequenceI seq;
--    // EAST SCALE
--    for (int i = 0; i < av.getAlignment().getHeight(); i++)
--    {
--      seq = av.getAlignment().getSequenceAt(i);
--      int index = endx;
--      int value = -1;
--
--      while (index > startx)
--      {
--        if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
++        else
          {
            index--;
--
--          continue;
          }
--
--        value = seq.findPosition(index);
--
--        break;
        }
  
        if (value != -1)
        {
--        g.drawString(String.valueOf(value), 0, (ypos + (i * charHeight))
++        /*
++         * draw scale value, justified, with half a character width
++         * separation from the sequence data
++         */
++        int justify = fm.stringWidth(String.valueOf(value)) + charWidth;
++        int xpos = left ? labelWidthWest - justify + charWidth / 2
++                : getWidth() - justify - charWidth / 2;
++
++        g.drawString(String.valueOf(value), xpos, (ypos + (i * charHeight))
                  - (charHeight / 5));
        }
      }
    }
  
--  boolean fastpainting = false;
--
    /**
     * need to make this thread safe move alignment rendering in response to
     * slider adjustment
            int canvasHeight, int startRes)
    {
      updateViewport();
--    AlignmentI al = av.getAlignment();
--
      int labelWidth = 0;
      if (av.getScaleRightWrapped() || av.getScaleLeftWrapped())
      {
      }
  
      int cWidth = (canvasWidth - labelWidthEast - labelWidthWest) / charWidth;
--    int cHeight = av.getAlignment().getHeight() * charHeight;
  
      av.setWrappedWidth(cWidth);
  
      av.getRanges().setViewportStartAndWidth(startRes, cWidth);
  
--    int endx;
      int ypos = hgap;
      int maxwidth = av.getAlignment().getWidth();
  
      }
  
      int annotationHeight = getAnnotationHeight();
++    int sequencesHeight = av.getAlignment().getHeight() * charHeight;
  
      while ((ypos <= canvasHeight) && (startRes < maxwidth))
      {
--      endx = startRes + cWidth - 1;
++      drawWrappedWidth(g, startRes, canvasHeight, cWidth, maxwidth, ypos);
  
--      if (endx > maxwidth)
--      {
--        endx = maxwidth;
--      }
++      ypos += sequencesHeight + annotationHeight + hgap;
  
--      g.setFont(av.getFont());
--      g.setColor(Color.black);
++      startRes += cWidth;
++    }
++  }
  
--      if (av.getScaleLeftWrapped())
--      {
--        drawWestScale(g, startRes, endx, ypos);
--      }
++  /**
++   * @param g
++   * @param startRes
++   * @param canvasHeight
++   * @param canvasWidth
++   * @param maxWidth
++   * @param ypos
++   */
++  protected void drawWrappedWidth(Graphics g, int startRes,
++          int canvasHeight, int canvasWidth, int maxWidth, int ypos)
++  {
++    int endx;
++    endx = startRes + canvasWidth - 1;
  
--      if (av.getScaleRightWrapped())
--      {
--        g.translate(canvasWidth - labelWidthEast, 0);
--        drawEastScale(g, startRes, endx, ypos);
--        g.translate(-(canvasWidth - labelWidthEast), 0);
--      }
++    if (endx > maxWidth)
++    {
++      endx = maxWidth;
++    }
  
--      g.translate(labelWidthWest, 0);
++    g.setFont(av.getFont());
++    g.setColor(Color.black);
  
--      if (av.getScaleAboveWrapped())
--      {
--        drawNorthScale(g, startRes, endx, ypos);
--      }
++    if (av.getScaleLeftWrapped())
++    {
++      drawVerticalScale(g, startRes, endx, ypos, true);
++    }
  
--      if (av.hasHiddenColumns() && av.getShowHiddenMarkers())
--      {
--        g.setColor(Color.blue);
--        int res;
--        HiddenColumns hidden = av.getAlignment().getHiddenColumns();
--        List<Integer> positions = hidden.findHiddenRegionPositions();
--        for (int pos : positions)
--        {
--          res = pos - startRes;
++    if (av.getScaleRightWrapped())
++    {
++      drawVerticalScale(g, startRes, endx, ypos, false);
++    }
  
--          if (res < 0 || res > endx - startRes)
--          {
--            continue;
--          }
++    g.translate(labelWidthWest, 0);
  
--          gg.fillPolygon(
--                  new int[] { res * charWidth - charHeight / 4,
--                      res * charWidth + charHeight / 4, res * charWidth },
--                  new int[] { ypos - (charHeight / 2),
--                      ypos - (charHeight / 2), ypos - (charHeight / 2) + 8 },
--                  3);
++    if (av.getScaleAboveWrapped())
++    {
++      drawNorthScale(g, startRes, endx, ypos);
++    }
  
++    if (av.hasHiddenColumns() && av.getShowHiddenMarkers())
++    {
++      g.setColor(Color.blue);
++      HiddenColumns hidden = av.getAlignment().getHiddenColumns();
++      List<Integer> positions = hidden.findHiddenRegionPositions();
++      for (int pos : positions)
++      {
++        int res = pos - startRes;
++
++        if (res < 0 || res > endx - startRes)
++        {
++          continue;
          }
--      }
  
--      // When printing we have an extra clipped region,
--      // the Printable page which we need to account for here
--      Shape clip = g.getClip();
++        gg.fillPolygon(new int[] { res * charWidth - charHeight / 4,
++            res * charWidth + charHeight / 4, res * charWidth }, new int[] {
++            ypos - (charHeight / 2), ypos - (charHeight / 2),
++            ypos - (charHeight / 2) + 8 }, 3);
  
--      if (clip == null)
--      {
--        g.setClip(0, 0, cWidth * charWidth, canvasHeight);
--      }
--      else
--      {
--        g.setClip(0, (int) clip.getBounds().getY(), cWidth * charWidth,
--                (int) clip.getBounds().getHeight());
        }
++    }
  
--      drawPanel(g, startRes, endx, 0, al.getHeight() - 1, ypos);
++    // When printing we have an extra clipped region,
++    // the Printable page which we need to account for here
++    Shape clip = g.getClip();
  
--      if (av.isShowAnnotation())
--      {
--        g.translate(0, cHeight + ypos + 3);
--        if (annotations == null)
--        {
--          annotations = new AnnotationPanel(av);
--        }
++    if (clip == null)
++    {
++      g.setClip(0, 0, canvasWidth * charWidth, canvasHeight);
++    }
++    else
++    {
++      g.setClip(0, (int) clip.getBounds().getY(), canvasWidth * charWidth,
++              (int) clip.getBounds().getHeight());
++    }
  
--        annotations.renderer.drawComponent(annotations, av, g, -1,
--                startRes, endx + 1);
--        g.translate(0, -cHeight - ypos - 3);
--      }
--      g.setClip(clip);
--      g.translate(-labelWidthWest, 0);
++    drawPanel(g, startRes, endx, 0, av.getAlignment().getHeight() - 1, ypos);
  
--      ypos += cHeight + annotationHeight + hgap;
++    int cHeight = av.getAlignment().getHeight() * charHeight;
  
--      startRes += cWidth;
++    if (av.isShowAnnotation())
++    {
++      g.translate(0, cHeight + ypos + 3);
++      if (annotations == null)
++      {
++        annotations = new AnnotationPanel(av);
++      }
++
++      annotations.renderer.drawComponent(annotations, av, g, -1, startRes,
++              endx + 1);
++      g.translate(0, -cHeight - ypos - 3);
      }
++    g.setClip(clip);
++    g.translate(-labelWidthWest, 0);
    }
  
    AnnotationPanel annotations;
    {
      String eventName = evt.getPropertyName();
  
--    if (av.getWrapAlignment())
--    {
--      if (eventName.equals(ViewportRanges.STARTRES))
--      {
--        repaint();
--      }
--    }
--    else
++    if (true/*!av.getWrapAlignment()*/)
      {
        int scrollX = 0;
        if (eventName.equals(ViewportRanges.STARTRES))
        if (eventName.equals(ViewportRanges.STARTRES))
        {
          // scroll - startres and endres both change
--        fastPaint(scrollX, 0);
++        if (av.getWrapAlignment())
++        {
++          fastPaintWrapped(scrollX);
++        }
++        else
++        {
++          fastPaint(scrollX, 0);
++        }
        }
        else if (eventName.equals(ViewportRanges.STARTSEQ))
        {
    }
  
    /**
++   * Does a minimal update of the image for a scroll movement. This method
++   * handles scroll movements of up to one width of the wrapped alignment (one
++   * click in the vertical scrollbar). Larger movements (for example after a
++   * scroll to highlight a mapped position) trigger a full redraw instead.
++   * 
++   * @param scrollX
++   *          number of positions scrolled (right if positive, left if negative)
++   */
++  protected void fastPaintWrapped(int scrollX)
++  {
++    if (Math.abs(scrollX) > 0 /*av.getRanges().getViewportWidth()*/)
++    {
++      /*
++       * shift of more than one view width is too much
++       * to handle in this method
++       */
++      fastPaint = false;
++      repaint();
++      return;
++    }
++
++    fastPaint = true;
++    shiftWrappedAlignment(-scrollX);
++
++    // add new columns (scale above, sequence, annotation)
++    // at top left if scrollX < 0 or bottom right if scrollX > 0
++
++    repaint();
++  }
++
++  /**
++   * Shifts the visible alignment by the specified number of columns - left if
++   * negative, right if positive. Includes scale above, left or right and
++   * annotations (if shown). Does not draw newly visible columns.
++   * 
++   * @param positions
++   */
++  protected void shiftWrappedAlignment(int positions)
++  {
++    if (positions == 0)
++    {
++      return;
++    }
++
++    int repeatHeight = getRepeatHeightWrapped();
++    ViewportRanges ranges = av.getRanges();
++    int widthToCopy = (ranges.getViewportWidth() - Math.abs(positions))
++            * charWidth;
++    int visibleWidths = getHeight() / repeatHeight;
++    if (getHeight() % repeatHeight > 0)
++    {
++      visibleWidths++;
++    }
++    int viewportWidth = ranges.getViewportWidth();
++
++    if (positions > 0)
++    {
++      /*
++       * shift right (after scroll left)
++       * for each wrapped width (starting with the last), copy (width-positions) 
++       * columns from the left margin to the right margin, and copy positions 
++       * columns from the right margin of the row above (if any) to the 
++       * left margin of the current row
++       */
++      int xpos = ranges.getStartRes() + (visibleWidths - 1) * viewportWidth;
++
++      /*
++       * get y-offset of last wrapped width
++       */
++      int y = getHeight() / repeatHeight * repeatHeight;
++      int copyFromLeftStart = labelWidthWest;
++      int copyFromRightStart = copyFromLeftStart + widthToCopy;
++
++      while (y >= 0)
++      {
++        // todo limit repeatHeight for a last part height width
++        gg.copyArea(copyFromLeftStart, y, widthToCopy, repeatHeight,
++                positions * charWidth, 0);
++        if (y > 0)
++        {
++          gg.copyArea(copyFromRightStart, y - repeatHeight, positions
++                  * charWidth, repeatHeight, -widthToCopy, repeatHeight);
++        }
++
++        if (av.getScaleLeftWrapped())
++        {
++          // drawVerticalScale(gg, xpos, xpos + viewportWidth, y, true);
++        }
++        if (av.getScaleRightWrapped())
++        {
++          // drawVerticalScale(gg, xpos, xpos + viewportWidth, y, false);
++        }
++
++        y -= repeatHeight;
++        xpos -= viewportWidth;
++      }
++    }
++    else
++    {
++      /*
++       * shift left (after scroll right)
++       * for each wrapped width (starting with the first), copy (width-positions) 
++       * columns from the right margin to the left margin, and copy positions 
++       * columns from the left margin of the row below (if any) to the 
++       * right margin of the current row
++       */
++      int xpos = ranges.getStartRes();
++      int y = 0;
++      int copyFromRightStart = labelWidthWest - positions * charWidth;
++
++      while (y < getHeight())
++      {
++        // todo limit repeatHeight for a last part height width
++        gg.copyArea(copyFromRightStart, y, widthToCopy, repeatHeight,
++                positions * charWidth, 0);
++        if (y + repeatHeight < getHeight())
++        {
++          gg.copyArea(labelWidthWest, y + repeatHeight, -positions
++                  * charWidth, repeatHeight, widthToCopy, -repeatHeight);
++        }
++
++        if (av.getScaleLeftWrapped())
++        {
++          // drawVerticalScale(gg, xpos, xpos + viewportWidth, y, true);
++        }
++        if (av.getScaleRightWrapped())
++        {
++          // drawVerticalScale(gg, xpos, xpos + viewportWidth, y, false);
++        }
++
++        y += repeatHeight;
++        xpos += ranges.getViewportWidth();
++      }
++    }
++  }
++
++  /**
     * Redraws any positions in the search results in the visible region of a
     * wrapped alignment. Any highlights are drawn depending on the search results
     * set on the Viewport, not the <code>results</code> argument. This allows
@@@ -215,7 -215,7 +215,7 @@@ public class SeqPanel extends JPanel im
                + hgap + seqCanvas.getAnnotationHeight();
  
        int y = evt.getY();
--      y -= hgap;
++      y = Math.max(0, y - hgap);
        x = Math.max(0, x - seqCanvas.labelWidthWest);
  
        int cwidth = seqCanvas.getWrappedCanvasWidth(this.getWidth());
        wrappedBlock += startRes / cwidth;
        // allow for wrapped view scrolled right (possible from Overview)
        int startOffset = startRes % cwidth;
--      res = wrappedBlock * cwidth
--              + Math.min(cwidth - 1, startOffset + x / av.getCharWidth());
++      res = wrappedBlock * cwidth + startOffset
++              + +Math.min(cwidth - 1, x / av.getCharWidth());
      }
      else
      {