Merge branch 'develop' into bug/JAL-147scrollWrappedView
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 3 Jul 2017 14:39:09 +0000 (15:39 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 3 Jul 2017 14:39:09 +0000 (15:39 +0100)
17 files changed:
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/AlignmentPanel.java
src/jalview/appletgui/IdCanvas.java
src/jalview/appletgui/ScalePanel.java
src/jalview/appletgui/SeqPanel.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignmentPanel.java
src/jalview/gui/IdCanvas.java
src/jalview/gui/IdPanel.java
src/jalview/gui/OverviewPanel.java
src/jalview/gui/ScalePanel.java
src/jalview/gui/SeqCanvas.java
src/jalview/gui/SeqPanel.java
src/jalview/viewmodel/AlignmentViewport.java
src/jalview/viewmodel/OverviewDimensions.java
src/jalview/viewmodel/ViewportRanges.java
test/jalview/viewmodel/ViewportRangesTest.java

index 8f28658..4ffdfc1 100644 (file)
@@ -599,25 +599,11 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     }
 
     case KeyEvent.VK_PAGE_UP:
-      if (viewport.getWrapAlignment())
-      {
-        ranges.scrollUp(true);
-      }
-      else
-      {
-        ranges.pageUp();
-      }
+      ranges.pageUp();
       break;
 
     case KeyEvent.VK_PAGE_DOWN:
-      if (viewport.getWrapAlignment())
-      {
-        ranges.scrollUp(false);
-      }
-      else
-      {
-        ranges.pageDown();
-      }
+      ranges.pageDown();
       break;
 
     case KeyEvent.VK_Z:
index e402b9b..edf6ad4 100644 (file)
@@ -802,42 +802,45 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
     sendViewPosition();
   }
 
-  private void adjustVertical(int offy)
+  private void adjustVertical(int newY)
   {
-    int oldX = vpRanges.getStartRes();
-    int oldwidth = vpRanges.getViewportWidth();
-    int oldY = vpRanges.getStartSeq();
-    int oldheight = vpRanges.getViewportHeight();
-
     if (av.getWrapAlignment())
     {
-      int rowSize = seqPanel.seqCanvas
-              .getWrappedCanvasWidth(seqPanel.seqCanvas.getWidth());
-
-      // if we're scrolling to the position we're already at, stop
-      // this prevents infinite recursion of events when the scroll/viewport
-      // ranges values are the same
-      if ((offy * rowSize == oldX) && (oldwidth == rowSize))
+      /*
+       * if we're scrolling to the position we're already at, stop
+       * this prevents infinite recursion of events when the scroll/viewport
+       * ranges values are the same
+       */
+      int oldX = vpRanges.getStartRes();
+      int oldY = vpRanges.getWrappedScrollPosition(oldX);
+      if (oldY == newY)
       {
         return;
       }
-      else if (offy > -1)
+      else if (newY > -1)
       {
-        vpRanges.setViewportStartAndWidth(offy * rowSize, rowSize);
+        /*
+         * limit page up/down to one width's worth of positions
+         */
+        int rowSize = vpRanges.getViewportWidth();
+        int newX = newY > oldY ? oldX + rowSize : oldX - rowSize;
+        vpRanges.setViewportStartAndWidth(newX, rowSize);
       }
     }
     else
     {
       int height = seqPanel.seqCanvas.getHeight() / av.getCharHeight();
+      int oldY = vpRanges.getStartSeq();
+      int oldheight = vpRanges.getViewportHeight();
 
       // if we're scrolling to the position we're already at, stop
       // this prevents infinite recursion of events when the scroll/viewport
       // ranges values are the same
-      if ((offy == oldY) && (height == oldheight))
+      if ((newY == oldY) && (height == oldheight))
       {
         return;
       }
-      vpRanges.setViewportStartAndHeight(offy, height);
+      vpRanges.setViewportStartAndHeight(newY, height);
     }
     if (av.getWrapAlignment() || !fastPaint)
     {
@@ -982,35 +985,20 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
 
   }
 
-  /*
+  /**
    * Set vertical scroll bar parameters for wrapped panel
-   * @param res 
-   *    the residue to scroll to
+   * 
+   * @param topLeftColumn
+   *          the column position at top left (0..)
    */
-  private void setScrollingForWrappedPanel(int res)
+  private void setScrollingForWrappedPanel(int topLeftColumn)
   {
-    // get the width of the alignment in residues
-    int maxwidth = av.getAlignment().getWidth();
-    if (av.hasHiddenColumns())
-    {
-      maxwidth = av.getAlignment().getHiddenColumns()
-              .findColumnPosition(maxwidth) - 1;
-    }
+    int scrollPosition = vpRanges.getWrappedScrollPosition(topLeftColumn);
+    int maxScroll = vpRanges.getWrappedScrollPosition(vpRanges
+            .getVisibleAlignmentWidth() - 1);
 
-    // get the width of the canvas in residues
-    int canvasWidth = seqPanel.seqCanvas
-            .getWrappedCanvasWidth(seqPanel.seqCanvas.getSize().width);
-    if (canvasWidth > 0)
-    {
-      // position we want to scroll to is number of canvasWidth's to get there
-      int current = res / canvasWidth;
-
-      // max scroll position: add one because extent is 1 and scrollbar value
-      // can only be set to at most max - extent
-      int max = maxwidth / canvasWidth + 1;
-      vscroll.setUnitIncrement(1);
-      vscroll.setValues(current, 1, 0, max);
-    }
+    vscroll.setUnitIncrement(1);
+    vscroll.setValues(scrollPosition, 1, 0, maxScroll);
   }
 
   protected Panel sequenceHolderPanel = new Panel();
index 5313b41..48c0c40 100755 (executable)
@@ -101,7 +101,7 @@ public class IdCanvas extends Panel implements ViewportListenerI
 
   public void fastPaint(int vertical)
   {
-    if (gg == null)
+    if (gg == null || av.getWrapAlignment())
     {
       repaint();
       return;
@@ -199,130 +199,148 @@ public class IdCanvas extends Panel implements ViewportListenerI
 
   void drawIds(int starty, int endy)
   {
-    // hardwired italic IDs in applet currently
-    Font italic = new Font(av.getFont().getName(), Font.ITALIC, av
-            .getFont().getSize());
-    // temp variable for speed
     avcharHeight = av.getCharHeight();
 
-    gg.setFont(italic);
-
     Color currentColor = Color.white;
     Color currentTextColor = Color.black;
 
     final boolean doHiddenCheck = av.isDisplayReferenceSeq()
-            || av.hasHiddenRows(), hiddenRows = av.hasHiddenRows()
-            && av.getShowHiddenMarkers();
+            || av.hasHiddenRows();
+    boolean hiddenRows = av.hasHiddenRows() && av.getShowHiddenMarkers();
 
     if (av.getWrapAlignment())
     {
-      int maxwidth = av.getAlignment().getWidth();
-      int alheight = av.getAlignment().getHeight();
+      drawIdsWrapped(starty, doHiddenCheck, hiddenRows);
+      return;
+    }
 
-      if (av.hasHiddenColumns())
+    // Now draw the id strings
+    SequenceI seq;
+    for (int i = starty; i <= endy; i++)
+    {
+      seq = av.getAlignment().getSequenceAt(i);
+      if (seq == null)
       {
-        maxwidth = av.getAlignment().getHiddenColumns()
-                .findColumnPosition(maxwidth) - 1;
+        continue;
+      }
+      // hardwired italic IDs in applet currently
+      Font italic = new Font(av.getFont().getName(), Font.ITALIC, av
+              .getFont().getSize());
+      gg.setFont(italic);
+      // boolean isrep=false;
+      if (doHiddenCheck)
+      {
+        // isrep =
+        setHiddenFont(seq);
       }
 
-      int annotationHeight = 0;
-      AnnotationLabels labels = null;
-
-      if (av.isShowAnnotation())
+      // Selected sequence colours
+      if ((searchResults != null) && searchResults.contains(seq))
       {
-        AnnotationPanel ap = new AnnotationPanel(av);
-        annotationHeight = ap.adjustPanelHeight();
-        labels = new AnnotationLabels(av);
+        currentColor = Color.black;
+        currentTextColor = Color.white;
       }
-      int hgap = avcharHeight;
-      if (av.getScaleAboveWrapped())
+      else if ((av.getSelectionGroup() != null)
+              && av.getSelectionGroup().getSequences(null).contains(seq))
+      {
+        currentColor = Color.lightGray;
+        currentTextColor = Color.black;
+      }
+      else
       {
-        hgap += avcharHeight;
+        currentColor = av.getSequenceColour(seq);
+        currentTextColor = Color.black;
       }
 
-      int cHeight = alheight * avcharHeight + hgap + annotationHeight;
+      gg.setColor(currentColor);
+      // TODO: isrep could be used to highlight the representative in a
+      // different way
+      gg.fillRect(0, (i - starty) * avcharHeight, getSize().width,
+              avcharHeight);
+      gg.setColor(currentTextColor);
 
-      int rowSize = av.getRanges().getEndRes()
-              - av.getRanges().getStartRes();
-      // Draw the rest of the panels
-      for (int ypos = hgap, row = av.getRanges().getStartRes(); (ypos <= getSize().height)
-              && (row < maxwidth); ypos += cHeight, row += rowSize)
+      gg.drawString(seq.getDisplayId(av.getShowJVSuffix()), 0,
+              (((i - starty) * avcharHeight) + avcharHeight)
+                      - (avcharHeight / 5));
+
+      if (hiddenRows)
       {
-        for (int i = starty; i < alheight; i++)
-        {
+        drawMarker(i, starty, 0);
+      }
+    }
+  }
 
-          SequenceI s = av.getAlignment().getSequenceAt(i);
-          gg.setFont(italic);
-          if (doHiddenCheck)
-          {
-            setHiddenFont(s);
-          }
-          drawIdString(gg, hiddenRows, s, i, 0, ypos);
-        }
+  /**
+   * Draws sequence ids in wrapped mode
+   * 
+   * @param starty
+   * @param doHiddenCheck
+   * @param hiddenRows
+   */
+  protected void drawIdsWrapped(int starty, final boolean doHiddenCheck,
+          boolean hiddenRows)
+  {
+    int maxwidth = av.getAlignment().getWidth();
+    int alheight = av.getAlignment().getHeight();
 
-        if (labels != null)
-        {
-          gg.translate(0, ypos + (alheight * avcharHeight));
-          labels.drawComponent(gg, getSize().width);
-          gg.translate(0, -ypos - (alheight * avcharHeight));
-        }
+    if (av.hasHiddenColumns())
+    {
+      maxwidth = av.getAlignment().getHiddenColumns()
+              .findColumnPosition(maxwidth) - 1;
+    }
 
-      }
+    int annotationHeight = 0;
+    AnnotationLabels labels = null;
+
+    if (av.isShowAnnotation())
+    {
+      AnnotationPanel ap = new AnnotationPanel(av);
+      annotationHeight = ap.adjustPanelHeight();
+      labels = new AnnotationLabels(av);
     }
-    else
+    int hgap = avcharHeight;
+    if (av.getScaleAboveWrapped())
     {
-      // Now draw the id strings
-      SequenceI seq;
-      for (int i = starty; i <= endy; i++)
-      {
+      hgap += avcharHeight;
+    }
 
-        seq = av.getAlignment().getSequenceAt(i);
-        if (seq == null)
-        {
-          continue;
-        }
-        gg.setFont(italic);
-        // boolean isrep=false;
-        if (doHiddenCheck)
-        {
-          // isrep =
-          setHiddenFont(seq);
-        }
+    int cHeight = alheight * avcharHeight + hgap + annotationHeight;
 
-        // Selected sequence colours
-        if ((searchResults != null) && searchResults.contains(seq))
-        {
-          currentColor = Color.black;
-          currentTextColor = Color.white;
-        }
-        else if ((av.getSelectionGroup() != null)
-                && av.getSelectionGroup().getSequences(null).contains(seq))
-        {
-          currentColor = Color.lightGray;
-          currentTextColor = Color.black;
-        }
-        else
-        {
-          currentColor = av.getSequenceColour(seq);
-          currentTextColor = Color.black;
-        }
+    int rowSize = av.getRanges().getViewportWidth();
 
-        gg.setColor(currentColor);
-        // TODO: isrep could be used to highlight the representative in a
-        // different way
-        gg.fillRect(0, (i - starty) * avcharHeight, getSize().width,
-                avcharHeight);
-        gg.setColor(currentTextColor);
+    // hardwired italic IDs in applet currently
+    Font italic = new Font(av.getFont().getName(), Font.ITALIC, av
+            .getFont().getSize());
+    gg.setFont(italic);
 
-        gg.drawString(seq.getDisplayId(av.getShowJVSuffix()), 0,
-                (((i - starty) * avcharHeight) + avcharHeight)
-                        - (avcharHeight / 5));
+    /*
+     * draw repeating sequence ids until out of sequence data or
+     * out of visible space, whichever comes first
+     */
+    int ypos = hgap;
+    int row = av.getRanges().getStartRes();
+    while ((ypos <= getHeight()) && (row < maxwidth))
+    {
+      for (int i = starty; i < alheight; i++)
+      {
 
-        if (hiddenRows)
+        SequenceI s = av.getAlignment().getSequenceAt(i);
+        // gg.setFont(italic);
+        if (doHiddenCheck)
         {
-          drawMarker(i, starty, 0);
+          setHiddenFont(s);
         }
+        drawIdString(gg, hiddenRows, s, i, 0, ypos);
       }
+
+      if (labels != null)
+      {
+        gg.translate(0, ypos + (alheight * avcharHeight));
+        labels.drawComponent(gg, getSize().width);
+        gg.translate(0, -ypos - (alheight * avcharHeight));
+      }
+      ypos += cHeight;
+      row += rowSize;
     }
   }
 
@@ -398,16 +416,25 @@ public class IdCanvas extends Panel implements ViewportListenerI
     return false;
   }
 
+  /**
+   * Respond to viewport range changes (e.g. alignment panel was scrolled). 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.
+   * <p>
+   * In unwrapped mode, only responds to a vertical scroll, as horizontal scroll
+   * leaves sequence ids unchanged. In wrapped mode, only vertical scroll is
+   * provided, but it generates a change of "startres" which does require an
+   * update here.
+   */
   @Override
   public void propertyChange(PropertyChangeEvent evt)
   {
-    // Respond to viewport range changes (e.g. alignment panel was scrolled)
-    // 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 (evt.getPropertyName().equals(ViewportRanges.STARTSEQ))
+    String propertyName = evt.getPropertyName();
+    if (propertyName.equals(ViewportRanges.STARTSEQ)
+            || (av.getWrapAlignment() && propertyName
+                    .equals(ViewportRanges.STARTRES)))
     {
       fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
     }
index 1737c01..f580d85 100755 (executable)
@@ -350,9 +350,15 @@ public class ScalePanel extends Panel implements MouseMotionListener,
   @Override
   public void paint(Graphics g)
   {
-    drawScale(g, av.getRanges().getStartRes(), av.getRanges().getEndRes(),
-            getSize().width,
-            getSize().height);
+    /*
+     * shouldn't get called in wrapped mode as the scale above is
+     * drawn instead by SeqCanvas.drawNorthScale
+     */
+    if (!av.getWrapAlignment())
+    {
+      drawScale(g, av.getRanges().getStartRes(),
+              av.getRanges().getEndRes(), getSize().width, getSize().height);
+    }
   }
 
   // scalewidth will normally be screenwidth,
index a72bbaa..e4e3640 100644 (file)
@@ -616,6 +616,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     int res = 0;
     int x = evt.getX();
 
+    int startRes = av.getRanges().getStartRes();
     if (av.getWrapAlignment())
     {
 
@@ -630,7 +631,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
 
       int y = evt.getY();
       y -= hgap;
-      x -= seqCanvas.LABEL_WEST;
+      x = Math.max(0, x - seqCanvas.LABEL_WEST);
 
       int cwidth = seqCanvas.getWrappedCanvasWidth(getSize().width);
       if (cwidth < 1)
@@ -639,14 +640,15 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       }
 
       wrappedBlock = y / cHeight;
-      wrappedBlock += av.getRanges().getStartRes() / cwidth;
-
-      res = wrappedBlock * cwidth + x / av.getCharWidth();
-
+      wrappedBlock += startRes / cwidth;
+      int startOffset = startRes % cwidth; // in case start is scrolled right
+                                           // from 0
+      res = wrappedBlock * cwidth
+              + Math.min(cwidth - 1, startOffset + x / av.getCharWidth());
     }
     else
     {
-      res = (x / av.getCharWidth()) + av.getRanges().getStartRes();
+      res = (x / av.getCharWidth()) + startRes;
     }
 
     if (av.hasHiddenColumns())
index fe84012..1a58e21 100644 (file)
@@ -689,24 +689,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           break;
         }
         case KeyEvent.VK_PAGE_UP:
-          if (viewport.getWrapAlignment())
-          {
-            vpRanges.scrollUp(true);
-          }
-          else
-          {
-            vpRanges.pageUp();
-          }
+          vpRanges.pageUp();
           break;
         case KeyEvent.VK_PAGE_DOWN:
-          if (viewport.getWrapAlignment())
-          {
-            vpRanges.scrollUp(false);
-          }
-          else
-          {
-            vpRanges.pageDown();
-          }
+          vpRanges.pageDown();
           break;
         }
       }
index 395f6b3..9d21a6c 100644 (file)
@@ -629,21 +629,24 @@ public class AlignmentPanel extends GAlignmentPanel implements
       annotationSpaceFillerHolder.setVisible(true);
     }
 
-    if (wrap)
-    {
-      int widthInRes = getSeqPanel().seqCanvas
-              .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth());
-      vpRanges.setViewportWidth(widthInRes);
-    }
-    else
-    {
-      int widthInRes = (getSeqPanel().seqCanvas.getWidth() / av
-              .getCharWidth()) - 1;
-      int heightInSeq = (getSeqPanel().seqCanvas.getHeight() / av
-              .getCharHeight()) - 1;
+    int canvasWidth = getSeqPanel().seqCanvas.getWidth();
+    if (canvasWidth > 0)
+    { // may not yet be laid out
+      if (wrap)
+      {
+        int widthInRes = getSeqPanel().seqCanvas
+                .getWrappedCanvasWidth(canvasWidth);
+        vpRanges.setViewportWidth(widthInRes);
+      }
+      else
+      {
+        int widthInRes = (canvasWidth / av.getCharWidth()) - 1;
+        int heightInSeq = (getSeqPanel().seqCanvas.getHeight() / av
+                .getCharHeight()) - 1;
 
-      vpRanges.setViewportWidth(widthInRes);
-      vpRanges.setViewportHeight(heightInSeq);
+        vpRanges.setViewportWidth(widthInRes);
+        vpRanges.setViewportHeight(heightInSeq);
+      }
     }
 
     idSpaceFillerPanel1.setVisible(!wrap);
@@ -735,97 +738,111 @@ public class AlignmentPanel extends GAlignmentPanel implements
   @Override
   public void adjustmentValueChanged(AdjustmentEvent evt)
   {
-    int oldX = vpRanges.getStartRes();
-    int oldwidth = vpRanges.getViewportWidth();
-    int oldY = vpRanges.getStartSeq();
-    int oldheight = vpRanges.getViewportHeight();
-
     if (av.getWrapAlignment())
     {
-      if (evt.getSource() == hscroll)
-      {
-        return; // no horizontal scroll when wrapped
-      }
-      else if (evt.getSource() == vscroll)
+      adjustScrollingWrapped(evt);
+      return;
+    }
+
+    if (evt.getSource() == hscroll)
+    {
+      int oldX = vpRanges.getStartRes();
+      int oldwidth = vpRanges.getViewportWidth();
+      int x = hscroll.getValue();
+      int width = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth();
+
+      // if we're scrolling to the position we're already at, stop
+      // this prevents infinite recursion of events when the scroll/viewport
+      // ranges values are the same
+      if ((x == oldX) && (width == oldwidth))
       {
-        int offy = vscroll.getValue();
-        int rowSize = getSeqPanel().seqCanvas
-                .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth());
-
-        // if we're scrolling to the position we're already at, stop
-        // this prevents infinite recursion of events when the scroll/viewport
-        // ranges values are the same
-        if ((offy * rowSize == oldX) && (oldwidth == rowSize))
-        {
-          return;
-        }
-        else if (offy > -1)
-        {
-          vpRanges.setViewportStartAndWidth(offy * rowSize, rowSize);
-        }
+        return;
       }
-      else
+      vpRanges.setViewportStartAndWidth(x, width);
+    }
+    else if (evt.getSource() == vscroll)
+    {
+      int oldY = vpRanges.getStartSeq();
+      int oldheight = vpRanges.getViewportHeight();
+      int y = vscroll.getValue();
+      int height = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight();
+
+      // if we're scrolling to the position we're already at, stop
+      // this prevents infinite recursion of events when the scroll/viewport
+      // ranges values are the same
+      if ((y == oldY) && (height == oldheight))
       {
-        // This is only called if file loaded is a jar file that
-        // was wrapped when saved and user has wrap alignment true
-        // as preference setting
-        SwingUtilities.invokeLater(new Runnable()
-        {
-          @Override
-          public void run()
-        {
-            // When updating scrolling to use ViewportChange events, this code
-            // could not be validated and it is not clear if it is now being
-            // called. Log warning here in case it is called and unforeseen
-            // problems occur
-            Cache.log
-                    .warn("Unexpected path through code: Wrapped jar file opened with wrap alignment set in preferences");
-
-            // scroll to start of panel
-            vpRanges.setStartRes(0);
-            vpRanges.setStartSeq(0);
-          }
-        });
+        return;
       }
+      vpRanges.setViewportStartAndHeight(y, height);
+    }
+    if (!fastPaint)
+    {
       repaint();
     }
-    else
+  }
+
+  /**
+   * Responds to a scroll change by setting the start position of the viewport.
+   * Does
+   * 
+   * @param evt
+   */
+  protected void adjustScrollingWrapped(AdjustmentEvent evt)
+  {
+    if (evt.getSource() == hscroll)
     {
-      // horizontal scroll
-      if (evt.getSource() == hscroll)
-      {
-        int x = hscroll.getValue();
-        int width = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth();
+      return; // no horizontal scroll when wrapped
+    }
+    if (evt.getSource() == vscroll)
+    {
+      int newY = vscroll.getValue();
 
-        // if we're scrolling to the position we're already at, stop
-        // this prevents infinite recursion of events when the scroll/viewport
-        // ranges values are the same
-        if ((x == oldX) && (width == oldwidth))
-        {
-          return;
-        }
-        vpRanges.setViewportStartAndWidth(x, width);
-      }
-      else if (evt.getSource() == vscroll)
+      /*
+       * if we're scrolling to the position we're already at, stop
+       * this prevents infinite recursion of events when the scroll/viewport
+       * ranges values are the same
+       */
+      int oldX = vpRanges.getStartRes();
+      int oldY = vpRanges.getWrappedScrollPosition(oldX);
+      if (oldY == newY)
       {
-        int y = vscroll.getValue();
-        int height = getSeqPanel().seqCanvas.getHeight()
-                / av.getCharHeight();
-
-        // if we're scrolling to the position we're already at, stop
-        // this prevents infinite recursion of events when the scroll/viewport
-        // ranges values are the same
-        if ((y == oldY) && (height == oldheight))
-        {
-          return;
-        }
-        vpRanges.setViewportStartAndHeight(y, height);
+        return;
       }
-      if (!fastPaint)
+      else if (newY > -1)
       {
-        repaint();
+        /*
+         * limit page up/down to one width's worth of positions
+         */
+        int rowSize = vpRanges.getViewportWidth();
+        int newX = newY > oldY ? oldX + rowSize : oldX - rowSize;
+        vpRanges.setViewportStartAndWidth(newX, rowSize);
       }
     }
+    else
+    {
+      // This is only called if file loaded is a jar file that
+      // was wrapped when saved and user has wrap alignment true
+      // as preference setting
+      SwingUtilities.invokeLater(new Runnable()
+      {
+        @Override
+        public void run()
+      {
+          // When updating scrolling to use ViewportChange events, this code
+          // could not be validated and it is not clear if it is now being
+          // called. Log warning here in case it is called and unforeseen
+          // problems occur
+          Cache.log
+                  .warn("Unexpected path through code: Wrapped jar file opened with wrap alignment set in preferences");
+
+          // scroll to start of panel
+          vpRanges.setStartRes(0);
+          vpRanges.setStartSeq(0);
+        }
+      });
+    }
+    repaint();
   }
 
   /**
@@ -875,35 +892,21 @@ public class AlignmentPanel extends GAlignmentPanel implements
     setScrollValues(vpRanges.getStartRes(), vpRanges.getStartSeq());
   }
 
-  /*
-   * Set vertical scroll bar parameters for wrapped panel
-   * @param res 
-   *    the residue to scroll to
+  /**
+   * Set vertical scroll bar position, and number of increments, for wrapped
+   * panel
+   * 
+   * @param topLeftColumn
+   *          the column position at top left (0..)
    */
-  private void setScrollingForWrappedPanel(int res)
+  private void setScrollingForWrappedPanel(int topLeftColumn)
   {
-    // get the width of the alignment in residues
-    int maxwidth = av.getAlignment().getWidth();
-    if (av.hasHiddenColumns())
-    {
-        maxwidth = av.getAlignment().getHiddenColumns()
-                .findColumnPosition(maxwidth) - 1;
-    }
+    int scrollPosition = vpRanges.getWrappedScrollPosition(topLeftColumn);
+    int maxScroll = vpRanges.getWrappedScrollPosition(vpRanges
+            .getVisibleAlignmentWidth() - 1);
 
-    // get the width of the canvas in residues
-    int canvasWidth = getSeqPanel().seqCanvas
-            .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth());
-    if (canvasWidth > 0)
-    {
-      // position we want to scroll to is number of canvasWidth's to get there
-      int current = res / canvasWidth;
-
-      // max scroll position: add one because extent is 1 and scrollbar value
-      // can only be set to at most max - extent
-      int max = maxwidth / canvasWidth + 1;
-      vscroll.setUnitIncrement(1);
-      vscroll.setValues(current, 1, 0, max);
-    }
+    vscroll.setUnitIncrement(1);
+    vscroll.setValues(scrollPosition, 1, 0, maxScroll);
   }
 
   /**
index 4642741..052c527 100755 (executable)
@@ -155,7 +155,10 @@ public class IdCanvas extends JPanel implements ViewportListenerI
    */
   public void fastPaint(int vertical)
   {
-    if (gg == null)
+    /*
+     * for now, not attempting fast paint of wrapped ids...
+     */
+    if (gg == null || av.getWrapAlignment())
     {
       repaint();
 
@@ -283,143 +286,158 @@ public class IdCanvas extends JPanel implements ViewportListenerI
     Color currentColor = Color.white;
     Color currentTextColor = Color.black;
 
-    final boolean doHiddenCheck = av.isDisplayReferenceSeq()
-            || av.hasHiddenRows(), hiddenRows = av.hasHiddenRows();
+    boolean hasHiddenRows = av.hasHiddenRows();
 
     if (av.getWrapAlignment())
     {
-      int maxwidth = av.getAlignment().getWidth();
-      int alheight = av.getAlignment().getHeight();
+      drawIdsWrapped(starty, hasHiddenRows);
+      return;
+    }
 
-      if (av.hasHiddenColumns())
-      {
-        maxwidth = av.getAlignment().getHiddenColumns()
-                .findColumnPosition(maxwidth) - 1;
-      }
+    // No need to hang on to labels if we're not wrapped
+    labels = null;
 
-      int annotationHeight = 0;
+    // Now draw the id strings
+    int panelWidth = getWidth();
+    int xPos = 0;
+
+    SequenceI sequence;
+    // Now draw the id strings
+    for (int i = starty; i <= endy; i++)
+    {
+      sequence = av.getAlignment().getSequenceAt(i);
 
-      if (av.isShowAnnotation())
+      if (sequence == null)
       {
-        if (ap == null)
-        {
-          ap = new AnnotationPanel(av);
-        }
+        continue;
+      }
 
-        annotationHeight = ap.adjustPanelHeight();
-        if (labels == null)
-        {
-          labels = new AnnotationLabels(av);
-        }
+      if (hasHiddenRows || av.isDisplayReferenceSeq())
+      {
+        setHiddenFont(sequence);
       }
 
-      int hgap = av.getCharHeight();
-      if (av.getScaleAboveWrapped())
+      // Selected sequence colours
+      if ((searchResults != null) && searchResults.contains(sequence))
       {
-        hgap += av.getCharHeight();
+        currentColor = Color.black;
+        currentTextColor = Color.white;
       }
+      else if ((av.getSelectionGroup() != null)
+              && av.getSelectionGroup().getSequences(null)
+                      .contains(sequence))
+      {
+        currentColor = Color.lightGray;
+        currentTextColor = Color.black;
+      }
+      else
+      {
+        currentColor = av.getSequenceColour(sequence);
+        currentTextColor = Color.black;
+      }
+
+      gg.setColor(currentColor);
+
+      gg.fillRect(0, (i - starty) * av.getCharHeight(), getWidth(),
+              av.getCharHeight());
 
-      int cHeight = alheight * av.getCharHeight() + hgap + annotationHeight;
+      gg.setColor(currentTextColor);
 
-      int rowSize = av.getRanges().getEndRes()
-              - av.getRanges().getStartRes();
+      String string = sequence.getDisplayId(av.getShowJVSuffix());
 
-      // Draw the rest of the panels
-      for (int ypos = hgap, row = av.getRanges().getStartRes(); (ypos <= getHeight())
-              && (row < maxwidth); ypos += cHeight, row += rowSize)
+      if (av.isRightAlignIds())
       {
-        for (int i = starty; i < alheight; i++)
-        {
-          SequenceI s = av.getAlignment().getSequenceAt(i);
-          if (doHiddenCheck)
-          {
-            setHiddenFont(s);
-          }
-          else
-          {
-            gg.setFont(getIdfont());
-          }
-
-          drawIdString(gg, hiddenRows, s, i, 0, ypos);
-        }
+        xPos = panelWidth - fm.stringWidth(string) - 4;
+      }
 
-        if (labels != null && av.isShowAnnotation())
-        {
-          gg.translate(0, ypos + (alheight * av.getCharHeight()));
-          labels.drawComponent(gg, getWidth());
-          gg.translate(0, -ypos - (alheight * av.getCharHeight()));
-        }
+      gg.drawString(string, xPos,
+              (((i - starty) * av.getCharHeight()) + av.getCharHeight())
+                      - (av.getCharHeight() / 5));
+
+      if (hasHiddenRows)
+      {
+        drawMarker(i, starty, 0);
       }
     }
-    else
-    {
-      // No need to hang on to labels if we're not wrapped
-      labels = null;
+  }
 
-      // Now draw the id strings
-      int panelWidth = getWidth();
-      int xPos = 0;
+  /**
+   * Draws sequence ids in wrapped mode
+   * 
+   * @param starty
+   * @param hasHiddenRows
+   */
+  protected void drawIdsWrapped(int starty, boolean hasHiddenRows)
+  {
+    int maxwidth = av.getAlignment().getWidth();
+    int alheight = av.getAlignment().getHeight();
 
-      SequenceI sequence;
-      // Now draw the id strings
-      for (int i = starty; i <= endy; i++)
-      {
-        sequence = av.getAlignment().getSequenceAt(i);
+    if (av.hasHiddenColumns())
+    {
+      maxwidth = av.getAlignment().getHiddenColumns()
+              .findColumnPosition(maxwidth) - 1;
+    }
 
-        if (sequence == null)
-        {
-          continue;
-        }
+    int annotationHeight = 0;
 
-        if (doHiddenCheck)
-        {
-          setHiddenFont(sequence);
-        }
+    if (av.isShowAnnotation())
+    {
+      if (ap == null)
+      {
+        ap = new AnnotationPanel(av);
+      }
 
-        // Selected sequence colours
-        if ((searchResults != null) && searchResults.contains(sequence))
-        {
-          currentColor = Color.black;
-          currentTextColor = Color.white;
-        }
-        else if ((av.getSelectionGroup() != null)
-                && av.getSelectionGroup().getSequences(null)
-                        .contains(sequence))
-        {
-          currentColor = Color.lightGray;
-          currentTextColor = Color.black;
-        }
-        else
-        {
-          currentColor = av.getSequenceColour(sequence);
-          currentTextColor = Color.black;
-        }
+      annotationHeight = ap.adjustPanelHeight();
+      if (labels == null)
+      {
+        labels = new AnnotationLabels(av);
+      }
+    }
 
-        gg.setColor(currentColor);
+    int hgap = av.getCharHeight();
+    if (av.getScaleAboveWrapped())
+    {
+      hgap += av.getCharHeight();
+    }
 
-        gg.fillRect(0, (i - starty) * av.getCharHeight(), getWidth(),
-                av.getCharHeight());
+    int cHeight = alheight * av.getCharHeight() + hgap + annotationHeight;
 
-        gg.setColor(currentTextColor);
+    ViewportRanges ranges = av.getRanges();
 
-        String string = sequence.getDisplayId(av.getShowJVSuffix());
+    int rowSize = ranges.getViewportWidth();
 
-        if (av.isRightAlignIds())
+    /*
+     * draw repeating sequence ids until out of sequence data or
+     * out of visible space, whichever comes first
+     */
+    int ypos = hgap;
+    int row = ranges.getStartRes();
+    while ((ypos <= getHeight()) && (row < maxwidth))
+    {
+      for (int i = starty; i < alheight; i++)
+      {
+        SequenceI s = av.getAlignment().getSequenceAt(i);
+        if (hasHiddenRows || av.isDisplayReferenceSeq())
         {
-          xPos = panelWidth - fm.stringWidth(string) - 4;
+          setHiddenFont(s);
         }
-
-        gg.drawString(string, xPos,
-                (((i - starty) * av.getCharHeight()) + av.getCharHeight())
-                        - (av.getCharHeight() / 5));
-
-        if (hiddenRows)
+        else
         {
-          drawMarker(i, starty, 0);
+          gg.setFont(getIdfont());
         }
 
+        drawIdString(gg, hasHiddenRows, s, i, 0, ypos);
+      }
+
+      if (labels != null && av.isShowAnnotation())
+      {
+        gg.translate(0, ypos + (alheight * av.getCharHeight()));
+        labels.drawComponent(gg, getWidth());
+        gg.translate(0, -ypos - (alheight * av.getCharHeight()));
       }
 
+      ypos += cHeight;
+      row += rowSize;
     }
   }
 
@@ -520,16 +538,25 @@ public class IdCanvas extends JPanel implements ViewportListenerI
     this.idfont = idfont;
   }
 
+  /**
+   * Respond to viewport range changes (e.g. alignment panel was scrolled). 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.
+   * <p>
+   * In unwrapped mode, only responds to a vertical scroll, as horizontal scroll
+   * leaves sequence ids unchanged. In wrapped mode, only vertical scroll is
+   * provided, but it generates a change of "startres" which does require an
+   * update here.
+   */
   @Override
   public void propertyChange(PropertyChangeEvent evt)
   {
-    // Respond to viewport range changes (e.g. alignment panel was scrolled)
-    // 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 (evt.getPropertyName().equals(ViewportRanges.STARTSEQ))
+    String propertyName = evt.getPropertyName();
+    if (propertyName.equals(ViewportRanges.STARTSEQ)
+            || (av.getWrapAlignment() && propertyName
+                    .equals(ViewportRanges.STARTRES)))
     {
       fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
     }
index 6097089..065f97e 100755 (executable)
@@ -138,7 +138,7 @@ public class IdPanel extends JPanel implements MouseListener,
     }
 
     lastid = seq;
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(false);
   }
 
   /**
@@ -313,7 +313,7 @@ public class IdPanel extends JPanel implements MouseListener,
 
     av.isSelectionGroupChanged(true);
 
-    alignPanel.paintAlignment(true);
+    alignPanel.paintAlignment(false);
   }
 
   /**
index 3fa674e..62e7a68 100755 (executable)
@@ -107,13 +107,11 @@ public class OverviewPanel extends JPanel implements Runnable,
       @Override
       public void mouseDragged(MouseEvent evt)
       {
-        if (!SwingUtilities.isRightMouseButton(evt)
-                && !av.getWrapAlignment())
+        if (!SwingUtilities.isRightMouseButton(evt))
         {
           od.updateViewportFromMouse(evt.getX(), evt.getY(), av
                   .getAlignment().getHiddenSequences(), av.getAlignment()
                   .getHiddenColumns());
-
         }
       }
     });
@@ -130,7 +128,8 @@ public class OverviewPanel extends JPanel implements Runnable,
             showPopupMenu(evt);
           }
         }
-        else if (!av.getWrapAlignment())
+        else
+        // if (!av.getWrapAlignment())
         {
           od.updateViewportFromMouse(evt.getX(), evt.getY(), av
                   .getAlignment().getHiddenSequences(), av.getAlignment()
index e5f2be8..ec03681 100755 (executable)
@@ -422,8 +422,15 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
   @Override
   public void paintComponent(Graphics g)
   {
-    drawScale(g, av.getRanges().getStartRes(), av.getRanges().getEndRes(),
-            getWidth(), getHeight());
+    /*
+     * shouldn't get called in wrapped mode as the scale above is
+     * drawn instead by SeqCanvas.drawNorthScale
+     */
+    if (!av.getWrapAlignment())
+    {
+      drawScale(g, av.getRanges().getStartRes(),
+              av.getRanges().getEndRes(), getWidth(), getHeight());
+    }
   }
 
   // scalewidth will normally be screenwidth,
index f392810..5cfa8c0 100755 (executable)
@@ -990,7 +990,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))
index 7651eb4..6fbed49 100644 (file)
@@ -189,6 +189,7 @@ public class SeqPanel extends JPanel implements MouseListener,
     int res = 0;
     int x = evt.getX();
 
+    int startRes = av.getRanges().getStartRes();
     if (av.getWrapAlignment())
     {
 
@@ -203,7 +204,7 @@ public class SeqPanel extends JPanel implements MouseListener,
 
       int y = evt.getY();
       y -= hgap;
-      x -= seqCanvas.LABEL_WEST;
+      x = Math.max(0, x - seqCanvas.LABEL_WEST);
 
       int cwidth = seqCanvas.getWrappedCanvasWidth(this.getWidth());
       if (cwidth < 1)
@@ -212,10 +213,11 @@ public class SeqPanel extends JPanel implements MouseListener,
       }
 
       wrappedBlock = y / cHeight;
-      wrappedBlock += av.getRanges().getStartRes() / cwidth;
-
-      res = wrappedBlock * cwidth + x / av.getCharWidth();
-
+      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());
     }
     else
     {
@@ -225,7 +227,7 @@ public class SeqPanel extends JPanel implements MouseListener,
         // right-hand gutter
         x = seqCanvas.getX() + seqCanvas.getWidth();
       }
-      res = (x / av.getCharWidth()) + av.getRanges().getStartRes();
+      res = (x / av.getCharWidth()) + startRes;
       if (res > av.getRanges().getEndRes())
       {
         // moused off right
index 9a08a2b..1e094fd 100644 (file)
@@ -405,6 +405,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   public void setWrapAlignment(boolean state)
   {
     viewStyle.setWrapAlignment(state);
+    ranges.setWrappedMode(state);
   }
 
   /**
index d2912d8..7ac07ac 100644 (file)
@@ -231,7 +231,8 @@ public abstract class OverviewDimensions
     resetAlignmentDims();
 
     // boxX, boxY is the x,y location equivalent to startRes, startSeq
-    boxX = Math.round((float) startRes * width / alwidth);
+    int xPos = Math.min(startRes, alwidth - vpwidth + 1);
+    boxX = Math.round((float) xPos * width / alwidth);
     boxY = Math.round((float) startSeq * sequencesHeight / alheight);
 
     // boxWidth is the width in residues translated to pixels
index c2bcf3f..36e598f 100644 (file)
@@ -40,6 +40,8 @@ public class ViewportRanges extends ViewportProperties
 
   public static final String ENDSEQ = "endseq";
 
+  private boolean wrappedMode = false;
+
   // start residue of viewport
   private int startRes;
 
@@ -129,7 +131,11 @@ public class ViewportRanges extends ViewportProperties
   public void setStartEndRes(int start, int end)
   {
     int oldstartres = this.startRes;
-    if (start > getVisibleAlignmentWidth() - 1)
+
+    /*
+     * if not wrapped, don't leave white space at the right margin
+     */
+    if (!wrappedMode && (start > getVisibleAlignmentWidth() - 1))
     {
       startRes = Math.max(getVisibleAlignmentWidth() - 1, 0);
     }
@@ -147,7 +153,7 @@ public class ViewportRanges extends ViewportProperties
     {
       endRes = 0;
     }
-    else if (end > getVisibleAlignmentWidth() - 1)
+    else if (!wrappedMode && (end > getVisibleAlignmentWidth() - 1))
     {
       endRes = Math.max(getVisibleAlignmentWidth() - 1, 0);
     }
@@ -175,9 +181,16 @@ public class ViewportRanges extends ViewportProperties
   {
     int startres = res;
     int width = getViewportWidth();
-    if (startres + width - 1 > getVisibleAlignmentWidth() - 1)
+
+    /*
+     * if not wrapped, don't leave white space at the right margin
+     */
+    if (!wrappedMode)
     {
-      startres = getVisibleAlignmentWidth() - width;
+      if (startres + width - 1 > getVisibleAlignmentWidth() - 1)
+      {
+        startres = getVisibleAlignmentWidth() - width;
+      }
     }
     setStartEndRes(startres - width + 1, startres);
   }
@@ -338,12 +351,18 @@ public class ViewportRanges extends ViewportProperties
     {
       vpstart = 0;
     }
-    else if ((w <= getVisibleAlignmentWidth())
-            && (vpstart + w - 1 > getVisibleAlignmentWidth() - 1))
-    // viewport width is less than the full alignment and we are running off the
-    // RHS edge
+
+    /*
+     * if not wrapped, don't leave white space at the right margin
+     */
+    if (!wrappedMode)
     {
-      vpstart = getVisibleAlignmentWidth() - w;
+      if ((w <= getVisibleAlignmentWidth())
+              && (vpstart + w - 1 > getVisibleAlignmentWidth() - 1))
+      {
+        vpstart = getVisibleAlignmentWidth() - w;
+      }
+
     }
     setStartEndRes(vpstart, vpstart + w - 1);
   }
@@ -514,7 +533,15 @@ public class ViewportRanges extends ViewportProperties
    */
   public void pageUp()
   {
-    setViewportStartAndHeight(2 * startSeq - endSeq, getViewportHeight());
+    if (wrappedMode)
+    {
+      setStartRes(Math.max(0, getStartRes() - getViewportWidth()));
+    }
+    else
+    {
+      setViewportStartAndHeight(startSeq - (endSeq - startSeq),
+              getViewportHeight());
+    }
   }
   
   /**
@@ -522,6 +549,63 @@ public class ViewportRanges extends ViewportProperties
    */
   public void pageDown()
   {
-    setViewportStartAndHeight(endSeq, getViewportHeight());
+    if (wrappedMode)
+    {
+      /*
+       * if height is more than width (i.e. not all sequences fit on screen),
+       * increase page down to height
+       */
+      int newStart = getStartRes()
+              + Math.max(getViewportHeight(), getViewportWidth());
+
+      /*
+       * don't page down beyond end of alignment, or if not all
+       * sequences fit in the visible height
+       */
+      if (newStart < getVisibleAlignmentWidth())
+      {
+        setStartRes(newStart);
+      }
+    }
+    else
+    {
+      setViewportStartAndHeight(endSeq, getViewportHeight());
+    }
+  }
+
+  public void setWrappedMode(boolean wrapped)
+  {
+    wrappedMode = wrapped;
+  }
+
+  /**
+   * Answers the vertical scroll position (0..) to set, given the visible column
+   * that is at top left. Note that if called with the total visible width of
+   * the alignment, this gives the maximum cursor scroll value.
+   * 
+   * <pre>
+   * Example:
+   *    viewport width 40 columns (0-39, 40-79, 80-119...)
+   *    column 0 returns scroll position 0
+   *    columns 0-40 return scroll position 1
+   *    columns 41-80 return scroll position 2
+   *    etc
+   * </pre>
+   * 
+   * @param topLeftColumn
+   *          (0..)
+   * @return
+   */
+  public int getWrappedScrollPosition(final int topLeftColumn)
+  {
+    int w = getViewportWidth();
+
+    /*
+     * visible whole widths
+     */
+    int scroll = topLeftColumn / w;
+    scroll += topLeftColumn % w > 0 ? 1 : 0;
+
+    return scroll;
   }
 }
index 636f8dd..4c93d71 100644 (file)
@@ -523,6 +523,39 @@ public class ViewportRangesTest {
     assertTrue(l.verify(1, Arrays.asList("startres")));
     l.reset();
   }
+
+  @Test(groups = { "Functional" })
+  public void testGetWrappedScrollPosition()
+  {
+    AlignmentI al2 = gen.generate(157, 15, 1, 5, 5);
+    ViewportRanges vr = new ViewportRanges(al2);
+    vr.setStartEndRes(0, 39);
+    int width = vr.getViewportWidth(); // 40
+
+    /*
+     * scroll is 0 at column 0 (only)
+     */
+    assertEquals(vr.getWrappedScrollPosition(0), 0);
+
+    /*
+     * scroll is 1 at columns 1-40
+     */
+    int i = 1;
+    int j = width;
+    for (; i <= j; i++)
+    {
+      assertEquals(1, vr.getWrappedScrollPosition(i));
+    }
+
+    /*
+     * scroll is 2 at columns 41-80, etc
+     */
+    j += width;
+    for (; i <= j; i++)
+    {
+      assertEquals(2, vr.getWrappedScrollPosition(i), "For " + i);
+    }
+  }
 }
 
 // mock listener for property change events