Merge branch 'develop' into features/JAL-2446NCList
[jalview.git] / src / jalview / gui / SeqCanvas.java
index e2401d2..0e31246 100755 (executable)
@@ -414,14 +414,15 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
   }
 
   /**
-   * DOCUMENT ME!
+   * Returns the visible width of the canvas in residues, after allowing for
+   * East or West scales (if shown)
    * 
-   * @param cwidth
-   *          DOCUMENT ME!
+   * @param canvasWidth
+   *          the width in pixels (possibly including scales)
    * 
-   * @return DOCUMENT ME!
+   * @return
    */
-  public int getWrappedCanvasWidth(int cwidth)
+  public int getWrappedCanvasWidth(int canvasWidth)
   {
     FontMetrics fm = getFontMetrics(av.getFont());
 
@@ -435,10 +436,11 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
 
     if (av.getScaleLeftWrapped())
     {
-      labelWidthWest = getLabelWidth(fm);
+      labelWidthWest = labelWidthEast > 0 ? labelWidthEast
+              : getLabelWidth(fm);
     }
 
-    return (cwidth - labelWidthEast - labelWidthWest) / charWidth;
+    return (canvasWidth - labelWidthEast - labelWidthWest) / charWidth;
   }
 
   /**
@@ -451,11 +453,15 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
    */
   protected int getLabelWidth(FontMetrics fm)
   {
+    /*
+     * find the biggest sequence end position we need to show
+     * (note this is not necessarily the sequence length)
+     */
     int maxWidth = 0;
-    for (int i = 0; i < av.getAlignment().getHeight(); i++)
+    AlignmentI alignment = av.getAlignment();
+    for (int i = 0; i < alignment.getHeight(); i++)
     {
-      maxWidth = Math.max(maxWidth, av.getAlignment().getSequenceAt(i)
-              .getEnd());
+      maxWidth = Math.max(maxWidth, alignment.getSequenceAt(i).getEnd());
     }
 
     int length = 2;
@@ -506,16 +512,16 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
 
     av.setWrappedWidth(cWidth);
 
-    av.getRanges().setEndRes(av.getRanges().getStartRes() + cWidth - 1);
+    av.getRanges().setViewportStartAndWidth(startRes, cWidth);
 
     int endx;
     int ypos = hgap;
-    int maxwidth = av.getAlignment().getWidth() - 1;
+    int maxwidth = av.getAlignment().getWidth();
 
     if (av.hasHiddenColumns())
     {
       maxwidth = av.getAlignment().getHiddenColumns()
-              .findColumnPosition(maxwidth) - 1;
+              .findColumnPosition(maxwidth);
     }
 
     int annotationHeight = getAnnotationHeight();
@@ -994,32 +1000,33 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
    * on a black background. Any previous highlighting is removed. Answers true
    * if any highlight was left on the visible alignment (so status bar should be
    * set to match), else false.
+   * <p>
+   * Currently fastPaint is not implemented for wrapped alignments. If a wrapped
+   * alignment had to be scrolled to show the highlighted region, then it should
+   * be fully redrawn, otherwise a fast paint can be performed. This argument
+   * could be removed if fast paint of scrolled wrapped alignment is coded in
+   * future (JAL-2609).
    * 
    * @param results
+   * @param noFastPaint
    * @return
    */
-  public boolean highlightSearchResults(SearchResultsI results)
+  public boolean highlightSearchResults(SearchResultsI results,
+          boolean noFastPaint)
   {
-    updateViewport();
-
-    /*
-     * for now, don't attempt fastpaint if wrapped format
-     */
-    boolean wrapped = av.getWrapAlignment();
-    if (wrapped)
+    if (fastpainting)
     {
-      // drawWrappedMappedPositions(results);
-      // img = null;
-      // av.setSearchResults(results);
-      // repaint();
-      // return;
+      return false;
     }
-    
-    fastpainting = true;
-    fastPaint = true;
+    boolean wrapped = av.getWrapAlignment();
 
     try
     {
+      fastPaint = !noFastPaint;
+      fastpainting = fastPaint;
+
+      updateViewport();
+
       /*
        * to avoid redrawing the whole visible region, we instead
        * redraw just the minimal regions to remove previous highlights
@@ -1031,8 +1038,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
       boolean drawn = false;
       if (wrapped)
       {
-        redrawn = drawWrappedMappedPositions(previous);
-        drawn = drawWrappedMappedPositions(results);
+        redrawn = drawMappedPositionsWrapped(previous);
+        drawn = drawMappedPositionsWrapped(results);
         redrawn |= drawn;
       }
       else
@@ -1059,7 +1066,6 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     {
       fastpainting = false;
     }
-
   }
 
   /**
@@ -1160,7 +1166,14 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
   {
     String eventName = evt.getPropertyName();
 
-    if (!av.getWrapAlignment())
+    if (av.getWrapAlignment())
+    {
+      if (eventName.equals(ViewportRanges.STARTRES))
+      {
+        repaint();
+      }
+    }
+    else
     {
       int scrollX = 0;
       if (eventName.equals(ViewportRanges.STARTRES))
@@ -1199,16 +1212,16 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
   }
 
   /**
-   * Redraws any positions in the search results in the visible region. Any
-   * highlights are drawn depending on the search results set on the Viewport,
-   * not the results parameter. This allows this method to be called to either
-   * clear highlighting (passing the previous search results), or set new
-   * highlighting.
+   * 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
+   * this method to be called either to clear highlights (passing the previous
+   * search results), or to draw new highlights.
    * 
    * @param results
    * @return
    */
-  protected boolean drawWrappedMappedPositions(SearchResultsI results)
+  protected boolean drawMappedPositionsWrapped(SearchResultsI results)
   {
     if (results == null)
     {
@@ -1216,15 +1229,22 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
     }
   
     boolean matchFound = false;
-  
-    /*
-     * Viewport ranges are set for the 'row' of the wrapped alignment
-     * the cursor is in, not the whole visible region; really we want
-     * the latter; +-6 a temporary fudge for codons wrapping across lines
-     */
+
+    int wrappedWidth = av.getWrappedWidth();
+    int wrappedHeight = getRepeatHeightWrapped();
+
     ViewportRanges ranges = av.getRanges();
-    int firstVisibleColumn = ranges.getStartRes() - 6;
-    int lastVisibleColumn = ranges.getEndRes() + 6;
+    int canvasHeight = getHeight();
+    int repeats = canvasHeight / wrappedHeight;
+    if (canvasHeight / wrappedHeight > 0)
+    {
+      repeats++;
+    }
+
+    int firstVisibleColumn = ranges.getStartRes();
+    int lastVisibleColumn = ranges.getStartRes() + repeats
+            * ranges.getViewportWidth() - 1;
+
     AlignmentI alignment = av.getAlignment();
     if (av.hasHiddenColumns())
     {
@@ -1233,17 +1253,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
       lastVisibleColumn = alignment.getHiddenColumns()
               .adjustForHiddenColumns(lastVisibleColumn);
     }
-  
-    /*
-     * find width of alignment in residues, and height of alignment, 
-     * so we can calculate where to render each matched position
-     */
-    int wrappedWidth = av.getWrappedWidth();
-    int wrappedHeight = av.getAlignment().getHeight() * av.getCharHeight();
-    int gapHeight = av.getCharHeight()
-            * (av.getScaleAboveWrapped() ? 2 : 1);
-    wrappedHeight += gapHeight;
-    wrappedHeight += getAnnotationHeight();
+
+    int gapHeight = charHeight * (av.getScaleAboveWrapped() ? 2 : 1);
 
     for (int seqNo = ranges.getStartSeq(); seqNo <= ranges
             .getEndSeq(); seqNo++)
@@ -1293,7 +1304,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
                * transY: offset from top edge of canvas to residue position
                */
               int transY = gapHeight;
-              transY += (displayColumn / wrappedWidth) * wrappedHeight;
+              transY += (displayColumn - ranges.getStartRes())
+                      / wrappedWidth * wrappedHeight;
               transY += (seqNo - ranges.getStartSeq()) * av.getCharHeight();
 
               /*
@@ -1316,4 +1328,25 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
   
     return matchFound;
   }
+
+  /**
+   * Answers the height in pixels of a repeating section of the wrapped
+   * alignment, including space above, scale above if shown, sequences, and
+   * annotation panel if shown
+   * 
+   * @return
+   */
+  protected int getRepeatHeightWrapped()
+  {
+    // gap (and maybe scale) above
+    int repeatHeight = charHeight * (av.getScaleAboveWrapped() ? 2 : 1);
+
+    // add sequences
+    repeatHeight += av.getRanges().getViewportHeight() * charHeight;
+
+    // add annotations panel height if shown
+    repeatHeight += getAnnotationHeight();
+
+    return repeatHeight;
+  }
 }