Merge branch 'bug/JAL-2491' into develop
[jalview.git] / src / jalview / gui / AlignmentPanel.java
index 885d79d..ce9e989 100644 (file)
@@ -36,6 +36,7 @@ import jalview.schemes.ResidueProperties;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
+import jalview.viewmodel.ViewportListenerI;
 import jalview.viewmodel.ViewportRanges;
 
 import java.awt.BorderLayout;
@@ -48,6 +49,8 @@ import java.awt.Graphics;
 import java.awt.Insets;
 import java.awt.event.AdjustmentEvent;
 import java.awt.event.AdjustmentListener;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
 import java.awt.print.PageFormat;
 import java.awt.print.Printable;
 import java.awt.print.PrinterException;
@@ -67,7 +70,8 @@ import javax.swing.SwingUtilities;
  * @version $Revision: 1.161 $
  */
 public class AlignmentPanel extends GAlignmentPanel implements
-        AdjustmentListener, Printable, AlignmentViewPanel
+        AdjustmentListener, Printable, AlignmentViewPanel,
+        ViewportListenerI
 {
   public AlignViewport av;
 
@@ -101,9 +105,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
   /*
    * Flag set while scrolling to follow complementary cDNA/protein scroll. When
-   * true, suppresses invoking the same method recursively.
+   * false, suppresses invoking the same method recursively.
    */
-  private boolean dontScrollComplement;
+  private boolean scrollComplementaryPanel = true;
 
   private PropertyChangeListener propertyChangeListener;
 
@@ -143,6 +147,34 @@ public class AlignmentPanel extends GAlignmentPanel implements
     hscroll.addAdjustmentListener(this);
     vscroll.addAdjustmentListener(this);
 
+    addComponentListener(new ComponentAdapter()
+    {
+      @Override
+      public void componentResized(ComponentEvent evt)
+      {
+        // reset the viewport ranges when the alignment panel is resized
+        // in particular, this initialises the end residue value when Jalview
+        // is initialised
+        if (av.getWrapAlignment())
+        {
+          int widthInRes = getSeqPanel().seqCanvas
+                  .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth());
+          vpRanges.setViewportWidth(widthInRes);
+        }
+        else
+        {
+          int widthInRes = getSeqPanel().seqCanvas.getWidth()
+                  / av.getCharWidth();
+          int heightInSeq = getSeqPanel().seqCanvas.getHeight()
+                  / av.getCharHeight();
+          
+          vpRanges.setViewportWidth(widthInRes);
+          vpRanges.setViewportHeight(heightInSeq);
+        }
+      }
+
+    });
+
     final AlignmentPanel ap = this;
     propertyChangeListener = new PropertyChangeListener()
     {
@@ -157,6 +189,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
       }
     };
     av.addPropertyChangeListener(propertyChangeListener);
+
+    av.getRanges().addPropertyChangeListener(this);
     fontChanged();
     adjustAnnotationHeight();
     updateLayout();
@@ -207,10 +241,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
     getIdPanel().getIdCanvas().setPreferredSize(d);
     hscrollFillerPanel.setPreferredSize(d);
 
-    if (overviewPanel != null)
-    {
-      overviewPanel.setBoxPosition();
-    }
     if (this.alignFrame.getSplitViewContainer() != null)
     {
       ((SplitFrame) this.alignFrame.getSplitViewContainer()).adjustLayout();
@@ -379,9 +409,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
       }
       int start = r[0];
       int end = r[1];
-      // DEBUG
-      // System.err.println(this.av.viewName + " Seq : " + seqIndex
-      // + " Scroll to " + start + "," + end);
 
       /*
        * To centre results, scroll to positions half the visible width
@@ -421,9 +448,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
        */
       seqIndex = Math.max(0, seqIndex - verticalOffset);
 
-      // System.out.println("start=" + start + ", end=" + end + ", startv="
-      // + av.getStartRes() + ", endv=" + av.getEndRes() + ", starts="
-      // + av.getStartSeq() + ", ends=" + av.getEndSeq());
       if (!av.getWrapAlignment())
       {
         if ((startv = vpRanges.getStartRes()) >= start)
@@ -431,7 +455,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
           /*
            * Scroll left to make start of search results visible
            */
-          // setScrollValues(start - 1, seqIndex); // plus one residue
           setScrollValues(start, seqIndex);
         }
         else if ((endv = vpRanges.getEndRes()) <= end)
@@ -439,7 +462,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
           /*
            * Scroll right to make end of search results visible
            */
-          // setScrollValues(startv + 1 + end - endv, seqIndex); // plus one
           setScrollValues(startv + end - endv, seqIndex);
         }
         else if ((starts = vpRanges.getStartSeq()) > seqIndex)
@@ -463,30 +485,14 @@ public class AlignmentPanel extends GAlignmentPanel implements
       }
       else
       {
-        scrollToWrappedVisible(start);
+        vpRanges.scrollToWrappedVisible(start);
       }
     }
-    if (redrawOverview && overviewPanel != null)
-    {
-      overviewPanel.setBoxPosition();
-    }
+
     paintAlignment(redrawOverview);
     return true;
   }
 
-  void scrollToWrappedVisible(int res)
-  {
-    int cwidth = getSeqPanel().seqCanvas
-            .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth());
-    if (res < vpRanges.getStartRes()
-            || res >= (vpRanges.getStartRes() + cwidth))
-    {
-      vscroll.setValue((res / cwidth));
-      vpRanges.setStartRes(vscroll.getValue() * cwidth);
-    }
-
-  }
-
   /**
    * DOCUMENT ME!
    * 
@@ -622,75 +628,28 @@ public class AlignmentPanel extends GAlignmentPanel implements
       annotationSpaceFillerHolder.setVisible(true);
     }
 
-    idSpaceFillerPanel1.setVisible(!wrap);
-
-    repaint();
-  }
-
-  // return value is true if the scroll is valid
-  public boolean scrollUp(boolean up)
-  {
-    if (up)
+    if (wrap)
     {
-      if (vscroll.getValue() < 1)
-      {
-        return false;
-      }
-
-      fastPaint = false;
-      vscroll.setValue(vscroll.getValue() - 1);
+      int widthInRes = getSeqPanel().seqCanvas
+              .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth());
+      vpRanges.setViewportWidth(widthInRes);
     }
     else
     {
-      if ((vextent + vscroll.getValue()) >= av.getAlignment().getHeight())
-      {
-        return false;
-      }
+      int widthInRes = (getSeqPanel().seqCanvas.getWidth() / av
+              .getCharWidth()) - 1;
+      int heightInSeq = (getSeqPanel().seqCanvas.getHeight() / av
+              .getCharHeight()) - 1;
 
-      fastPaint = false;
-      vscroll.setValue(vscroll.getValue() + 1);
+      vpRanges.setViewportWidth(widthInRes);
+      vpRanges.setViewportHeight(heightInSeq);
     }
 
-    fastPaint = true;
+    idSpaceFillerPanel1.setVisible(!wrap);
 
-    return true;
+    repaint();
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param right
-   *          DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public boolean scrollRight(boolean right)
-  {
-    if (!right)
-    {
-      if (hscroll.getValue() < 1)
-      {
-        return false;
-      }
-
-      fastPaint = false;
-      hscroll.setValue(hscroll.getValue() - 1);
-    }
-    else
-    {
-      if ((hextent + hscroll.getValue()) >= av.getAlignment().getWidth())
-      {
-        return false;
-      }
-
-      fastPaint = false;
-      hscroll.setValue(hscroll.getValue() + 1);
-    }
-
-    fastPaint = true;
-
-    return true;
-  }
 
   /**
    * Adjust row/column scrollers to show a visible position in the alignment.
@@ -701,176 +660,170 @@ public class AlignmentPanel extends GAlignmentPanel implements
    *          visible row to scroll to
    * 
    */
-  public void setScrollValues(int x, int y)
+  public void setScrollValues(int xpos, int ypos)
   {
+    int x = xpos;
+    int y = ypos;
+
     if (av == null || av.getAlignment() == null)
     {
       return;
     }
-    int width = av.getAlignment().getWidth();
-    int height = av.getAlignment().getHeight();
 
-    if (av.hasHiddenColumns())
+    if (av.getWrapAlignment())
     {
-      // reset the width to exclude hidden columns
-      width = av.getAlignment().getHiddenColumns()
-              .findColumnPosition(width);
+      setScrollingForWrappedPanel(x);
     }
+    else
+    {
+      int width = av.getAlignment().getWidth();
+      int height = av.getAlignment().getHeight();
 
-    hextent = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth();
-    vextent = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight();
+      if (av.hasHiddenColumns())
+      {
+        // reset the width to exclude hidden columns
+        width = av.getAlignment().getHiddenColumns().findColumnPosition(width);
+      }
 
-    if (hextent > width)
-    {
-      hextent = width;
-    }
+      hextent = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth();
+      vextent = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight();
 
-    if (vextent > height)
-    {
-      vextent = height;
-    }
+      if (hextent > width)
+      {
+        hextent = width;
+      }
 
-    if ((hextent + x) > width)
-    {
-      x = width - hextent;
-    }
+      if (vextent > height)
+      {
+        vextent = height;
+      }
 
-    if ((vextent + y) > height)
-    {
-      y = height - vextent;
-    }
+      if ((hextent + x) > width)
+      {
+        x = width - hextent;
+      }
 
-    if (y < 0)
-    {
-      y = 0;
-    }
+      if ((vextent + y) > height)
+      {
+        y = height - vextent;
+      }
 
-    if (x < 0)
-    {
-      x = 0;
-    }
+      if (y < 0)
+      {
+        y = 0;
+      }
 
-    // update endRes after x has (possibly) been adjusted
-    vpRanges.setEndRes((x + (getSeqPanel().seqCanvas.getWidth() / av
-            .getCharWidth())) - 1);
+      if (x < 0)
+      {
+        x = 0;
+      }
 
-    /*
-     * each scroll adjustment triggers adjustmentValueChanged, which resets the
-     * 'do not scroll complement' flag; ensure it is the same for both
-     * operations
-     */
-    boolean flag = isDontScrollComplement();
-    hscroll.setValues(x, hextent, 0, width);
-    setDontScrollComplement(flag);
-    vscroll.setValues(y, vextent, 0, height);
+      // update the scroll values
+      hscroll.setValues(x, hextent, 0, width);
+      vscroll.setValues(y, vextent, 0, height);
+    }
   }
 
   /**
-   * DOCUMENT ME!
+   * Respond to adjustment event when horizontal or vertical scrollbar is
+   * changed
    * 
    * @param evt
-   *          DOCUMENT ME!
+   *          adjustment event encoding whether hscroll or vscroll changed
    */
   @Override
   public void adjustmentValueChanged(AdjustmentEvent evt)
   {
     int oldX = vpRanges.getStartRes();
+    int oldwidth = vpRanges.getViewportWidth();
     int oldY = vpRanges.getStartSeq();
+    int oldheight = vpRanges.getViewportHeight();
 
-    if (evt.getSource() == hscroll)
-    {
-      int x = hscroll.getValue();
-      vpRanges.setStartRes(x);
-      vpRanges.setEndRes((x + (getSeqPanel().seqCanvas.getWidth() / av
-              .getCharWidth())) - 1);
-    }
-
-    if (evt.getSource() == vscroll)
+    if (av.getWrapAlignment())
     {
-      int offy = vscroll.getValue();
-
-      if (av.getWrapAlignment())
+      if (evt.getSource() == hscroll)
       {
-        if (offy > -1)
+        return; // no horizontal scroll when wrapped
+      }
+      else if (evt.getSource() == vscroll)
+      {
+        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))
         {
-          int rowSize = getSeqPanel().seqCanvas
-                  .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth());
-          vpRanges.setStartRes(offy * rowSize);
-          vpRanges.setEndRes((offy + 1) * rowSize);
+          return;
         }
-        else
+        else if (offy > -1)
         {
-          // 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()
-            {
-              setScrollValues(vpRanges.getStartRes(),
-                      vpRanges.getStartSeq());
-            }
-          });
+          vpRanges.setViewportStartAndWidth(offy * rowSize, rowSize);
         }
       }
       else
       {
-        vpRanges.setStartSeq(offy);
-        vpRanges.setEndSeq(offy
-                + (getSeqPanel().seqCanvas.getHeight() / av.getCharHeight())
-                - 1);
+        // 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);
+          }
+        });
       }
-    }
-
-    if (overviewPanel != null)
-    {
-      overviewPanel.setBoxPosition();
-    }
-
-    int scrollX = vpRanges.getStartRes() - oldX;
-    int scrollY = vpRanges.getStartSeq() - oldY;
-
-    if (av.getWrapAlignment() || !fastPaint)
-    {
       repaint();
     }
     else
     {
-      // Make sure we're not trying to draw a panel
-      // larger than the visible window
-      if (scrollX > vpRanges.getEndRes() - vpRanges.getStartRes())
+      // horizontal scroll
+      if (evt.getSource() == hscroll)
       {
-        scrollX = vpRanges.getEndRes() - vpRanges.getStartRes();
-      }
-      else if (scrollX < vpRanges.getStartRes() - vpRanges.getEndRes())
-      {
-        scrollX = vpRanges.getStartRes() - vpRanges.getEndRes();
-      }
+        int x = hscroll.getValue();
+        int width = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth();
 
-      if (scrollX != 0 || scrollY != 0)
+        // 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)
       {
-        getIdPanel().getIdCanvas().fastPaint(scrollY);
-        getSeqPanel().seqCanvas.fastPaint(scrollX, scrollY);
-        getScalePanel().repaint();
-
-        if (av.isShowAnnotation() && scrollX != 0)
+        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))
         {
-          getAnnotationPanel().fastPaint(scrollX);
+          return;
         }
+        vpRanges.setViewportStartAndHeight(y, height);
+      }
+      if (!fastPaint)
+      {
+        repaint();
       }
-    }
-    /*
-     * If there is one, scroll the (Protein/cDNA) complementary alignment to
-     * match, unless we are ourselves doing that.
-     */
-    if (isDontScrollComplement())
-    {
-      setDontScrollComplement(false);
-    }
-    else
-    {
-      av.scrollComplementaryAlignment();
     }
   }
 
@@ -916,37 +869,39 @@ public class AlignmentPanel extends GAlignmentPanel implements
     validate();
 
     /*
-     * set scroll bar positions; first suppress this being 'followed' in any
-     * complementary split pane
+     * set scroll bar positions
      */
-    setDontScrollComplement(true);
+    setScrollValues(vpRanges.getStartRes(), vpRanges.getStartSeq());
+  }
 
-    if (av.getWrapAlignment())
+  /*
+   * Set vertical scroll bar parameters for wrapped panel
+   * @param res 
+   *    the residue to scroll to
+   */
+  private void setScrollingForWrappedPanel(int res)
+  {
+    // get the width of the alignment in residues
+    int maxwidth = av.getAlignment().getWidth();
+    if (av.hasHiddenColumns())
     {
-      int maxwidth = av.getAlignment().getWidth();
-
-      if (av.hasHiddenColumns())
-      {
         maxwidth = av.getAlignment().getHiddenColumns()
                 .findColumnPosition(maxwidth) - 1;
-      }
-
-      int canvasWidth = getSeqPanel().seqCanvas
-              .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth());
-      if (canvasWidth > 0)
-      {
-        int max = maxwidth
-                / getSeqPanel().seqCanvas
-                        .getWrappedCanvasWidth(getSeqPanel().seqCanvas
-                                .getWidth()) + 1;
-        vscroll.setMaximum(max);
-        vscroll.setUnitIncrement(1);
-        vscroll.setVisibleAmount(1);
-      }
     }
-    else
+
+    // get the width of the canvas in residues
+    int canvasWidth = getSeqPanel().seqCanvas
+            .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth());
+    if (canvasWidth > 0)
     {
-      setScrollValues(vpRanges.getStartRes(), vpRanges.getStartSeq());
+      // 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);
     }
   }
 
@@ -1902,14 +1857,19 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * 
    * @param b
    */
-  protected void setDontScrollComplement(boolean b)
+  protected void setToScrollComplementPanel(boolean b)
   {
-    this.dontScrollComplement = b;
+    this.scrollComplementaryPanel = b;
   }
 
-  protected boolean isDontScrollComplement()
+  /**
+   * Get whether to scroll complement panel
+   * 
+   * @return true if cDNA/protein complement panels should be scrolled
+   */
+  protected boolean isSetToScrollComplementPanel()
   {
-    return this.dontScrollComplement;
+    return this.scrollComplementaryPanel;
   }
 
   /**
@@ -1934,6 +1894,28 @@ public class AlignmentPanel extends GAlignmentPanel implements
     }
   }
 
+  @Override
+  /**
+   * Property change event fired when a change is made to the viewport ranges 
+   * object associated with this alignment panel's viewport
+   */
+  public void propertyChange(PropertyChangeEvent evt)
+  {
+    // update this panel's scroll values based on the new viewport ranges values
+    int x = vpRanges.getStartRes();
+    int y = vpRanges.getStartSeq();
+    setScrollValues(x, y);
+
+    // now update any complementary alignment (its viewport ranges object
+    // is different so does not get automatically updated)
+    if (isSetToScrollComplementPanel())
+    {
+      setToScrollComplementPanel(false);
+      av.scrollComplementaryAlignment();
+      setToScrollComplementPanel(true);
+    }
+  }
+
   /**
    * Set the reference to the PCA/Tree chooser dialog for this panel. This
    * reference should be nulled when the dialog is closed.