JAL-3438 spotless for 2.11.2.0
[jalview.git] / src / jalview / appletgui / SeqCanvas.java
index 39382c5..3b79f12 100755 (executable)
@@ -25,9 +25,11 @@ import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
+import jalview.datamodel.VisibleContigsIterator;
 import jalview.renderer.ScaleRenderer;
 import jalview.renderer.ScaleRenderer.ScaleMark;
 import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.ViewportListenerI;
 import jalview.viewmodel.ViewportRanges;
 
 import java.awt.Color;
@@ -35,8 +37,11 @@ import java.awt.FontMetrics;
 import java.awt.Graphics;
 import java.awt.Image;
 import java.awt.Panel;
+import java.beans.PropertyChangeEvent;
+import java.util.Iterator;
 
-public class SeqCanvas extends Panel
+@SuppressWarnings("serial")
+public class SeqCanvas extends Panel implements ViewportListenerI
 {
   FeatureRenderer fr;
 
@@ -65,6 +70,8 @@ public class SeqCanvas extends Panel
     sr = new SequenceRenderer(av);
     PaintRefresher.Register(this, av.getSequenceSetId());
     updateViewport();
+
+    av.getRanges().addPropertyChangeListener(this);
   }
 
   int avcharHeight = 0, avcharWidth = 0;
@@ -108,12 +115,12 @@ public class SeqCanvas extends Panel
       {
         if (mstring != null)
         {
-          g.drawString(mstring, mpos * avcharWidth, ypos
-                  - (avcharHeight / 2));
+          g.drawString(mstring, mpos * avcharWidth,
+                  ypos - (avcharHeight / 2));
         }
-        g.drawLine((mpos * avcharWidth) + (avcharWidth / 2), (ypos + 2)
-                - (avcharHeight / 2), (mpos * avcharWidth)
-                + (avcharWidth / 2), ypos - 2);
+        g.drawLine((mpos * avcharWidth) + (avcharWidth / 2),
+                (ypos + 2) - (avcharHeight / 2),
+                (mpos * avcharWidth) + (avcharWidth / 2), ypos - 2);
       }
     }
   }
@@ -125,16 +132,9 @@ public class SeqCanvas extends Panel
     if (av.hasHiddenColumns())
     {
       startx = av.getAlignment().getHiddenColumns()
-              .adjustForHiddenColumns(startx);
+              .visibleToAbsoluteColumn(startx);
       endx = av.getAlignment().getHiddenColumns()
-              .adjustForHiddenColumns(endx);
-    }
-
-    int maxwidth = av.getAlignment().getWidth();
-    if (av.hasHiddenColumns())
-    {
-      maxwidth = av.getAlignment().getHiddenColumns()
-              .findColumnPosition(maxwidth) - 1;
+              .visibleToAbsoluteColumn(endx);
     }
 
     // WEST SCALE
@@ -162,8 +162,8 @@ public class SeqCanvas extends Panel
       {
         int x = LABEL_WEST - fm.stringWidth(String.valueOf(value))
                 - avcharWidth / 2;
-        g.drawString(value + "", x, (ypos + (i * avcharHeight))
-                - (avcharHeight / 5));
+        g.drawString(value + "", x,
+                (ypos + (i * avcharHeight)) - (avcharHeight / 5));
       }
     }
   }
@@ -175,7 +175,7 @@ public class SeqCanvas extends Panel
     if (av.hasHiddenColumns())
     {
       endx = av.getAlignment().getHiddenColumns()
-              .adjustForHiddenColumns(endx);
+              .visibleToAbsoluteColumn(endx);
     }
 
     SequenceI seq;
@@ -202,8 +202,8 @@ public class SeqCanvas extends Panel
 
       if (value != -1)
       {
-        g.drawString(String.valueOf(value), 0, (ypos + (i * avcharHeight))
-                - (avcharHeight / 5));
+        g.drawString(String.valueOf(value), 0,
+                (ypos + (i * avcharHeight)) - (avcharHeight / 5));
       }
     }
   }
@@ -232,14 +232,14 @@ public class SeqCanvas extends Panel
     lastsr = ranges.getStartRes();
 
     fastPaint = true;
-    gg.copyArea(horizontal * avcharWidth, vertical * avcharHeight, imgWidth
-            - horizontal * avcharWidth,
+    gg.copyArea(horizontal * avcharWidth, vertical * avcharHeight,
+            imgWidth - horizontal * avcharWidth,
             imgHeight - vertical * avcharHeight, -horizontal * avcharWidth,
             -vertical * avcharHeight);
 
-    int sr = ranges.getStartRes(), er = ranges.getEndRes(), ss = ranges
-            .getStartSeq(), es = ranges
-            .getEndSeq(), transX = 0, transY = 0;
+    int sr = ranges.getStartRes(), er = ranges.getEndRes(),
+            ss = ranges.getStartSeq(), es = ranges.getEndSeq(), transX = 0,
+            transY = 0;
 
     if (horizontal > 0) // scrollbar pulled right, image to the left
     {
@@ -256,7 +256,7 @@ public class SeqCanvas extends Panel
       ss = es - vertical;
       if (ss < ranges.getStartSeq()) // ie scrolling too fast, more than a page
                                      // at a
-                                 // time
+      // time
       {
         ss = ranges.getStartSeq();
       }
@@ -302,8 +302,8 @@ public class SeqCanvas extends Panel
   {
 
     if (img != null
-            && (fastPaint || (getSize().width != g.getClipBounds().width) || (getSize().height != g
-                    .getClipBounds().height)))
+            && (fastPaint || (getSize().width != g.getClipBounds().width)
+                    || (getSize().height != g.getClipBounds().height)))
     {
       g.drawImage(img, 0, 0, this);
       fastPaint = false;
@@ -415,6 +415,9 @@ public class SeqCanvas extends Panel
 
     FontMetrics fm = getFontMetrics(av.getFont());
 
+    LABEL_EAST = 0;
+    LABEL_WEST = 0;
+
     if (av.getScaleRightWrapped())
     {
       LABEL_EAST = fm.stringWidth(getMask());
@@ -436,18 +439,12 @@ public class SeqCanvas extends Panel
 
     av.setWrappedWidth(cWidth);
 
-    av.getRanges().setEndRes(av.getRanges().getStartRes() + cWidth);
+    av.getRanges().setViewportStartAndWidth(startRes, cWidth);
 
     int endx;
     int ypos = hgap;
 
-    int maxwidth = av.getAlignment().getWidth() - 1;
-
-    if (av.hasHiddenColumns())
-    {
-      maxwidth = av.getAlignment().getHiddenColumns()
-              .findColumnPosition(maxwidth) - 1;
-    }
+    int maxwidth = av.getAlignment().getVisibleWidth();
 
     while ((ypos <= canvasHeight) && (startRes < maxwidth))
     {
@@ -483,23 +480,20 @@ public class SeqCanvas extends Panel
         HiddenColumns hidden = av.getAlignment().getHiddenColumns();
         g.setColor(Color.blue);
         int res;
-        for (int i = 0; i < hidden.getHiddenRegions()
-                .size(); i++)
+        Iterator<Integer> it = hidden.getStartRegionIterator(startRes,
+                endx + 1);
+        while (it.hasNext())
         {
-          res = hidden.findHiddenRegionPosition(i)
-                  - startRes;
-
-          if (res < 0 || res > endx - startRes)
-          {
-            continue;
-          }
-
-          gg.fillPolygon(new int[] { res * avcharWidth - avcharHeight / 4,
-              res * avcharWidth + avcharHeight / 4, res * avcharWidth },
-                  new int[] { ypos - (avcharHeight / 2),
-                      ypos - (avcharHeight / 2),
-                      ypos - (avcharHeight / 2) + 8 }, 3);
-
+          res = it.next() - startRes;
+          gg.fillPolygon(
+                  new int[]
+                  { res * avcharWidth - avcharHeight / 4,
+                      res * avcharWidth + avcharHeight / 4,
+                      res * avcharWidth },
+                  new int[]
+                  { ypos - (avcharHeight / 2), ypos - (avcharHeight / 2),
+                      ypos - (avcharHeight / 2) + 8 },
+                  3);
         }
       }
 
@@ -548,8 +542,8 @@ public class SeqCanvas extends Panel
     return annotations.adjustPanelHeight();
   }
 
-  private void drawPanel(Graphics g1, int startRes, int endRes,
-          int startSeq, int endSeq, int offset)
+  private void drawPanel(Graphics g1, final int startRes, final int endRes,
+          final int startSeq, final int endSeq, final int offset)
   {
 
     if (!av.hasHiddenColumns())
@@ -558,62 +552,45 @@ public class SeqCanvas extends Panel
     }
     else
     {
-
       int screenY = 0;
-      int blockStart = startRes;
-      int blockEnd = endRes;
+      int blockStart;
+      int blockEnd;
 
-      if (av.hasHiddenColumns())
-      {
-        HiddenColumns hidden = av.getAlignment().getHiddenColumns();
-        for (int[] region : hidden.getHiddenRegions())
-        {
-          int hideStart = region[0];
-          int hideEnd = region[1];
-
-          if (hideStart <= blockStart)
-          {
-            blockStart += (hideEnd - hideStart) + 1;
-            continue;
-          }
-
-          blockEnd = hideStart - 1;
-
-          g1.translate(screenY * avcharWidth, 0);
-
-          draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
-
-          if (av.getShowHiddenMarkers())
-          {
-            g1.setColor(Color.blue);
-            g1.drawLine((blockEnd - blockStart + 1) * avcharWidth - 1,
-                    0 + offset, (blockEnd - blockStart + 1) * avcharWidth
-                            - 1, (endSeq - startSeq + 1) * avcharHeight
-                            + offset);
-          }
-
-          g1.translate(-screenY * avcharWidth, 0);
-          screenY += blockEnd - blockStart + 1;
-          blockStart = hideEnd + 1;
+      HiddenColumns hidden = av.getAlignment().getHiddenColumns();
+      VisibleContigsIterator regions = hidden
+              .getVisContigsIterator(startRes, endRes + 1, true);
 
-          if (screenY > (endRes - startRes))
-          {
-            // already rendered last block
-            return;
-          }
-        }
-      }
-      if (screenY <= (endRes - startRes))
+      while (regions.hasNext())
       {
-        // remaining visible region to render
-        blockEnd = blockStart + (endRes - startRes) - screenY;
+        int[] region = regions.next();
+        blockEnd = region[1];
+        blockStart = region[0];
+
+        /*
+         * draw up to just before the next hidden region, or the end of
+         * the visible region, whichever comes first
+         */
         g1.translate(screenY * avcharWidth, 0);
+
         draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
 
+        /*
+         * draw the downline of the hidden column marker (ScalePanel draws the
+         * triangle on top) if we reached it
+         */
+        if (av.getShowHiddenMarkers()
+                && (regions.hasNext() || regions.endsAtHidden()))
+        {
+          g1.setColor(Color.blue);
+          g1.drawLine((blockEnd - blockStart + 1) * avcharWidth - 1,
+                  0 + offset, (blockEnd - blockStart + 1) * avcharWidth - 1,
+                  (endSeq - startSeq + 1) * avcharHeight + offset);
+        }
+
         g1.translate(-screenY * avcharWidth, 0);
+        screenY += blockEnd - blockStart + 1;
       }
     }
-
   }
 
   // int startRes, int endRes, int startSeq, int endSeq, int x, int y,
@@ -642,8 +619,8 @@ public class SeqCanvas extends Panel
 
       if (av.isShowSequenceFeatures())
       {
-        fr.drawSequence(g, nextSeq, startRes, endRes, offset
-                + ((i - startSeq) * avcharHeight), false);
+        fr.drawSequence(g, nextSeq, startRes, endRes,
+                offset + ((i - startSeq) * avcharHeight), false);
       }
 
       // / Highlight search Results once all sequences have been drawn
@@ -651,16 +628,15 @@ public class SeqCanvas extends Panel
       if (av.hasSearchResults())
       {
         int[] visibleResults = av.getSearchResults().getResults(nextSeq,
-                startRes,
-                endRes);
+                startRes, endRes);
         if (visibleResults != null)
         {
           for (int r = 0; r < visibleResults.length; r += 2)
           {
             sr.drawHighlightedText(nextSeq, visibleResults[r],
-                    visibleResults[r + 1], (visibleResults[r] - startRes)
-                            * avcharWidth, offset
-                            + ((i - startSeq) * avcharHeight));
+                    visibleResults[r + 1],
+                    (visibleResults[r] - startRes) * avcharWidth,
+                    offset + ((i - startSeq) * avcharHeight));
           }
         }
       }
@@ -716,7 +692,8 @@ public class SeqCanvas extends Panel
         {
           sx = (group.getStartRes() - startRes) * avcharWidth;
           sy = offset + ((i - startSeq) * avcharHeight);
-          ex = (((group.getEndRes() + 1) - group.getStartRes()) * avcharWidth) - 1;
+          ex = (((group.getEndRes() + 1) - group.getStartRes())
+                  * avcharWidth) - 1;
 
           if (sx + ex < 0 || sx > imgWidth)
           {
@@ -724,22 +701,20 @@ public class SeqCanvas extends Panel
           }
 
           if ((sx <= (endRes - startRes) * avcharWidth)
-                  && group.getSequences(null).contains(
-                          av.getAlignment().getSequenceAt(i)))
+                  && group.getSequences(null)
+                          .contains(av.getAlignment().getSequenceAt(i)))
           {
             if ((bottom == -1)
-                    && (i >= alHeight || !group.getSequences(null)
-                            .contains(
-                                    av.getAlignment().getSequenceAt(i + 1))))
+                    && (i >= alHeight || !group.getSequences(null).contains(
+                            av.getAlignment().getSequenceAt(i + 1))))
             {
               bottom = sy + avcharHeight;
             }
 
             if (!inGroup)
             {
-              if (((top == -1) && (i == 0))
-                      || !group.getSequences(null).contains(
-                              av.getAlignment().getSequenceAt(i - 1)))
+              if (((top == -1) && (i == 0)) || !group.getSequences(null)
+                      .contains(av.getAlignment().getSequenceAt(i - 1)))
               {
                 top = sy;
               }
@@ -866,4 +841,88 @@ public class SeqCanvas extends Panel
     repaint();
   }
 
+  @Override
+  public void propertyChange(PropertyChangeEvent evt)
+  {
+    String eventName = evt.getPropertyName();
+
+    if (eventName.equals(SequenceGroup.SEQ_GROUP_CHANGED))
+    {
+      fastPaint = true;
+      repaint();
+      return;
+    }
+    else if (eventName.equals(ViewportRanges.MOVE_VIEWPORT))
+    {
+      fastPaint = false;
+      repaint();
+      return;
+    }
+
+    if (!av.getWrapAlignment())
+    {
+      int scrollX = 0;
+      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();
+        int range = vpRanges.getEndRes() - vpRanges.getStartRes();
+        if (scrollX > range)
+        {
+          scrollX = range;
+        }
+        else if (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.
+      if (eventName.equals(ViewportRanges.STARTRES))
+      {
+        // scroll - startres and endres both change
+        fastPaint(scrollX, 0);
+      }
+      else if (eventName.equals(ViewportRanges.STARTSEQ))
+      {
+        // scroll
+        fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
+      }
+      else if (eventName.equals(ViewportRanges.STARTRESANDSEQ))
+      {
+        fastPaint(scrollX, 0);
+      }
+    }
+  }
+
+  /**
+   * Ensure that a full paint is done next, for whatever reason. This was
+   * necessary for JavaScript; apparently in Java the timing is just right on
+   * multiple threads (EventQueue-0, Consensus, Conservation) that we can get
+   * away with one fast paint before the others, but this ensures that in the
+   * end we get a full paint. Problem arose in relation to copy/paste, where the
+   * paste was not finalized with a full paint.
+   * 
+   * @author hansonr 2019.04.17
+   */
+  public void clearFastPaint()
+  {
+    fastPaint = false;
+  }
+
 }