Merge branch 'bug/JAL-3072scrollThread' into merge/JAL-3072_3073
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 4 Mar 2019 17:06:13 +0000 (17:06 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 4 Mar 2019 17:06:13 +0000 (17:06 +0000)
Conflicts:
src/jalview/datamodel/Alignment.java
src/jalview/gui/ScalePanel.java
src/jalview/gui/SeqPanel.java

src/jalview/datamodel/Alignment.java
src/jalview/gui/AnnotationPanel.java
src/jalview/gui/IdPanel.java
src/jalview/gui/ScalePanel.java
src/jalview/gui/SeqPanel.java

index 0f51b7e..ac00fa2 100755 (executable)
@@ -712,12 +712,8 @@ public class Alignment implements AlignmentI
   
     for (int i = 0; i < sequences.size(); i++)
     {
-      if (getSequenceAt(i).getLength() > maxLength)
-      {
-        maxLength = getSequenceAt(i).getLength();
-      }
+      maxLength = Math.max(maxLength, getSequenceAt(i).getLength());
     }
-  
     return maxLength;
   }
 
index ab86707..16db94c 100755 (executable)
@@ -77,6 +77,11 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
         MouseListener, MouseWheelListener, MouseMotionListener,
         ActionListener, AdjustmentListener, Scrollable, ViewportListenerI
 {
+  enum DragMode
+  {
+    Select, Resize, Undefined
+  };
+
   String HELIX = MessageManager.getString("label.helix");
 
   String SHEET = MessageManager.getString("label.sheet");
@@ -120,11 +125,11 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
   // Used For mouse Dragging and resizing graphs
   int graphStretch = -1;
 
-  int graphStretchY = -1;
+  int mouseDragLastX = -1;
 
-  int min; // used by mouseDragged to see if user
+  int mouseDragLastY = -1;
 
-  int max; // used by mouseDragged to see if user
+  DragMode dragMode = DragMode.Undefined;
 
   boolean mouseDragging = false;
 
@@ -500,10 +505,11 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
   }
 
   /**
-   * DOCUMENT ME!
+   * Action on right mouse pressed on Mac is to show a pop-up menu for the
+   * annotation. Action on left mouse pressed is to find which annotation is
+   * pressed and mark the start of a column selection or graph resize operation.
    * 
    * @param evt
-   *          DOCUMENT ME!
    */
   @Override
   public void mousePressed(MouseEvent evt)
@@ -514,7 +520,13 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     {
       return;
     }
+    mouseDragLastX = evt.getX();
+    mouseDragLastY = evt.getY();
 
+    /*
+     * add visible annotation heights until we reach the y
+     * position, to find which annotation it is in
+     */
     int height = 0;
     activeRow = -1;
 
@@ -534,11 +546,11 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
         }
         else if (aa[i].graph > 0)
         {
-          // Stretch Graph
+          /*
+           * we have clicked on a resizable graph annotation
+           */
           graphStretch = i;
-          graphStretchY = y;
         }
-
         break;
       }
     }
@@ -604,17 +616,20 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
   }
 
   /**
-   * DOCUMENT ME!
+   * Action on mouse up is to clear mouse drag data and call mouseReleased on
+   * ScalePanel, to deal with defining the selection group (if any) defined by
+   * the mouse drag
    * 
    * @param evt
-   *          DOCUMENT ME!
    */
   @Override
   public void mouseReleased(MouseEvent evt)
   {
     graphStretch = -1;
-    graphStretchY = -1;
+    mouseDragLastX = -1;
+    mouseDragLastY = -1;
     mouseDragging = false;
+    dragMode = DragMode.Undefined;
     ap.getScalePanel().mouseReleased(evt);
 
     /*
@@ -661,24 +676,73 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
   @Override
   public void mouseDragged(MouseEvent evt)
   {
-    if (graphStretch > -1)
+    /*
+     * todo: if dragMode is Undefined:
+     * - set to Select if dx > dy
+     * - set to Resize if dy > dx
+     * - do nothing if dx == dy
+     */
+    final int x = evt.getX();
+    final int y = evt.getY();
+    if (dragMode == DragMode.Undefined)
     {
-      av.getAlignment()
-              .getAlignmentAnnotation()[graphStretch].graphHeight += graphStretchY
-                      - evt.getY();
-      if (av.getAlignment()
-              .getAlignmentAnnotation()[graphStretch].graphHeight < 0)
+      int dx = Math.abs(x - mouseDragLastX);
+      int dy = Math.abs(y - mouseDragLastY);
+      if (graphStretch == -1 || dx > dy)
       {
-        av.getAlignment()
-                .getAlignmentAnnotation()[graphStretch].graphHeight = 0;
+        /*
+         * mostly horizontal drag, or not a graph annotation
+         */
+        dragMode = DragMode.Select;
+      }
+      else if (dy > dx)
+      {
+        /*
+         * mostly vertical drag
+         */
+        dragMode = DragMode.Resize;
       }
-      graphStretchY = evt.getY();
-      adjustPanelHeight();
-      ap.paintAlignment(false, false);
     }
-    else
+
+    if (dragMode == DragMode.Undefined)
+    {
+      /*
+       * drag is diagonal - defer deciding whether to
+       * treat as up/down or left/right
+       */
+      return;
+    }
+
+    try
+    {
+      if (dragMode == DragMode.Resize)
+      {
+        /*
+         * resize graph annotation if mouse was dragged up or down
+         */
+        int deltaY = mouseDragLastY - evt.getY();
+        if (deltaY != 0)
+        {
+          AlignmentAnnotation graphAnnotation = av.getAlignment()
+                  .getAlignmentAnnotation()[graphStretch];
+          int newHeight = Math.max(0, graphAnnotation.graphHeight + deltaY);
+          graphAnnotation.graphHeight = newHeight;
+          adjustPanelHeight();
+          ap.paintAlignment(false, false);
+        }
+      }
+      else
+      {
+        /*
+         * for mouse drag left or right, delegate to 
+         * ScalePanel to adjust the column selection
+         */
+        ap.getScalePanel().mouseDragged(evt);
+      }
+    } finally
     {
-      ap.getScalePanel().mouseDragged(evt);
+      mouseDragLastX = x;
+      mouseDragLastY = y;
     }
   }
 
index 1fd9e49..113fd9f 100755 (executable)
@@ -30,6 +30,7 @@ import jalview.io.SequenceAnnotationReport;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.ViewportRanges;
 
 import java.awt.BorderLayout;
 import java.awt.event.MouseEvent;
@@ -261,7 +262,7 @@ public class IdPanel extends JPanel
   {
     if (scrollThread != null)
     {
-      scrollThread.running = false;
+      scrollThread.stopScrolling();
     }
   }
 
@@ -484,7 +485,7 @@ public class IdPanel extends JPanel
   {
     if (scrollThread != null)
     {
-      scrollThread.running = false;
+      scrollThread.stopScrolling();
     }
     MousePos pos = alignPanel.getSeqPanel().findMousePosition(e);
 
@@ -534,24 +535,42 @@ public class IdPanel extends JPanel
     this.idCanvas = idCanvas;
   }
 
-  // this class allows scrolling off the bottom of the visible alignment
+  /**
+   * Performs scrolling of the visible alignment up or down, adding newly
+   * visible sequences to the current selection
+   */
   class ScrollThread extends Thread
   {
-    boolean running = false;
+    private boolean running = false;
 
-    boolean up = true;
+    private boolean up;
 
+    /**
+     * Constructor for a thread that scrolls either up or down
+     * 
+     * @param up
+     */
     public ScrollThread(boolean up)
     {
       this.up = up;
+      setName("IdPanel$ScrollThread$" + String.valueOf(up));
       start();
     }
 
+    /**
+     * Sets a flag to stop the scrolling
+     */
     public void stopScrolling()
     {
       running = false;
     }
 
+    /**
+     * Scrolls the alignment either up or down, one row at a time, adding newly
+     * visible sequences to the current selection. Speed is limited to a maximum
+     * of ten rows per second. The thread exits when the end of the alignment is
+     * reached or a flag is set to stop it.
+     */
     @Override
     public void run()
     {
@@ -559,29 +578,20 @@ public class IdPanel extends JPanel
 
       while (running)
       {
-        if (av.getRanges().scrollUp(up))
+        ViewportRanges ranges = IdPanel.this.av.getRanges();
+        if (ranges.scrollUp(up))
         {
-          // scroll was ok, so add new sequence to selection
-          int seq = av.getRanges().getStartSeq();
-
-          if (!up)
-          {
-            seq = av.getRanges().getEndSeq();
-          }
-
-          if (seq < lastid)
-          {
-            selectSeqs(lastid - 1, seq);
-          }
-          else if (seq > lastid)
-          {
-            selectSeqs(lastid + 1, seq);
-          }
-
-          lastid = seq;
+          int toSeq = up ? ranges.getStartSeq() : ranges.getEndSeq();
+          int fromSeq = toSeq < lastid ? lastid - 1 : lastid + 1;
+          IdPanel.this.selectSeqs(fromSeq, toSeq);
+
+          lastid = toSeq;
         }
         else
         {
+          /*
+           * scroll did nothing - reached limit of visible alignment
+           */
           running = false;
         }
 
index 9788ec6..0f2b765 100755 (executable)
@@ -34,6 +34,7 @@ import java.awt.Color;
 import java.awt.FontMetrics;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
+import java.awt.Point;
 import java.awt.RenderingHints;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
@@ -265,31 +266,26 @@ public class ScalePanel extends JPanel
   }
 
   /**
-   * DOCUMENT ME!
+   * Action on mouseUp is to set the limit of the current selection group (if
+   * there is one) and broadcast the selection
    * 
    * @param evt
-   *          DOCUMENT ME!
    */
   @Override
   public void mouseReleased(MouseEvent evt)
   {
     mouseDragging = false;
+    ap.getSeqPanel().stopScrolling();
 
     int xCords = Math.max(0, evt.getX()); // prevent negative X coordinates
-
     int res = (xCords / av.getCharWidth())
             + av.getRanges().getStartRes();
-
     if (av.hasHiddenColumns())
     {
       res = av.getAlignment().getHiddenColumns()
               .visibleToAbsoluteColumn(res);
     }
-
-    if (res >= av.getAlignment().getWidth())
-    {
-      res = av.getAlignment().getWidth() - 1;
-    }
+    res = Math.min(res, av.getAlignment().getWidth() - 1);
 
     if (!stretchingGroup)
     {
@@ -362,16 +358,22 @@ public class ScalePanel extends JPanel
   {
     if (mouseDragging)
     {
-      ap.getSeqPanel().scrollCanvas(null);
+      ap.getSeqPanel().stopScrolling();
     }
   }
 
+  /**
+   * Action on leaving the panel bounds with mouse drag in progress is to start
+   * scrolling the alignment in the direction of the mouse. To restrict
+   * scrolling to left-right (not up-down), the y-value of the mouse position is
+   * replaced with zero.
+   */
   @Override
   public void mouseExited(MouseEvent evt)
   {
     if (mouseDragging)
     {
-      ap.getSeqPanel().scrollCanvas(evt);
+      ap.getSeqPanel().startScrolling(new Point(evt.getX(), 0));
     }
   }
 
index 7abbd7d..4a1a9ee 100644 (file)
@@ -49,6 +49,7 @@ import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.ViewportRanges;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -742,13 +743,14 @@ public class SeqPanel extends JPanel
       return;
     }
 
-    if (!editingSeqs)
+    if (editingSeqs)
+    {
+      endEditing();
+    }
+    else
     {
       doMouseReleasedDefineMode(evt, didDrag);
-      return;
     }
-
-    endEditing();
   }
 
   /**
@@ -1325,9 +1327,9 @@ public class SeqPanel extends JPanel
     }
 
     mouseDragging = true;
-    if ((scrollThread != null) && (scrollThread.isRunning()))
+    if (scrollThread != null)
     {
-      scrollThread.setEvent(evt);
+      scrollThread.setMousePosition(evt.getPoint());
     }
   }
 
@@ -1898,10 +1900,10 @@ public class SeqPanel extends JPanel
   }
 
   /**
-   * DOCUMENT ME!
+   * On reentering the panel, stops any scrolling that was started on dragging
+   * out of the panel
    * 
    * @param e
-   *          DOCUMENT ME!
    */
   @Override
   public void mouseEntered(MouseEvent e)
@@ -1910,19 +1912,14 @@ public class SeqPanel extends JPanel
     {
       oldSeq = 0;
     }
-
-    if ((scrollThread != null) && (scrollThread.isRunning()))
-    {
-      scrollThread.stopScrolling();
-      scrollThread = null;
-    }
+    stopScrolling();
   }
 
   /**
-   * DOCUMENT ME!
+   * On leaving the panel, if the mouse is being dragged, starts a thread to
+   * scroll it until the mouse is released (in unwrapped mode only)
    * 
    * @param e
-   *          DOCUMENT ME!
    */
   @Override
   public void mouseExited(MouseEvent e)
@@ -2248,10 +2245,7 @@ public class SeqPanel extends JPanel
       return;
     }
 
-    if (res >= av.getAlignment().getWidth())
-    {
-      res = av.getAlignment().getWidth() - 1;
-    }
+    res = Math.min(res, av.getAlignment().getWidth()-1);
 
     if (stretchGroup.getEndRes() == res)
     {
@@ -2337,9 +2331,9 @@ public class SeqPanel extends JPanel
 
     mouseDragging = true;
 
-    if ((scrollThread != null) && (scrollThread.isRunning()))
+    if (scrollThread != null)
     {
-      scrollThread.setEvent(evt);
+      scrollThread.setMousePosition(evt.getPoint());
     }
 
     /*
@@ -2369,83 +2363,120 @@ public class SeqPanel extends JPanel
     ap.alignFrame.setStatus(status.toString());
   }
 
-  void scrollCanvas(MouseEvent evt)
+  /**
+   * Stops the scroll thread if it is running
+   */
+  void stopScrolling()
   {
-    if (evt == null)
+    if (scrollThread != null)
     {
-      if ((scrollThread != null) && (scrollThread.isRunning()))
-      {
-        scrollThread.stopScrolling();
-        scrollThread = null;
-      }
-      mouseDragging = false;
+      scrollThread.stopScrolling();
+      scrollThread = null;
     }
-    else
-    {
-      if (scrollThread == null)
-      {
-        scrollThread = new ScrollThread();
-      }
+    mouseDragging = false;
+  }
 
-      mouseDragging = true;
-      scrollThread.setEvent(evt);
+  /**
+   * Starts a thread to scroll the alignment, towards a given mouse position
+   * outside the panel bounds
+   * 
+   * @param mousePos
+   */
+  void startScrolling(Point mousePos)
+  {
+    if (scrollThread == null)
+    {
+      scrollThread = new ScrollThread();
     }
 
+    mouseDragging = true;
+    scrollThread.setMousePosition(mousePos);
   }
 
-  // this class allows scrolling off the bottom of the visible alignment
+  /**
+   * Performs scrolling of the visible alignment left, right, up or down
+   */
   class ScrollThread extends Thread
   {
-    MouseEvent evt;
+    private Point mousePos;
 
     private volatile boolean threadRunning = true;
 
+    /**
+     * Constructor
+     */
     public ScrollThread()
     {
+      setName("SeqPanel$ScrollThread");
       start();
     }
 
-    public void setEvent(MouseEvent e)
+    /**
+     * Sets the position of the mouse that determines the direction of the
+     * scroll to perform
+     * 
+     * @param p
+     */
+    public void setMousePosition(Point p)
     {
-      evt = e;
+      mousePos = p;
     }
 
+    /**
+     * Sets a flag that will cause the thread to exit
+     */
     public void stopScrolling()
     {
       threadRunning = false;
     }
 
-    public boolean isRunning()
-    {
-      return threadRunning;
-    }
-
+    /**
+     * Scrolls the alignment left or right, and/or up or down, depending on the
+     * last notified mouse position, until the limit of the alignment is
+     * reached, or a flag is set to stop the scroll
+     */
     @Override
     public void run()
     {
-      while (threadRunning)
+      while (threadRunning && mouseDragging)
       {
-        if (evt != null)
+        if (mousePos != null)
         {
-          if (mouseDragging && (evt.getY() < 0)
-                  && (av.getRanges().getStartSeq() > 0))
+          boolean scrolled = false;
+          ViewportRanges ranges = SeqPanel.this.av.getRanges();
+
+          /*
+           * scroll up or down
+           */
+          if (mousePos.y < 0)
           {
-            av.getRanges().scrollUp(true);
+            // mouse is above this panel - try scroll up
+            scrolled = ranges.scrollUp(true);
           }
-
-          if (mouseDragging && (evt.getY() >= getHeight()) && (av
-                  .getAlignment().getHeight() > av.getRanges().getEndSeq()))
+          else if (mousePos.y >= getHeight())
           {
-            av.getRanges().scrollUp(false);
+            // mouse is below this panel - try scroll down
+            scrolled = ranges.scrollUp(false);
           }
 
-          if (mouseDragging && (evt.getX() < 0))
+          /*
+           * scroll left or right
+           */
+          if (mousePos.x < 0)
           {
-            av.getRanges().scrollRight(false);
+            scrolled |= ranges.scrollRight(false);
           }
-          else if (mouseDragging && (evt.getX() >= getWidth()))
+          else if (mousePos.x >= getWidth())
           {
-            av.getRanges().scrollRight(true);
+            scrolled |= ranges.scrollRight(true);
+          }
+          if (!scrolled)
+          {
+            /*
+             * we have reached the limit of the visible alignment - quit
+             */
+            threadRunning = false;
+            SeqPanel.this.ap.repaint();
           }
         }