JAL-3074 don't scroll up/down on drag out of scale or annotation panel, performance...
[jalview.git] / src / jalview / gui / SeqPanel.java
index fb6efe5..401bd0a 100644 (file)
@@ -48,6 +48,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;
@@ -246,7 +247,7 @@ public class SeqPanel extends JPanel
     if (av.hasHiddenColumns())
     {
       res = av.getAlignment().getHiddenColumns()
-              .adjustForHiddenColumns(res);
+              .visibleToAbsoluteColumn(res);
     }
 
     return res;
@@ -359,13 +360,25 @@ public class SeqPanel extends JPanel
       int original = seqCanvas.cursorX - dx;
       int maxWidth = av.getAlignment().getWidth();
 
-      // TODO: once JAL-2759 is ready, change this loop to something more
-      // efficient
-      while (!hidden.isVisible(seqCanvas.cursorX)
-              && seqCanvas.cursorX < maxWidth && seqCanvas.cursorX > 0
-              && dx != 0)
+      if (!hidden.isVisible(seqCanvas.cursorX))
       {
-        seqCanvas.cursorX += dx;
+        int visx = hidden.absoluteToVisibleColumn(seqCanvas.cursorX - dx);
+        int[] region = hidden.getRegionWithEdgeAtRes(visx);
+
+        if (region != null) // just in case
+        {
+          if (dx == 1)
+          {
+            // moving right
+            seqCanvas.cursorX = region[1] + 1;
+          }
+          else if (dx == -1)
+          {
+            // moving left
+            seqCanvas.cursorX = region[0] - 1;
+          }
+        }
+        seqCanvas.cursorX = (seqCanvas.cursorX < 0) ? 0 : seqCanvas.cursorX;
       }
 
       if (seqCanvas.cursorX >= maxWidth
@@ -420,7 +433,7 @@ public class SeqPanel extends JPanel
       {
         // scrollToWrappedVisible expects x-value to have hidden cols subtracted
         int x = av.getAlignment().getHiddenColumns()
-                .findColumnPosition(seqCanvas.cursorX);
+                .absoluteToVisibleColumn(seqCanvas.cursorX);
         av.getRanges().scrollToWrappedVisible(x);
       }
       else
@@ -616,13 +629,14 @@ public class SeqPanel extends JPanel
       return;
     }
 
-    if (!editingSeqs)
+    if (editingSeqs)
+    {
+      endEditing();
+    }
+    else
     {
       doMouseReleasedDefineMode(evt, didDrag);
-      return;
     }
-
-    endEditing();
   }
 
   /**
@@ -1149,9 +1163,9 @@ public class SeqPanel extends JPanel
     }
 
     mouseDragging = true;
-    if ((scrollThread != null) && (scrollThread.isRunning()))
+    if (scrollThread != null)
     {
-      scrollThread.setEvent(evt);
+      scrollThread.setMousePosition(evt.getPoint());
     }
   }
 
@@ -1260,9 +1274,9 @@ public class SeqPanel extends JPanel
     {
       fixedColumns = true;
       int y1 = av.getAlignment().getHiddenColumns()
-              .getHiddenBoundaryLeft(startres);
+              .getNextHiddenBoundary(true, startres);
       int y2 = av.getAlignment().getHiddenColumns()
-              .getHiddenBoundaryRight(startres);
+              .getNextHiddenBoundary(false, startres);
 
       if ((insertGap && startres > y1 && lastres < y1)
               || (!insertGap && startres < y2 && lastres > y2))
@@ -1338,7 +1352,8 @@ public class SeqPanel extends JPanel
           if (sg.getSize() == av.getAlignment().getHeight())
           {
             if ((av.hasHiddenColumns() && startres < av.getAlignment()
-                    .getHiddenColumns().getHiddenBoundaryRight(startres)))
+                    .getHiddenColumns()
+                    .getNextHiddenBoundary(false, startres)))
             {
               endEditing();
               return;
@@ -1562,10 +1577,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)
@@ -1574,29 +1589,19 @@ 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)
   {
-    if (av.getWrapAlignment())
-    {
-      return;
-    }
-
-    if (mouseDragging && scrollThread == null)
+    if (!av.getWrapAlignment() && mouseDragging && scrollThread == null)
     {
       scrollThread = new ScrollThread();
     }
@@ -1657,7 +1662,8 @@ public class SeqPanel extends JPanel
   public void mouseWheelMoved(MouseWheelEvent e)
   {
     e.consume();
-    if (e.getWheelRotation() > 0)
+    double wheelRotation = e.getPreciseWheelRotation();
+    if (wheelRotation > 0)
     {
       if (e.isShiftDown())
       {
@@ -1669,7 +1675,7 @@ public class SeqPanel extends JPanel
         av.getRanges().scrollUp(false);
       }
     }
-    else
+    else if (wheelRotation < 0)
     {
       if (e.isShiftDown())
       {
@@ -1905,10 +1911,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)
     {
@@ -1994,89 +1997,126 @@ public class SeqPanel extends JPanel
 
     mouseDragging = true;
 
-    if ((scrollThread != null) && (scrollThread.isRunning()))
+    if (scrollThread != null)
     {
-      scrollThread.setEvent(evt);
+      scrollThread.setMousePosition(evt.getPoint());
     }
   }
 
-  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)
+          {
+            scrolled |= ranges.scrollRight(false);
+          }
+          else if (mousePos.x >= getWidth())
           {
-            av.getRanges().scrollRight(false);
+            scrolled |= ranges.scrollRight(true);
           }
-          else if (mouseDragging && (evt.getX() >= getWidth()))
+          if (!scrolled)
           {
-            av.getRanges().scrollRight(true);
+            /*
+             * we have reached the limit of the visible alignment - quit
+             */
+            threadRunning = false;
+            SeqPanel.this.ap.repaint();
           }
         }
 
@@ -2278,4 +2318,13 @@ public class SeqPanel extends JPanel
 
     return true;
   }
+
+  /**
+   * 
+   * @return null or last search results handled by this panel
+   */
+  public SearchResultsI getLastSearchResults()
+  {
+    return lastSearchResults;
+  }
 }