Merge branch 'features/JAL-2446NCList' into features/JAL-2574findFeaturesByColumn
[jalview.git] / src / jalview / gui / SeqPanel.java
index 20e585a..8e65840 100644 (file)
@@ -84,6 +84,16 @@ public class SeqPanel extends JPanel implements MouseListener,
   /** DOCUMENT ME!! */
   public AlignmentPanel ap;
 
+  /*
+   * last column position for mouseMoved event
+   */
+  private int lastMouseColumn;
+
+  /*
+   * last sequence offset for mouseMoved event
+   */
+  private int lastMouseSeq;
+
   protected int lastres;
 
   protected int startseq;
@@ -170,6 +180,9 @@ public class SeqPanel extends JPanel implements MouseListener,
       ssm.addStructureViewerListener(this);
       ssm.addSelectionListener(this);
     }
+
+    lastMouseColumn = -1;
+    lastMouseSeq = -1;
   }
 
   int startWrapBlock = -1;
@@ -389,38 +402,11 @@ public class SeqPanel extends JPanel implements MouseListener,
     endEditing();
     if (av.getWrapAlignment())
     {
-      ap.scrollToWrappedVisible(seqCanvas.cursorX);
+      av.getRanges().scrollToWrappedVisible(seqCanvas.cursorX);
     }
     else
     {
-      while (seqCanvas.cursorY < av.getRanges().getStartSeq())
-      {
-        ap.scrollUp(true);
-      }
-      while (seqCanvas.cursorY > av.getRanges().getEndSeq())
-      {
-        ap.scrollUp(false);
-      }
-      if (!av.getWrapAlignment())
-      {
-        HiddenColumns hidden = av.getAlignment().getHiddenColumns();
-        while (seqCanvas.cursorX < hidden.adjustForHiddenColumns(av
-                .getRanges().getStartRes()))
-        {
-          if (!ap.scrollRight(false))
-          {
-            break;
-          }
-        }
-        while (seqCanvas.cursorX > hidden.adjustForHiddenColumns(av
-                .getRanges().getEndRes()))
-        {
-          if (!ap.scrollRight(true))
-          {
-            break;
-          }
-        }
-      }
+      av.getRanges().scrollToVisible(seqCanvas.cursorX, seqCanvas.cursorY);
     }
     setStatusMessage(av.getAlignment().getSequenceAt(seqCanvas.cursorY),
             seqCanvas.cursorX, seqCanvas.cursorY);
@@ -590,6 +576,7 @@ public class SeqPanel extends JPanel implements MouseListener,
   @Override
   public void mouseReleased(MouseEvent evt)
   {
+    boolean didDrag = mouseDragging; // did we come here after a drag
     mouseDragging = false;
     mouseWheelPressed = false;
 
@@ -602,7 +589,7 @@ public class SeqPanel extends JPanel implements MouseListener,
 
     if (!editingSeqs)
     {
-      doMouseReleasedDefineMode(evt);
+      doMouseReleasedDefineMode(evt, didDrag);
       return;
     }
 
@@ -696,17 +683,16 @@ public class SeqPanel extends JPanel implements MouseListener,
 
     if (av.isFollowHighlight())
     {
-      /*
-       * if scrollToPosition requires a scroll adjustment, this flag prevents
-       * another scroll event being propagated back to the originator
-       * 
-       * @see AlignmentPanel#adjustmentValueChanged
-       */
-      ap.setDontScrollComplement(true);
+      // don't allow highlight of protein/cDNA to also scroll a complementary
+      // panel,as this sets up a feedback loop (scrolling panel 1 causes moused
+      // over residue to change abruptly, causing highlighted residue in panel 2
+      // to change, causing a scroll in panel 1 etc)
+      ap.setToScrollComplementPanel(false);
       if (ap.scrollToPosition(results, false))
       {
         seqCanvas.revalidate();
       }
+      ap.setToScrollComplementPanel(true);
     }
     setStatusMessage(results);
     seqCanvas.highlightSearchResults(results);
@@ -745,8 +731,18 @@ public class SeqPanel extends JPanel implements MouseListener,
     int seq = findSeq(evt);
     if (column < 0 || seq < 0 || seq >= av.getAlignment().getHeight())
     {
+      lastMouseSeq = -1;
+      return;
+    }
+    if (column == lastMouseColumn && seq == lastMouseSeq)
+    {
+      /*
+       * just a pixel move without change of residue
+       */
       return;
     }
+    lastMouseColumn = column;
+    lastMouseSeq = seq;
 
     SequenceI sequence = av.getAlignment().getSequenceAt(seq);
 
@@ -758,8 +754,9 @@ public class SeqPanel extends JPanel implements MouseListener,
     /*
      * set status bar message, returning residue position in sequence
      */
+    boolean isGapped = Comparison.isGap(sequence.getCharAt(column));
     final int pos = setStatusMessage(sequence, column, seq);
-    if (ssm != null && pos > -1)
+    if (ssm != null && !isGapped)
     {
       mouseOverSequence(sequence, column, pos);
     }
@@ -788,10 +785,15 @@ public class SeqPanel extends JPanel implements MouseListener,
       }
     }
 
-    if (av.isShowSequenceFeatures() && pos != -1)
+    /*
+     * add any features at the position to the tooltip; if over a gap, only
+     * add features that straddle the gap (pos may be the residue before or
+     * after the gap)
+     */
+    if (av.isShowSequenceFeatures())
     {
       List<SequenceFeature> features = ap.getFeatureRenderer()
-              .findFeaturesAtRes(sequence.getDatasetSequence(), pos);
+              .findFeaturesAtColumn(sequence, column + 1);
       seqARep.appendFeatures(tooltipText, pos, features,
               this.ap.getSeqPanel().seqCanvas.fr.getMinMax());
     }
@@ -802,18 +804,15 @@ public class SeqPanel extends JPanel implements MouseListener,
     }
     else
     {
-      if (lastTooltip == null
-              || !lastTooltip.equals(tooltipText.toString()))
+      String textString = tooltipText.toString();
+      if (lastTooltip == null || !lastTooltip.equals(textString))
       {
-        String formatedTooltipText = JvSwingUtils.wrapTooltip(true,
-                tooltipText.toString());
-        // String formatedTooltipText = tooltipText.toString();
-        setToolTipText(formatedTooltipText);
-        lastTooltip = tooltipText.toString();
+        String formattedTooltipText = JvSwingUtils.wrapTooltip(true,
+                textString);
+        setToolTipText(formattedTooltipText);
+        lastTooltip = textString;
       }
-
     }
-
   }
 
   private Point lastp = null;
@@ -860,9 +859,10 @@ public class SeqPanel extends JPanel implements MouseListener,
 
   /**
    * Sets the status message in alignment panel, showing the sequence number
-   * (index) and id, residue and residue position for the given sequence and
-   * column position. Returns the calculated residue position in the sequence,
-   * or -1 for a gapped column position.
+   * (index) and id, and residue and residue position if not at a gap, for the
+   * given sequence and column position. Returns the residue position returned
+   * by Sequence.findPosition. Note this may be for the nearest adjacent residue
+   * if at a gapped position.
    * 
    * @param sequence
    *          aligned sequence object
@@ -870,7 +870,8 @@ public class SeqPanel extends JPanel implements MouseListener,
    *          alignment column
    * @param seq
    *          index of sequence in alignment
-   * @return position of column in sequence or -1 if at a gap
+   * @return sequence position of residue at column, or adjacent residue if at a
+   *         gap
    */
   int setStatusMessage(SequenceI sequence, final int column, int seq)
   {
@@ -884,36 +885,34 @@ public class SeqPanel extends JPanel implements MouseListener,
             .append(sequence.getName());
 
     String residue = null;
+
     /*
      * Try to translate the display character to residue name (null for gap).
      */
     final String displayChar = String.valueOf(sequence.getCharAt(column));
-    if (av.getAlignment().isNucleotide())
+    boolean isGapped = Comparison.isGap(sequence.getCharAt(column));
+    int pos = sequence.findPosition(column);
+
+    if (!isGapped)
     {
-      residue = ResidueProperties.nucleotideName.get(displayChar);
-      if (residue != null)
+      boolean nucleotide = av.getAlignment().isNucleotide();
+      if (nucleotide)
       {
-        text.append(" Nucleotide: ").append(residue);
+        residue = ResidueProperties.nucleotideName.get(displayChar);
       }
-    }
-    else
-    {
-      residue = "X".equalsIgnoreCase(displayChar) ? "X" : ("*"
-              .equals(displayChar) ? "STOP" : ResidueProperties.aa2Triplet
-              .get(displayChar));
-      if (residue != null)
+      else
       {
-        text.append(" Residue: ").append(residue);
+        residue = "X".equalsIgnoreCase(displayChar) ? "X" : ("*"
+                .equals(displayChar) ? "STOP"
+                : ResidueProperties.aa2Triplet.get(displayChar));
       }
-    }
+      text.append(" ").append(nucleotide ? "Nucleotide" : "Residue")
+              .append(": ").append(residue == null ? displayChar : residue);
 
-    int pos = -1;
-    if (residue != null)
-    {
-      pos = sequence.findPosition(column);
       text.append(" (").append(Integer.toString(pos)).append(")");
     }
     ap.alignFrame.statusBar.setText(text.toString());
+
     return pos;
   }
 
@@ -1553,9 +1552,14 @@ public class SeqPanel extends JPanel implements MouseListener,
         av.setSelectionGroup(null);
       }
 
+      int column = findColumn(evt);
+
+      /*
+       * find features at the position (if not gapped), or straddling
+       * the position (if at a gap)
+       */
       List<SequenceFeature> features = seqCanvas.getFeatureRenderer()
-              .findFeaturesAtRes(sequence.getDatasetSequence(),
-                      sequence.findPosition(findColumn(evt)));
+              .findFeaturesAtColumn(sequence, column + 1);
 
       if (!features.isEmpty())
       {
@@ -1587,23 +1591,23 @@ public class SeqPanel extends JPanel implements MouseListener,
     {
       if (e.isShiftDown())
       {
-        ap.scrollRight(true);
+        av.getRanges().scrollRight(true);
 
       }
       else
       {
-        ap.scrollUp(false);
+        av.getRanges().scrollUp(false);
       }
     }
     else
     {
       if (e.isShiftDown())
       {
-        ap.scrollRight(false);
+        av.getRanges().scrollRight(false);
       }
       else
       {
-        ap.scrollUp(true);
+        av.getRanges().scrollUp(true);
       }
     }
     // TODO Update tooltip for new position.
@@ -1732,13 +1736,12 @@ public class SeqPanel extends JPanel implements MouseListener,
    */
   void showPopupMenu(MouseEvent evt)
   {
-    final int res = findColumn(evt);
+    final int column = findColumn(evt);
     final int seq = findSeq(evt);
     SequenceI sequence = av.getAlignment().getSequenceAt(seq);
     List<SequenceFeature> allFeatures = ap.getFeatureRenderer()
-            .findFeaturesAtRes(sequence.getDatasetSequence(),
-                    sequence.findPosition(res));
-    List<String> links = new ArrayList<String>();
+            .findFeaturesAtColumn(sequence, column + 1);
+    List<String> links = new ArrayList<>();
     for (SequenceFeature sf : allFeatures)
     {
       if (sf.links != null)
@@ -1755,12 +1758,15 @@ public class SeqPanel extends JPanel implements MouseListener,
   }
 
   /**
-   * DOCUMENT ME!
+   * Update the display after mouse up on a selection or group
    * 
    * @param evt
-   *          DOCUMENT ME!
+   *          mouse released event details
+   * @param afterDrag
+   *          true if this event is happening after a mouse drag (rather than a
+   *          mouse down)
    */
-  public void doMouseReleasedDefineMode(MouseEvent evt)
+  public void doMouseReleasedDefineMode(MouseEvent evt, boolean afterDrag)
   {
     if (stretchGroup == null)
     {
@@ -1769,7 +1775,8 @@ public class SeqPanel extends JPanel implements MouseListener,
     // always do this - annotation has own state
     // but defer colourscheme update until hidden sequences are passed in
     boolean vischange = stretchGroup.recalcConservation(true);
-    needOverviewUpdate |= vischange && av.isSelectionDefinedGroup();
+    needOverviewUpdate |= vischange && av.isSelectionDefinedGroup()
+            && afterDrag;
     if (stretchGroup.cs != null)
     {
       stretchGroup.cs.alignmentChanged(stretchGroup,
@@ -1971,23 +1978,23 @@ public class SeqPanel extends JPanel implements MouseListener,
           if (mouseDragging && (evt.getY() < 0)
                   && (av.getRanges().getStartSeq() > 0))
           {
-            running = ap.scrollUp(true);
+            running = av.getRanges().scrollUp(true);
           }
 
           if (mouseDragging && (evt.getY() >= getHeight())
                   && (av.getAlignment().getHeight() > av.getRanges()
                           .getEndSeq()))
           {
-            running = ap.scrollUp(false);
+            running = av.getRanges().scrollUp(false);
           }
 
           if (mouseDragging && (evt.getX() < 0))
           {
-            running = ap.scrollRight(false);
+            running = av.getRanges().scrollRight(false);
           }
           else if (mouseDragging && (evt.getX() >= getWidth()))
           {
-            running = ap.scrollRight(true);
+            running = av.getRanges().scrollRight(true);
           }
         }
 
@@ -2023,11 +2030,13 @@ public class SeqPanel extends JPanel implements MouseListener,
         ap.getCalculationDialog().validateCalcTypes();
       }
 
-      // process further ?
-      if (!av.followSelection)
-      {
-        return;
-      }
+      return;
+    }
+
+    // process further ?
+    if (!av.followSelection)
+    {
+      return;
     }
 
     /*