Merge develop to Release_2_8_3_Branch
[jalview.git] / src / jalview / appletgui / SeqPanel.java
index e488a32..ae4ae10 100644 (file)
  */
 package jalview.appletgui;
 
+import java.awt.BorderLayout;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Panel;
+import java.awt.Point;
+import java.awt.event.InputEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.util.List;
+import java.util.Vector;
+
+import jalview.api.AlignViewportI;
 import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResults.Match;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.schemes.ResidueProperties;
+import jalview.structure.SelectionListener;
 import jalview.structure.SelectionSource;
 import jalview.structure.SequenceListener;
 import jalview.structure.StructureSelectionManager;
+import jalview.structure.VamsasSource;
+import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
 
-import java.awt.BorderLayout;
-import java.awt.Font;
-import java.awt.FontMetrics;
-import java.awt.Panel;
-import java.awt.Point;
-import java.awt.event.InputEvent;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
-import java.awt.event.MouseMotionListener;
-import java.util.Vector;
-
 public class SeqPanel extends Panel implements MouseMotionListener,
-        MouseListener, SequenceListener
+        MouseListener, SequenceListener, SelectionListener
 {
 
   public SeqCanvas seqCanvas;
@@ -109,6 +115,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     seqCanvas.addMouseListener(this);
     ssm = StructureSelectionManager.getStructureSelectionManager(av.applet);
     ssm.addStructureViewerListener(this);
+    ssm.addSelectionListener(this);
 
     seqCanvas.repaint();
   }
@@ -159,8 +166,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
 
   void setCursorPosition()
   {
-    SequenceI sequence = av.getAlignment().getSequenceAt(
-            seqCanvas.cursorY);
+    SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
 
     seqCanvas.cursorX = sequence.findIndex(getKeyboardNo1()) - 1;
     scrollToVisible();
@@ -212,7 +218,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     }
 
     endEditing();
-    if (av.wrapAlignment)
+    if (av.getWrapAlignment())
     {
       ap.scrollToWrappedVisible(seqCanvas.cursorX);
     }
@@ -252,8 +258,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
 
   void setSelectionAreaAtCursor(boolean topLeft)
   {
-    SequenceI sequence = av.getAlignment().getSequenceAt(
-            seqCanvas.cursorY);
+    SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
 
     if (av.getSelectionGroup() != null)
     {
@@ -393,41 +398,107 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     return 1;
   }
 
+  /**
+   * Set status message in alignment panel
+   * 
+   * @param sequence
+   *          aligned sequence object
+   * @param res
+   *          alignment column
+   * @param seq
+   *          index of sequence in alignment
+   * @return position of res in sequence
+   */
   void setStatusMessage(SequenceI sequence, int res, int seq)
   {
-    StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: "
-            + sequence.getName());
-
-    Object obj = null;
+    // TODO remove duplication of identical gui method
+    StringBuilder text = new StringBuilder(32);
+    String seqno = seq == -1 ? "" : " " + (seq + 1);
+    text.append("Sequence" + seqno + " ID: " + sequence.getName());
+
+    String residue = null;
+    /*
+     * Try to translate the display character to residue name (null for gap).
+     */
+    final String displayChar = String.valueOf(sequence.getCharAt(res));
     if (av.getAlignment().isNucleotide())
     {
-      obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res)
-              + "");
-      if (obj != null)
+      residue = ResidueProperties.nucleotideName.get(displayChar);
+      if (residue != null)
       {
-        text.append(" Nucleotide: ");
+        text.append(" Nucleotide: ").append(residue);
       }
     }
     else
     {
-      obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");
-      if (obj != null)
-      {
-        text.append("  Residue: ");
-      }
-    }
+      residue = "X".equalsIgnoreCase(displayChar) ? "X"
+              : ResidueProperties.aa2Triplet.get(displayChar);
+      if (residue != null)
+      {
+        text.append(" Residue: ").append(residue);
+      }
+    }
+
+    int pos = -1;
+    if (residue != null)
+    {
+      pos = sequence.findPosition(res);
+      text.append(" (").append(Integer.toString(pos)).append(")");
+    }
+    // Object obj = null;
+    // if (av.getAlignment().isNucleotide())
+    // {
+    // obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res)
+    // + "");
+    // if (obj != null)
+    // {
+    // text.append(" Nucleotide: ");
+    // }
+    // }
+    // else
+    // {
+    // obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");
+    // if (obj != null)
+    // {
+    // text.append("  Residue: ");
+    // }
+    // }
+    //
+    // if (obj != null)
+    // {
+    //
+    // if (obj != "")
+    // {
+    // text.append(obj + " (" + sequence.findPosition(res) + ")");
+    // }
+    // }
 
-    if (obj != null)
-    {
+    ap.alignFrame.statusBar.setText(text.toString());
 
-      if (obj != "")
-      {
-        text.append(obj + " (" + sequence.findPosition(res) + ")");
-      }
-    }
+  }
 
-    ap.alignFrame.statusBar.setText(text.toString());
+  /**
+   * Set the status bar message to highlight the first matched position in
+   * search results.
+   * 
+   * @param results
+   */
+  private void setStatusMessage(SearchResults results)
+  {
+    List<Match> matches = results.getResults();
+    if (!matches.isEmpty())
+    {
+      Match m = matches.get(0);
+      SequenceI seq = m.getSequence();
+      int sequenceIndex = this.av.getAlignment().findIndex(seq);
 
+      /*
+       * Convert position in sequence (base 1) to sequence character array index
+       * (base 0)
+       */
+      int start = m.getStart() - 1;
+      setStatusMessage(seq, start, sequenceIndex);
+    }
   }
 
   public void mousePressed(MouseEvent evt)
@@ -538,16 +609,17 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     int res = 0;
     int x = evt.getX();
 
-    if (av.wrapAlignment)
+    if (av.getWrapAlignment())
     {
 
-      int hgap = av.charHeight;
-      if (av.scaleAboveWrapped)
+      int hgap = av.getCharHeight();
+      if (av.getScaleAboveWrapped())
       {
-        hgap += av.charHeight;
+        hgap += av.getCharHeight();
       }
 
-      int cHeight = av.getAlignment().getHeight() * av.charHeight + hgap
+      int cHeight = av.getAlignment().getHeight() * av.getCharHeight()
+              + hgap
               + seqCanvas.getAnnotationHeight();
 
       int y = evt.getY();
@@ -596,15 +668,16 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     int seq = 0;
     int y = evt.getY();
 
-    if (av.wrapAlignment)
+    if (av.getWrapAlignment())
     {
-      int hgap = av.charHeight;
-      if (av.scaleAboveWrapped)
+      int hgap = av.getCharHeight();
+      if (av.getScaleAboveWrapped())
       {
-        hgap += av.charHeight;
+        hgap += av.getCharHeight();
       }
 
-      int cHeight = av.getAlignment().getHeight() * av.charHeight + hgap
+      int cHeight = av.getAlignment().getHeight() * av.getCharHeight()
+              + hgap
               + seqCanvas.getAnnotationHeight();
 
       y -= hgap;
@@ -676,10 +749,17 @@ public class SeqPanel extends Panel implements MouseMotionListener,
         ap.alignFrame.repaint();
       }
     }
+    setStatusMessage(results);
     seqCanvas.highlightSearchResults(results);
 
   }
 
+  @Override
+  public VamsasSource getVamsasSource()
+  {
+    return this.ap == null ? null : this.ap.av;
+  }
+
   public void updateColours(SequenceI seq, int index)
   {
     System.out.println("update the seqPanel colours");
@@ -716,39 +796,38 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       mouseOverSequence(sequence, res, respos);
     }
 
-    StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: "
-            + sequence.getName());
+    StringBuilder text = new StringBuilder();
+    text.append("Sequence ").append(Integer.toString(seq + 1))
+            .append(" ID: ").append(sequence.getName());
 
-    Object obj = null;
+    String obj = null;
+    final String ch = String.valueOf(sequence.getCharAt(res));
     if (av.getAlignment().isNucleotide())
     {
-      obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res)
-              + "");
+      obj = ResidueProperties.nucleotideName.get(ch);
       if (obj != null)
       {
-        text.append(" Nucleotide: ");
+        text.append(" Nucleotide: ").append(obj);
       }
     }
     else
     {
-      obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");
+      obj = "X".equalsIgnoreCase(ch) ? "X"
+              : ResidueProperties.aa2Triplet.get(ch);
       if (obj != null)
       {
-        text.append("  Residue: ");
+        text.append(" Residue: ").append(obj);
       }
     }
 
     if (obj != null)
     {
-      if (obj != "")
-      {
-        text.append(obj + " (" + respos + ")");
-      }
+      text.append(" (").append(Integer.toString(respos)).append(")");
     }
 
     ap.alignFrame.statusBar.setText(text.toString());
 
-    StringBuffer tooltipText = new StringBuffer();
+    StringBuilder tooltipText = new StringBuilder();
     SequenceGroup[] groups = av.getAlignment().findAllGroups(sequence);
     if (groups != null)
     {
@@ -759,7 +838,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
           if (!groups[g].getName().startsWith("JTreeGroup")
                   && !groups[g].getName().startsWith("JGroup"))
           {
-            tooltipText.append(groups[g].getName() + " ");
+            tooltipText.append(groups[g].getName()).append(" ");
           }
           if (groups[g].getDescription() != null)
           {
@@ -849,7 +928,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
   {
     if (mouseWheelPressed)
     {
-      int oldWidth = av.charWidth;
+      int oldWidth = av.getCharWidth();
 
       // Which is bigger, left-right or up-down?
       if (Math.abs(evt.getY() - lastMousePress.y) > Math.abs(evt.getX()
@@ -857,7 +936,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       {
         int fontSize = av.font.getSize();
 
-        if (evt.getY() < lastMousePress.y && av.charHeight > 1)
+        if (evt.getY() < lastMousePress.y && av.getCharHeight() > 1)
         {
           fontSize--;
         }
@@ -872,29 +951,29 @@ public class SeqPanel extends Panel implements MouseMotionListener,
         }
 
         av.setFont(new Font(av.font.getName(), av.font.getStyle(), fontSize));
-        av.charWidth = oldWidth;
+        av.setCharWidth(oldWidth);
       }
       else
       {
-        if (evt.getX() < lastMousePress.x && av.charWidth > 1)
+        if (evt.getX() < lastMousePress.x && av.getCharWidth() > 1)
         {
-          av.charWidth--;
+          av.setCharWidth(av.getCharWidth() - 1);
         }
         else if (evt.getX() > lastMousePress.x)
         {
-          av.charWidth++;
+          av.setCharWidth(av.getCharWidth() + 1);
         }
 
-        if (av.charWidth < 1)
+        if (av.getCharWidth() < 1)
         {
-          av.charWidth = 1;
+          av.setCharWidth(1);
         }
       }
 
       ap.fontChanged();
 
       FontMetrics fm = getFontMetrics(av.getFont());
-      av.validCharWidth = fm.charWidth('M') <= av.charWidth;
+      av.validCharWidth = fm.charWidth('M') <= av.getCharWidth();
 
       lastMousePress = evt.getPoint();
 
@@ -960,15 +1039,18 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     StringBuffer message = new StringBuffer();
     if (groupEditing)
     {
-      message.append(MessageManager.getString("action.edit_group")).append(":");
+      message.append(MessageManager.getString("action.edit_group")).append(
+              ":");
       if (editCommand == null)
       {
-        editCommand = new EditCommand(MessageManager.getString("action.edit_group"));
+        editCommand = new EditCommand(
+                MessageManager.getString("action.edit_group"));
       }
     }
     else
     {
-      message.append(MessageManager.getString("label.edit_sequence")).append(" " + seq.getName());
+      message.append(MessageManager.getString("label.edit_sequence"))
+              .append(" " + seq.getName());
       String label = seq.getName();
       if (label.length() > 10)
       {
@@ -976,7 +1058,9 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       }
       if (editCommand == null)
       {
-        editCommand = new EditCommand(MessageManager.formatMessage("label.edit_params", new String[]{label}));
+        editCommand = new EditCommand(MessageManager.formatMessage(
+                "label.edit_params", new String[]
+                { label }));
       }
     }
 
@@ -1178,8 +1262,8 @@ public class SeqPanel extends Panel implements MouseMotionListener,
         }
         else
         {
-          editCommand.appendEdit(Action.INSERT_GAP, groupSeqs,
-                  startres, startres - lastres, av.getAlignment(), true);
+          editCommand.appendEdit(Action.INSERT_GAP, groupSeqs, startres,
+                  startres - lastres, av.getAlignment(), true);
         }
       }
       else
@@ -1194,8 +1278,8 @@ public class SeqPanel extends Panel implements MouseMotionListener,
         }
         else
         {
-          editCommand.appendEdit(Action.DELETE_GAP, groupSeqs,
-                  startres, lastres - startres, av.getAlignment(), true);
+          editCommand.appendEdit(Action.DELETE_GAP, groupSeqs, startres,
+                  lastres - startres, av.getAlignment(), true);
         }
 
       }
@@ -1290,16 +1374,16 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     editCommand.appendEdit(Action.DELETE_GAP, seq, blankColumn, 1,
             av.getAlignment(), true);
 
-    editCommand.appendEdit(Action.INSERT_GAP, seq, j, 1,
-            av.getAlignment(), true);
+    editCommand.appendEdit(Action.INSERT_GAP, seq, j, 1, av.getAlignment(),
+            true);
 
   }
 
   void deleteChar(int j, SequenceI[] seq, int fixedColumn)
   {
 
-    editCommand.appendEdit(Action.DELETE_GAP, seq, j, 1,
-            av.getAlignment(), true);
+    editCommand.appendEdit(Action.DELETE_GAP, seq, j, 1, av.getAlignment(),
+            true);
 
     editCommand.appendEdit(Action.INSERT_GAP, seq, fixedColumn, 1,
             av.getAlignment(), true);
@@ -1378,7 +1462,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       SequenceFeature[] allFeatures = findFeaturesAtRes(sequence,
               sequence.findPosition(res));
 
-      Vector links = null;
+      Vector<String> links = null;
       if (allFeatures != null)
       {
         for (int i = 0; i < allFeatures.length; i++)
@@ -1387,7 +1471,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
           {
             if (links == null)
             {
-              links = new Vector();
+              links = new Vector<String>();
             }
             for (int j = 0; j < allFeatures[i].links.size(); j++)
             {
@@ -1718,6 +1802,16 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     {
       return;
     }
+
+    /*
+     * Check for selection in a view of which this one is a dna/protein
+     * complement.
+     */
+    if (selectionFromTranslation(seqsel, colsel, source))
+    {
+      return;
+    }
+
     // do we want to thread this ? (contention with seqsel and colsel locks, I
     // suspect)
     // rules are: colsel is copied if there is a real intersection between
@@ -1835,4 +1929,46 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     ap.scrollTo(column, column, ap.av.startSeq, true, true);
   }
 
+  /**
+   * If this panel is a cdna/protein translation view of the selection source,
+   * tries to map the source selection to a local one, and returns true. Else
+   * returns false.
+   * 
+   * @param seqsel
+   * @param colsel
+   * @param source
+   */
+  protected boolean selectionFromTranslation(SequenceGroup seqsel,
+          ColumnSelection colsel, SelectionSource source)
+  {
+    if (!(source instanceof AlignViewportI)) {
+      return false;
+    }
+    final AlignViewportI sourceAv = (AlignViewportI) source;
+    if (sourceAv.getCodingComplement() != av && av.getCodingComplement() != sourceAv)
+    {
+      return false;
+    }
+  
+    /*
+     * Map sequence selection
+     */
+    SequenceGroup sg = MappingUtils.mapSequenceGroup(seqsel, sourceAv, av);
+    av.setSelectionGroup(sg);
+    av.isSelectionGroupChanged(true);
+  
+    /*
+     * Map column selection
+     */
+    ColumnSelection cs = MappingUtils.mapColumnSelection(colsel, sourceAv,
+            av);
+    av.setColumnSelection(cs);
+    av.isColSelChanged(true);
+  
+    ap.scalePanelHolder.repaint();
+    ap.repaint();
+  
+    return true;
+  }
+
 }