Merge branch 'releases/Release_2_11_3_Branch'
[jalview.git] / src / jalview / gui / SeqPanel.java
index f433aec..2caea17 100644 (file)
@@ -44,7 +44,7 @@ import javax.swing.Timer;
 import javax.swing.ToolTipManager;
 
 import jalview.api.AlignViewportI;
-import jalview.bin.Cache;
+import jalview.bin.Console;
 import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
 import jalview.commands.EditCommand.Edit;
@@ -137,7 +137,7 @@ public class SeqPanel extends JPanel
       MousePos o = (MousePos) obj;
       boolean b = (column == o.column && seqIndex == o.seqIndex
               && annotationIndex == o.annotationIndex);
-      // System.out.println(obj + (b ? "= " : "!= ") + this);
+      // jalview.bin.Console.outPrintln(obj + (b ? "= " : "!= ") + this);
       return b;
     }
 
@@ -212,8 +212,6 @@ public class SeqPanel extends JPanel
 
   StringBuffer keyboardNo2;
 
-  java.net.URL linkImageURL;
-
   private final SequenceAnnotationReport seqARep;
 
   /*
@@ -244,13 +242,11 @@ public class SeqPanel extends JPanel
    */
   public SeqPanel(AlignViewport viewport, AlignmentPanel alignPanel)
   {
-    linkImageURL = getClass().getResource("/images/link.gif");
-    seqARep = new SequenceAnnotationReport(linkImageURL.toString());
+    seqARep = new SequenceAnnotationReport(true);
     ToolTipManager.sharedInstance().registerComponent(this);
     ToolTipManager.sharedInstance().setInitialDelay(0);
     ToolTipManager.sharedInstance().setDismissDelay(10000);
-    
-    
+
     this.av = viewport;
     setBackground(Color.white);
 
@@ -278,6 +274,9 @@ public class SeqPanel extends JPanel
   /**
    * Computes the column and sequence row (and possibly annotation row when in
    * wrapped mode) for the given mouse position
+   * <p>
+   * Mouse position is not set if in wrapped mode with the cursor either between
+   * sequences, or over the left or right vertical scale.
    * 
    * @param evt
    * @return
@@ -342,15 +341,39 @@ public class SeqPanel extends JPanel
 
     return new MousePos(col, seqIndex, annIndex);
   }
+
+  /**
+   * @param evt
+   * @return absolute column in alignment nearest to the mouse pointer
+   */
+  int findAlignmentColumn(MouseEvent evt)
+  {
+    return findNearestColumn(evt, true);
+  }
+
   /**
    * Returns the aligned sequence position (base 0) at the mouse position, or
    * the closest visible one
+   * <p>
+   * Returns -1 if in wrapped mode with the mouse over either left or right
+   * vertical scale.
    * 
    * @param evt
    * @return
    */
   int findColumn(MouseEvent evt)
   {
+    return findNearestColumn(evt, false);
+  }
+
+  /**
+   * @param nearestColumn
+   *          when false returns negative values for out of bound positions - -1
+   *          for scale left/right, <-1 if far to right
+   * @return nearest absolute column to mouse pointer
+   */
+  private int findNearestColumn(MouseEvent evt, boolean nearestColumn)
+  {
     int res = 0;
     int x = evt.getX();
 
@@ -374,7 +397,14 @@ public class SeqPanel extends JPanel
       if (x < 0)
       {
         // mouse is over left scale
-        return -1;
+        if (!nearestColumn)
+        {
+          return -1;
+        }
+        else
+        {
+          x = 0;
+        }
       }
 
       int cwidth = seqCanvas.getWrappedCanvasWidth(this.getWidth());
@@ -384,8 +414,15 @@ public class SeqPanel extends JPanel
       }
       if (x >= cwidth * charWidth)
       {
-        // mouse is over right scale
-        return -1;
+        if (!nearestColumn)
+        {
+          // mouse is over right scale
+          return -1;
+        }
+        else
+        {
+          x = cwidth * charWidth - 1;
+        }
       }
 
       wrappedBlock = y / cHeight;
@@ -402,8 +439,14 @@ public class SeqPanel extends JPanel
        * rather than right-hand gutter
        */
       x = Math.min(x, seqCanvas.getX() + seqCanvas.getWidth());
+      if (nearestColumn)
+      {
+        x = Math.max(x, 0);
+      }
+
       res = (x / charWidth) + startRes;
       res = Math.min(res, av.getRanges().getEndRes());
+
     }
 
     if (av.hasHiddenColumns())
@@ -480,45 +523,85 @@ public class SeqPanel extends JPanel
 
   void moveCursor(int dx, int dy)
   {
-    seqCanvas.cursorX += dx;
-    seqCanvas.cursorY += dy;
+    moveCursor(dx, dy, false);
+  }
 
+  void moveCursor(int dx, int dy, boolean nextWord)
+  {
     HiddenColumns hidden = av.getAlignment().getHiddenColumns();
 
-    if (av.hasHiddenColumns() && !hidden.isVisible(seqCanvas.cursorX))
+    if (nextWord)
     {
-      int original = seqCanvas.cursorX - dx;
       int maxWidth = av.getAlignment().getWidth();
-
-      if (!hidden.isVisible(seqCanvas.cursorX))
-      {
-        int visx = hidden.absoluteToVisibleColumn(seqCanvas.cursorX - dx);
-        int[] region = hidden.getRegionWithEdgeAtRes(visx);
-
-        if (region != null) // just in case
+      int maxHeight = av.getAlignment().getHeight();
+      SequenceI seqAtRow = av.getAlignment()
+              .getSequenceAt(seqCanvas.cursorY);
+      // look for next gap or residue
+      boolean isGap = Comparison
+              .isGap(seqAtRow.getCharAt(seqCanvas.cursorX));
+      int p = seqCanvas.cursorX, lastP, r = seqCanvas.cursorY, lastR;
+      do
+      {
+        lastP = p;
+        lastR = r;
+        if (dy != 0)
         {
-          if (dx == 1)
+          r += dy;
+          if (r < 0)
           {
-            // moving right
-            seqCanvas.cursorX = region[1] + 1;
+            r = 0;
           }
-          else if (dx == -1)
+          if (r >= maxHeight)
           {
-            // moving left
-            seqCanvas.cursorX = region[0] - 1;
+            r = maxHeight - 1;
           }
+          seqAtRow = av.getAlignment().getSequenceAt(r);
         }
-        seqCanvas.cursorX = (seqCanvas.cursorX < 0) ? 0 : seqCanvas.cursorX;
-      }
+        p = nextVisible(hidden, maxWidth, p, dx);
+      } while ((dx != 0 ? p != lastP : r != lastR)
+              && isGap == Comparison.isGap(seqAtRow.getCharAt(p)));
+      seqCanvas.cursorX = p;
+      seqCanvas.cursorY = r;
+    }
+    else
+    {
+      int maxWidth = av.getAlignment().getWidth();
+      seqCanvas.cursorX = nextVisible(hidden, maxWidth, seqCanvas.cursorX,
+              dx);
+      seqCanvas.cursorY += dy;
+    }
+    scrollToVisible(false);
+  }
 
-      if (seqCanvas.cursorX >= maxWidth
-              || !hidden.isVisible(seqCanvas.cursorX))
+  private int nextVisible(HiddenColumns hidden, int maxWidth, int original,
+          int dx)
+  {
+    int newCursorX = original + dx;
+    if (av.hasHiddenColumns() && !hidden.isVisible(newCursorX))
+    {
+      int visx = hidden.absoluteToVisibleColumn(newCursorX - dx);
+      int[] region = hidden.getRegionWithEdgeAtRes(visx);
+
+      if (region != null) // just in case
       {
-        seqCanvas.cursorX = original;
+        if (dx == 1)
+        {
+          // moving right
+          newCursorX = region[1] + 1;
+        }
+        else if (dx == -1)
+        {
+          // moving left
+          newCursorX = region[0] - 1;
+        }
       }
     }
-
-    scrollToVisible(false);
+    newCursorX = (newCursorX < 0) ? 0 : newCursorX;
+    if (newCursorX >= maxWidth || !hidden.isVisible(newCursorX))
+    {
+      newCursorX = original;
+    }
+    return newCursorX;
   }
 
   /**
@@ -576,7 +659,7 @@ public class SeqPanel extends JPanel
     if (av.getAlignment().getHiddenColumns().isVisible(seqCanvas.cursorX))
     {
       setStatusMessage(av.getAlignment().getSequenceAt(seqCanvas.cursorY),
-            seqCanvas.cursorX, seqCanvas.cursorY);
+              seqCanvas.cursorX, seqCanvas.cursorY);
     }
 
     if (repaintNeeded)
@@ -585,7 +668,6 @@ public class SeqPanel extends JPanel
     }
   }
 
-
   void setSelectionAreaAtCursor(boolean topLeft)
   {
     SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
@@ -839,7 +921,7 @@ public class SeqPanel extends JPanel
 
     if (lastMessage == null || !lastMessage.equals(tmp))
     {
-      // System.err.println("mouseOver Sequence: "+tmp);
+      // jalview.bin.Console.errPrintln("mouseOver Sequence: "+tmp);
       ssm.mouseOverSequence(sequence, index, pos, av);
     }
     lastMessage = tmp;
@@ -912,19 +994,19 @@ public class SeqPanel extends JPanel
     AlignFrame af = Desktop.getAlignFrameFor(complement);
     FeatureRendererModel fr2 = af.getFeatureRenderer();
 
-    int j = results.getSize();
+    List<SearchResultMatchI> matches = results.getResults();
+    int j = matches.size();
     List<String> infos = new ArrayList<>();
     for (int i = 0; i < j; i++)
     {
-      SearchResultMatchI match = results.getResults().get(i);
+      SearchResultMatchI match = matches.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);
+        MappedFeatures mf = fr2.findComplementFeaturesAtResidue(ds, pos);
         if (mf != null)
         {
           for (SequenceFeature sf : mf.features)
@@ -964,7 +1046,7 @@ public class SeqPanel extends JPanel
   @Override
   public void updateColours(SequenceI seq, int index)
   {
-    System.out.println("update the seqPanel colours");
+    jalview.bin.Console.outPrintln("update the seqPanel colours");
     // repaint();
   }
 
@@ -1067,8 +1149,7 @@ public class SeqPanel extends JPanel
     {
       List<SequenceFeature> features = ap.getFeatureRenderer()
               .findFeaturesAtColumn(sequence, column + 1);
-      unshownFeatures = seqARep.appendFeaturesLengthLimit(tooltipText, pos,
-              features,
+      unshownFeatures = seqARep.appendFeatures(tooltipText, pos, features,
               this.ap.getSeqPanel().seqCanvas.fr, MAX_TOOLTIP_LENGTH);
 
       /*
@@ -1087,9 +1168,8 @@ public class SeqPanel extends JPanel
                   pos);
           if (mf != null)
           {
-            unshownFeatures = seqARep.appendFeaturesLengthLimit(
-                    tooltipText, pos, mf, fr2,
-                    MAX_TOOLTIP_LENGTH);
+            unshownFeatures += seqARep.appendFeatures(tooltipText, pos, mf,
+                    fr2, MAX_TOOLTIP_LENGTH);
           }
         }
       }
@@ -1117,8 +1197,7 @@ public class SeqPanel extends JPanel
       if (!textString.equals(lastTooltip))
       {
         lastTooltip = textString;
-        lastFormattedTooltip = JvSwingUtils.wrapTooltip(true,
-                textString);
+        lastFormattedTooltip = JvSwingUtils.wrapTooltip(true, textString);
         setToolTipText(lastFormattedTooltip);
       }
     }
@@ -1136,6 +1215,7 @@ public class SeqPanel extends JPanel
     final int column = pos.column;
     final int rowIndex = pos.annotationIndex;
 
+    // TODO - get yOffset for annotation, too
     if (column < 0 || !av.getWrapAlignment() || !av.isShowAnnotation()
             || rowIndex < 0)
     {
@@ -1144,10 +1224,10 @@ public class SeqPanel extends JPanel
     AlignmentAnnotation[] anns = av.getAlignment().getAlignmentAnnotation();
 
     String tooltip = AnnotationPanel.buildToolTip(anns[rowIndex], column,
-            anns);
-    if (true || !tooltip.equals(lastTooltip))
+            anns, 0, av, ap);
+    if (tooltip == null ? tooltip != lastTooltip
+            : !tooltip.equals(lastTooltip))
     {
-      System.out.println("wrapped tooltip set");
       lastTooltip = tooltip;
       lastFormattedTooltip = tooltip == null ? null
               : JvSwingUtils.wrapTooltip(true, tooltip);
@@ -1155,7 +1235,7 @@ public class SeqPanel extends JPanel
     }
 
     String msg = AnnotationPanel.getStatusMessage(av.getAlignment(), column,
-            anns[rowIndex]);
+            anns[rowIndex], 0, av);
     ap.alignFrame.setStatus(msg);
   }
 
@@ -1202,8 +1282,8 @@ public class SeqPanel extends JPanel
 
     tempTip.setTipText(lastFormattedTooltip);
     int tipWidth = (int) tempTip.getPreferredSize().getWidth();
-    
-    // was      x += (w - x < 200) ? -(w / 2) : 5;
+
+    // was x += (w - x < 200) ? -(w / 2) : 5;
     x = (x + tipWidth < w ? x + 10 : w - tipWidth);
     Point p = new Point(x, y + av.getCharHeight()); // BH 2018 was - 20?
 
@@ -1217,7 +1297,8 @@ public class SeqPanel extends JPanel
    * changed, so selective redraws can be applied (ie. only structures, only
    * overview, etc)
    */
-  private boolean updateOverviewAndStructs = false; // TODO: refactor to avcontroller
+  private boolean updateOverviewAndStructs = false; // TODO: refactor to
+                                                    // avcontroller
 
   /**
    * set if av.getSelectionGroup() refers to a group that is defined on the
@@ -1246,7 +1327,7 @@ public class SeqPanel extends JPanel
   {
     char sequenceChar = sequence.getCharAt(column);
     int pos = sequence.findPosition(column);
-    setStatusMessage(sequence, seqIndex, sequenceChar, pos);
+    setStatusMessage(sequence.getName(), seqIndex, sequenceChar, pos);
 
     return pos;
   }
@@ -1262,7 +1343,7 @@ public class SeqPanel extends JPanel
    * Sequence 6 ID: O.niloticus.3 Nucleotide: Uracil (2)
    * </pre>
    * 
-   * @param sequence
+   * @param seqName
    * @param seqIndex
    *          sequence position in the alignment (1..)
    * @param sequenceChar
@@ -1270,7 +1351,7 @@ public class SeqPanel extends JPanel
    * @param residuePos
    *          the sequence residue position (if not over a gap)
    */
-  protected void setStatusMessage(SequenceI sequence, int seqIndex,
+  protected void setStatusMessage(String seqName, int seqIndex,
           char sequenceChar, int residuePos)
   {
     StringBuilder text = new StringBuilder(32);
@@ -1279,8 +1360,7 @@ public class SeqPanel extends JPanel
      * Sequence number (if known), and sequence name.
      */
     String seqno = seqIndex == -1 ? "" : " " + (seqIndex + 1);
-    text.append("Sequence").append(seqno).append(" ID: ")
-            .append(sequence.getName());
+    text.append("Sequence").append(seqno).append(" ID: ").append(seqName);
 
     String residue = null;
 
@@ -1325,7 +1405,8 @@ public class SeqPanel extends JPanel
     {
       return;
     }
-    SequenceI ds = al.getSequenceAt(sequenceIndex).getDatasetSequence();
+    SequenceI alignedSeq = al.getSequenceAt(sequenceIndex);
+    SequenceI ds = alignedSeq.getDatasetSequence();
     for (SearchResultMatchI m : results.getResults())
     {
       SequenceI seq = m.getSequence();
@@ -1337,8 +1418,8 @@ public class SeqPanel extends JPanel
       if (seq == ds)
       {
         int start = m.getStart();
-        setStatusMessage(seq, sequenceIndex, seq.getCharAt(start - 1),
-                start);
+        setStatusMessage(alignedSeq.getName(), sequenceIndex,
+                seq.getCharAt(start - 1), start);
         return;
       }
     }
@@ -1538,12 +1619,12 @@ public class SeqPanel extends JPanel
     String label = null;
     if (groupEditing)
     {
-        message.append("Edit group:");
+      message.append("Edit group:");
       label = MessageManager.getString("action.edit_group");
     }
     else
     {
-        message.append("Edit sequence: " + seq.getName());
+      message.append("Edit sequence: " + seq.getName());
       label = seq.getName();
       if (label.length() > 10)
       {
@@ -1735,8 +1816,7 @@ public class SeqPanel extends JPanel
           {
             for (int j = 0; j < startres - editLastRes; j++)
             {
-              if (!Comparison
-                      .isGap(groupSeqs[g].getCharAt(fixedRight - j)))
+              if (!Comparison.isGap(groupSeqs[g].getCharAt(fixedRight - j)))
               {
                 blank = false;
                 break;
@@ -2128,8 +2208,8 @@ public class SeqPanel extends JPanel
          * highlight the first feature at the position on the alignment
          */
         SearchResultsI highlight = new SearchResults();
-        highlight.addResult(sequence, features.get(0).getBegin(), features
-                .get(0).getEnd());
+        highlight.addResult(sequence, features.get(0).getBegin(),
+                features.get(0).getEnd());
         seqCanvas.highlightSearchResults(highlight, true);
 
         /*
@@ -2393,7 +2473,7 @@ public class SeqPanel extends JPanel
       return;
     }
 
-    res = Math.min(res, av.getAlignment().getWidth()-1);
+    res = Math.min(res, av.getAlignment().getWidth() - 1);
 
     if (stretchGroup.getEndRes() == res)
     {
@@ -2766,7 +2846,7 @@ public class SeqPanel extends JPanel
     {
       if (av.getAlignment() == null)
       {
-        Cache.log.warn("alignviewport av SeqSetId=" + av.getSequenceSetId()
+        Console.warn("alignviewport av SeqSetId=" + av.getSequenceSetId()
                 + " ViewId=" + av.getViewId()
                 + " 's alignment is NULL! returning immediately.");
         return;
@@ -2821,7 +2901,7 @@ public class SeqPanel extends JPanel
     if (copycolsel && av.hasHiddenColumns()
             && (av.getAlignment().getHiddenColumns() == null))
     {
-      System.err.println("Bad things");
+      jalview.bin.Console.errPrintln("Bad things");
     }
     if (repaint) // always true!
     {
@@ -2891,6 +2971,8 @@ public class SeqPanel extends JPanel
      * if hidden column selection has changed
      */
     ap.paintAlignment(hiddenChanged, hiddenChanged);
+    // propagate any selection changes
+    PaintRefresher.Refresh(ap, av.getSequenceSetId());
 
     return true;
   }