Merge branch 'develop' into features/JAL-1793VCF
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 22 Nov 2017 15:33:17 +0000 (15:33 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 22 Nov 2017 15:33:17 +0000 (15:33 +0000)
1  2 
src/jalview/datamodel/Sequence.java
src/jalview/datamodel/SequenceI.java
src/jalview/gui/SeqPanel.java

@@@ -657,10 -657,10 +657,10 @@@ public class Sequence extends ASequenc
    }
  
    /**
 -   * DOCUMENT ME!
 +   * Sets the sequence description, and also parses out any special formats of
 +   * interest
     * 
     * @param desc
 -   *          DOCUMENT ME!
     */
    @Override
    public void setDescription(String desc)
      this.description = desc;
    }
  
 +  @Override
 +  public void setGeneLoci(String speciesId, String assemblyId,
 +          String chromosomeId, MapList map)
 +  {
 +    addDBRef(new DBRefEntry(speciesId, assemblyId, DBRefEntry.CHROMOSOME
 +            + ":" + chromosomeId, new Mapping(map)));
 +  }
 +
    /**
 -   * DOCUMENT ME!
 +   * Returns the gene loci mapping for the sequence (may be null)
     * 
 -   * @return DOCUMENT ME!
 +   * @return
 +   */
 +  @Override
 +  public GeneLociI getGeneLoci()
 +  {
 +    DBRefEntry[] refs = getDBRefs();
 +    if (refs != null)
 +    {
 +      for (final DBRefEntry ref : refs)
 +      {
 +        if (ref.isChromosome())
 +        {
 +          return new GeneLociI()
 +          {
 +            @Override
 +            public String getSpeciesId()
 +            {
 +              return ref.getSource();
 +            }
 +
 +            @Override
 +            public String getAssemblyId()
 +            {
 +              return ref.getVersion();
 +            }
 +
 +            @Override
 +            public String getChromosomeId()
 +            {
 +              // strip off "chromosome:" prefix to chrId
 +              return ref.getAccessionId().substring(
 +                      DBRefEntry.CHROMOSOME.length() + 1);
 +            }
 +
 +            @Override
 +            public MapList getMap()
 +            {
 +              return ref.getMap().getMap();
 +            }
 +          };
 +        }
 +      }
 +    }
 +    return null;
 +  }
 +
 +  /**
 +   * Answers the description
 +   * 
 +   * @return
     */
    @Override
    public String getDescription()
    }
  
    @Override
-   public void deleteChars(int i, int j)
+   public void deleteChars(final int i, final int j)
    {
      int newstart = start, newend = end;
      if (i >= sequence.length || i < 0)
      boolean createNewDs = false;
      // TODO: take a (second look) at the dataset creation validation method for
      // the very large sequence case
-     int eindex = -1, sindex = -1;
-     boolean ecalc = false, scalc = false;
+     int startIndex = findIndex(start) - 1;
+     int endIndex = findIndex(end) - 1;
+     int startDeleteColumn = -1; // for dataset sequence deletions
+     int deleteCount = 0;
      for (int s = i; s < j; s++)
      {
-       if (jalview.schemes.ResidueProperties.aaIndex[sequence[s]] != 23)
+       if (Comparison.isGap(sequence[s]))
+       {
+         continue;
+       }
+       deleteCount++;
+       if (startDeleteColumn == -1)
+       {
+         startDeleteColumn = findPosition(s) - start;
+       }
+       if (createNewDs)
+       {
+         newend--;
+       }
+       else
        {
-         if (createNewDs)
+         if (startIndex == s)
          {
-           newend--;
+           /*
+            * deleting characters from start of sequence; new start is the
+            * sequence position of the next column (position to the right
+            * if the column position is gapped)
+            */
+           newstart = findPosition(j);
+           break;
          }
          else
          {
-           if (!scalc)
-           {
-             sindex = findIndex(start) - 1;
-             scalc = true;
-           }
-           if (sindex == s)
+           if (endIndex < j)
            {
-             // delete characters including start of sequence
-             newstart = findPosition(j);
-             break; // don't need to search for any more residue characters.
+             /*
+              * deleting characters at end of sequence; new end is the sequence
+              * position of the column before the deletion; subtract 1 if this is
+              * gapped since findPosition returns the next sequence position
+              */
+             newend = findPosition(i - 1);
+             if (Comparison.isGap(sequence[i - 1]))
+             {
+               newend--;
+             }
+             break;
            }
            else
            {
-             // delete characters after start.
-             if (!ecalc)
-             {
-               eindex = findIndex(end) - 1;
-               ecalc = true;
-             }
-             if (eindex < j)
-             {
-               // delete characters at end of sequence
-               newend = findPosition(i - 1);
-               break; // don't need to search for any more residue characters.
-             }
-             else
-             {
-               createNewDs = true;
-               newend--; // decrease end position by one for the deleted residue
-               // and search further
-             }
+             createNewDs = true;
+             newend--;
            }
          }
        }
      }
-     // deletion occured in the middle of the sequence
      if (createNewDs && this.datasetSequence != null)
      {
-       // construct a new sequence
+       /*
+        * if deletion occured in the middle of the sequence,
+        * construct a new dataset sequence and delete the residues
+        * that were deleted from the aligned sequence
+        */
        Sequence ds = new Sequence(datasetSequence);
+       ds.deleteChars(startDeleteColumn, startDeleteColumn + deleteCount);
+       datasetSequence = ds;
        // TODO: remove any non-inheritable properties ?
        // TODO: create a sequence mapping (since there is a relation here ?)
-       ds.deleteChars(i, j);
-       datasetSequence = ds;
      }
      start = newstart;
      end = newend;
@@@ -21,7 -21,6 +21,7 @@@
  package jalview.datamodel;
  
  import jalview.datamodel.features.SequenceFeaturesI;
 +import jalview.util.MapList;
  
  import java.util.BitSet;
  import java.util.List;
@@@ -193,13 -192,14 +193,14 @@@ public interface SequenceI extends ASeq
    public int findIndex(int pos);
  
    /**
-    * Returns the sequence position for an alignment position.
+    * Returns the sequence position for an alignment (column) position. If at a
+    * gap, returns the position of the next residue to the right. If beyond the
+    * end of the sequence, returns 1 more than the last residue position.
     * 
     * @param i
     *          column index in alignment (from 0..<length)
     * 
-    * @return TODO: JAL-2562 - residue number for residue (left of and) nearest
-    *         ith column
+    * @return
     */
    public int findPosition(int i);
  
     * @param c2
     */
    public int replace(char c1, char c2);
 +
 +  /**
 +   * Answers the GeneLociI, or null if not known
 +   * 
 +   * @return
 +   */
 +  GeneLociI getGeneLoci();
 +
 +  /**
 +   * Sets the mapping to gene loci for the sequence
 +   * 
 +   * @param speciesId
 +   * @param assemblyId
 +   * @param chromosomeId
 +   * @param map
 +   */
 +  void setGeneLoci(String speciesId, String assemblyId,
 +          String chromosomeId, MapList map);
  }
@@@ -59,6 -59,7 +59,6 @@@ 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;
  
@@@ -75,11 -76,12 +75,11 @@@ import javax.swing.ToolTipManager
  public class SeqPanel extends JPanel
          implements MouseListener, MouseMotionListener, MouseWheelListener,
          SequenceListener, SelectionListener
 -
  {
 -  /** DOCUMENT ME!! */
 +  private static final int MAX_TOOLTIP_LENGTH = 300;
 +
    public SeqCanvas seqCanvas;
  
 -  /** DOCUMENT ME!! */
    public AlignmentPanel ap;
  
    /*
    SearchResultsI lastSearchResults;
  
    /**
 -   * Creates a new SeqPanel object.
 +   * Creates a new SeqPanel object
     * 
 -   * @param avp
 -   *          DOCUMENT ME!
 -   * @param p
 -   *          DOCUMENT ME!
 +   * @param viewport
 +   * @param alignPanel
     */
 -  public SeqPanel(AlignViewport av, AlignmentPanel ap)
 +  public SeqPanel(AlignViewport viewport, AlignmentPanel alignPanel)
    {
      linkImageURL = getClass().getResource("/images/link.gif");
      seqARep = new SequenceAnnotationReport(linkImageURL.toString());
      ToolTipManager.sharedInstance().registerComponent(this);
      ToolTipManager.sharedInstance().setInitialDelay(0);
      ToolTipManager.sharedInstance().setDismissDelay(10000);
 -    this.av = av;
 +    this.av = viewport;
      setBackground(Color.white);
  
 -    seqCanvas = new SeqCanvas(ap);
 +    seqCanvas = new SeqCanvas(alignPanel);
      setLayout(new BorderLayout());
      add(seqCanvas, BorderLayout.CENTER);
  
 -    this.ap = ap;
 +    this.ap = alignPanel;
  
 -    if (!av.isDataset())
 +    if (!viewport.isDataset())
      {
        addMouseMotionListener(this);
        addMouseListener(this);
        addMouseWheelListener(this);
 -      ssm = av.getStructureSelectionManager();
 +      ssm = viewport.getStructureSelectionManager();
        ssm.addStructureViewerListener(this);
        ssm.addSelectionListener(this);
      }
    void setCursorRow()
    {
      seqCanvas.cursorY = getKeyboardNo1() - 1;
-     scrollToVisible();
+     scrollToVisible(true);
    }
  
    void setCursorColumn()
    {
      seqCanvas.cursorX = getKeyboardNo1() - 1;
-     scrollToVisible();
+     scrollToVisible(true);
    }
  
    void setCursorRowAndColumn()
      {
        seqCanvas.cursorX = getKeyboardNo1() - 1;
        seqCanvas.cursorY = getKeyboardNo2() - 1;
-       scrollToVisible();
+       scrollToVisible(true);
      }
    }
  
      SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
  
      seqCanvas.cursorX = sequence.findIndex(getKeyboardNo1()) - 1;
-     scrollToVisible();
+     scrollToVisible(true);
    }
  
    void moveCursor(int dx, int dy)
        }
      }
  
-     scrollToVisible();
+     scrollToVisible(false);
    }
  
-   void scrollToVisible()
+   /**
+    * Scroll to make the cursor visible in the viewport.
+    * 
+    * @param jump
+    *          just jump to the location rather than scrolling
+    */
+   void scrollToVisible(boolean jump)
    {
      if (seqCanvas.cursorX < 0)
      {
      }
  
      endEditing();
-     if (av.getWrapAlignment())
+     boolean repaintNeeded = true;
+     if (jump)
      {
-       av.getRanges().scrollToWrappedVisible(seqCanvas.cursorX);
+       // only need to repaint if the viewport did not move, as otherwise it will
+       // get a repaint
+       repaintNeeded = !av.getRanges().setViewportLocation(seqCanvas.cursorX,
+               seqCanvas.cursorY);
      }
      else
      {
-       av.getRanges().scrollToVisible(seqCanvas.cursorX, seqCanvas.cursorY);
+       if (av.getWrapAlignment())
+       {
+         // scrollToWrappedVisible expects x-value to have hidden cols subtracted
+         int x = av.getAlignment().getHiddenColumns()
+                 .findColumnPosition(seqCanvas.cursorX);
+         av.getRanges().scrollToWrappedVisible(x);
+       }
+       else
+       {
+         av.getRanges().scrollToVisible(seqCanvas.cursorX,
+                 seqCanvas.cursorY);
+       }
      }
-     setStatusMessage(av.getAlignment().getSequenceAt(seqCanvas.cursorY),
+     if (av.getAlignment().getHiddenColumns().isVisible(seqCanvas.cursorX))
+     {
+       setStatusMessage(av.getAlignment().getSequenceAt(seqCanvas.cursorY),
              seqCanvas.cursorX, seqCanvas.cursorY);
+     }
  
-     seqCanvas.repaint();
+     if (repaintNeeded)
+     {
+       seqCanvas.repaint();
+     }
    }
  
    void setSelectionAreaAtCursor(boolean topLeft)
    {
      SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
        List<SequenceFeature> features = ap.getFeatureRenderer()
                .findFeaturesAtColumn(sequence, column + 1);
        seqARep.appendFeatures(tooltipText, pos, features,
 -              this.ap.getSeqPanel().seqCanvas.fr.getMinMax());
 +              this.ap.getSeqPanel().seqCanvas.fr);
      }
      if (tooltipText.length() == 6) // <html>
      {
      }
      else
      {
 +      if (tooltipText.length() > MAX_TOOLTIP_LENGTH) // constant
 +      {
 +        tooltipText.setLength(MAX_TOOLTIP_LENGTH);
 +        tooltipText.append("...");
 +      }
        String textString = tooltipText.toString();
        if (lastTooltip == null || !lastTooltip.equals(textString))
        {
      final int column = findColumn(evt);
      final int seq = findSeq(evt);
      SequenceI sequence = av.getAlignment().getSequenceAt(seq);
 -    List<SequenceFeature> allFeatures = ap.getFeatureRenderer()
 +    List<SequenceFeature> features = ap.getFeatureRenderer()
              .findFeaturesAtColumn(sequence, column + 1);
 -    List<String> links = new ArrayList<>();
 -    for (SequenceFeature sf : allFeatures)
 -    {
 -      if (sf.links != null)
 -      {
 -        for (String link : sf.links)
 -        {
 -          links.add(link);
 -        }
 -      }
 -    }
  
 -    PopupMenu pop = new PopupMenu(ap, null, links);
 +    PopupMenu pop = new PopupMenu(ap, null, features);
      pop.show(this, evt.getX(), evt.getY());
    }