3253-omnibus save
[jalview.git] / src / jalview / gui / SeqPanel.java
index b188888..85d6025 100644 (file)
@@ -29,6 +29,7 @@ import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.MappedFeatures;
 import jalview.datamodel.SearchResultMatchI;
 import jalview.datamodel.SearchResults;
 import jalview.datamodel.SearchResultsI;
@@ -50,6 +51,7 @@ import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.viewmodel.ViewportRanges;
+import jalview.viewmodel.seqfeatures.FeatureRendererModel;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -63,6 +65,7 @@ import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionListener;
 import java.awt.event.MouseWheelEvent;
 import java.awt.event.MouseWheelListener;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -224,7 +227,7 @@ public class SeqPanel extends JPanel
   SearchResultsI lastSearchResults;
 
   /**
-   * Constructor
+   * Creates a new SeqPanel object
    * 
    * @param viewport
    * @param alignPanel
@@ -841,11 +844,11 @@ public class SeqPanel extends JPanel
    * the start of the highlighted region.
    */
   @Override
-  public void highlightSequence(SearchResultsI results)
+  public String highlightSequence(SearchResultsI results)
   {
     if (results == null || results.equals(lastSearchResults))
     {
-      return;
+      return null;
     }
     lastSearchResults = results;
 
@@ -871,6 +874,77 @@ public class SeqPanel extends JPanel
     {
       setStatusMessage(results);
     }
+    return results.isEmpty() ? null : getHighlightInfo(results);
+  }
+
+  /**
+   * temporary hack: answers a message suitable to show on structure hover
+   * label. This is normally null. It is a peptide variation description if
+   * <ul>
+   * <li>results are a single residue in a protein alignment</li>
+   * <li>there is a mapping to a coding sequence (codon)</li>
+   * <li>there are one or more SNP variant features on the codon</li>
+   * </ul>
+   * in which case the answer is of the format (e.g.) "p.Glu388Asp"
+   * 
+   * @param results
+   * @return
+   */
+  private String getHighlightInfo(SearchResultsI results)
+  {
+    /*
+     * ideally, just find mapped CDS (as we don't care about render style here);
+     * for now, go via split frame complement's FeatureRenderer
+     */
+    AlignViewportI complement = ap.getAlignViewport().getCodingComplement();
+    if (complement == null)
+    {
+      return null;
+    }
+    AlignFrame af = Desktop.getAlignFrameFor(complement);
+    FeatureRendererModel fr2 = af.getFeatureRenderer();
+
+    int j = results.getSize();
+    List<String> infos = new ArrayList<>();
+    for (int i = 0; i < j; i++)
+    {
+      SearchResultMatchI match = results.getResults().get(i);
+      int pos = match.getStart();
+      if (pos == match.getEnd())
+      {
+        SequenceI seq = match.getSequence();
+        SequenceI ds = seq.getDatasetSequence() == null ? seq
+                : seq.getDatasetSequence();
+        MappedFeatures mf = fr2
+                .findComplementFeaturesAtResidue(ds, pos);
+        if (mf != null)
+        {
+          for (SequenceFeature sf : mf.features)
+          {
+            String pv = mf.findProteinVariants(sf);
+            if (pv.length() > 0 && !infos.contains(pv))
+            {
+              infos.add(pv);
+            }
+          }
+        }
+      }
+    }
+
+    if (infos.isEmpty())
+    {
+      return null;
+    }
+    StringBuilder sb = new StringBuilder();
+    for (String info : infos)
+    {
+      if (sb.length() > 0)
+      {
+        sb.append("|");
+      }
+      sb.append(info);
+    }
+    return sb.toString();
   }
 
   @Override
@@ -977,25 +1051,57 @@ public class SeqPanel extends JPanel
      * add features that straddle the gap (pos may be the residue before or
      * after the gap)
      */
+    int unshownFeatures = 0;
     if (av.isShowSequenceFeatures())
     {
       List<SequenceFeature> features = ap.getFeatureRenderer()
               .findFeaturesAtColumn(sequence, column + 1);
-      seqARep.appendFeatures(tooltipText, pos, features,
-              this.ap.getSeqPanel().seqCanvas.fr);
+      unshownFeatures = seqARep.appendFeaturesLengthLimit(tooltipText, pos,
+              features,
+              this.ap.getSeqPanel().seqCanvas.fr, MAX_TOOLTIP_LENGTH);
+
+      /*
+       * add features in CDS/protein complement at the corresponding
+       * position if configured to do so
+       */
+      if (av.isShowComplementFeatures())
+      {
+        if (!Comparison.isGap(sequence.getCharAt(column)))
+        {
+          AlignViewportI complement = ap.getAlignViewport()
+                  .getCodingComplement();
+          AlignFrame af = Desktop.getAlignFrameFor(complement);
+          FeatureRendererModel fr2 = af.getFeatureRenderer();
+          MappedFeatures mf = fr2.findComplementFeaturesAtResidue(sequence,
+                  pos);
+          if (mf != null)
+          {
+            unshownFeatures = seqARep.appendFeaturesLengthLimit(
+                    tooltipText, pos, mf, fr2,
+                    MAX_TOOLTIP_LENGTH);
+          }
+        }
+      }
     }
-    if (tooltipText.length() == 6) // <html>
+    if (tooltipText.length() == 6) // "<html>"
     {
       setToolTipText(null);
       lastTooltip = null;
     }
     else
     {
-      if (tooltipText.length() > MAX_TOOLTIP_LENGTH) // constant
+      if (tooltipText.length() > MAX_TOOLTIP_LENGTH)
       {
         tooltipText.setLength(MAX_TOOLTIP_LENGTH);
         tooltipText.append("...");
       }
+      if (unshownFeatures > 0)
+      {
+        tooltipText.append("<br/>").append("... ").append("<i>")
+                .append(MessageManager.formatMessage(
+                        "label.features_not_shown", unshownFeatures))
+                .append("</i>");
+      }
       String textString = tooltipText.toString();
       if (lastTooltip == null || !lastTooltip.equals(textString))
       {
@@ -1949,13 +2055,19 @@ public class SeqPanel extends JPanel
       return;
     }
 
-    /*
-     * start scrolling if mouse dragging, whether the drag started
-     * in the scale panel or this panel
-     */
-    if (mouseDragging || ap.getScalePanel().isMouseDragging())
+    // BH check was:
+//    /*
+//     * start scrolling if mouse dragging, whether the drag started
+//     * in the scale panel or this panel
+//     */
+//    if (mouseDragging || ap.getScalePanel().isMouseDragging())
+//    {
+//      startScrolling(new Point(e.getX(), 0));
+//    }
+
+    if (mouseDragging && scrollThread == null)
     {
-      startScrolling(new Point(e.getX(), 0));
+      startScrolling(e.getPoint());
     }
   }
 
@@ -1974,7 +2086,7 @@ public class SeqPanel extends JPanel
       return;
     }
 
-    if (evt.getClickCount() > 1)
+    if (evt.getClickCount() > 1 && av.isShowSequenceFeatures())
     {
       sg = av.getSelectionGroup();
       if (sg != null && sg.getSize() == 1
@@ -2175,12 +2287,12 @@ public class SeqPanel extends JPanel
     final int column = pos.column;
     final int seq = pos.seqIndex;
     SequenceI sequence = av.getAlignment().getSequenceAt(seq);
-    List<SequenceFeature> features = ap.getFeatureRenderer()
-            .findFeaturesAtColumn(sequence, column + 1);
-
-    PopupMenu pop = new PopupMenu(ap, null, features);
+    if (sequence != null)
+    {
+      PopupMenu pop = new PopupMenu(ap, sequence, column);
     pop.show(this, evt.getX(), evt.getY());
   }
+  }
 
   /**
    * Update the display after mouse up on a selection or group
@@ -2428,10 +2540,17 @@ public class SeqPanel extends JPanel
               // if (!scrollOnce() {t.stop();}) gives compiler error :-(
               scrollThread.scrollOnce();
             }
+          }
+        });
+        t.addActionListener(new ActionListener()
+        {
+          @Override
+          public void actionPerformed(ActionEvent e)
+          {
             if (scrollThread == null)
             {
               // SeqPanel.stopScrolling called
-              ((Timer) e.getSource()).stop();
+              t.stop();
             }
           }
         });