JAL-1645 Version-Rel Version 2.9 Year-Rel 2015 Licensing glob
[jalview.git] / src / jalview / gui / AlignViewport.java
index 44b4167..8a95015 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
- * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
+ * Copyright (C) 2015 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
  */
 package jalview.gui;
 
-import java.awt.Container;
-import java.awt.Dimension;
-import java.awt.Font;
-import java.awt.Rectangle;
-import java.util.ArrayList;
-import java.util.Hashtable;
-import java.util.Set;
-import java.util.Vector;
-
-import javax.swing.JInternalFrame;
-import javax.swing.JOptionPane;
-
 import jalview.analysis.AlignmentUtils;
 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
 import jalview.analysis.NJTree;
 import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
 import jalview.api.ViewStyleI;
 import jalview.bin.Cache;
 import jalview.commands.CommandI;
@@ -62,6 +51,7 @@ import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SearchResults;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
@@ -75,6 +65,19 @@ import jalview.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.ws.params.AutoCalcSetting;
 
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Rectangle;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JOptionPane;
+
 /**
  * DOCUMENT ME!
  * 
@@ -82,16 +85,8 @@ import jalview.ws.params.AutoCalcSetting;
  * @version $Revision: 1.141 $
  */
 public class AlignViewport extends AlignmentViewport implements
-        SelectionSource, AlignViewportI, CommandListener
+        SelectionSource, CommandListener
 {
-  int startRes;
-
-  int endRes;
-
-  int startSeq;
-
-  int endSeq;
-
   Font font;
 
   NJTree currentTree = null;
@@ -114,6 +109,7 @@ public class AlignViewport extends AlignmentViewport implements
   private boolean gatherViewsHere = false;
 
   private AnnotationColumnChooser annotationColumnSelectionState;
+
   /**
    * Creates a new AlignViewport object.
    * 
@@ -250,7 +246,7 @@ public class AlignViewport extends AlignmentViewport implements
     showAutocalculatedAbove = Cache.getDefault(
             Preferences.SHOW_AUTOCALC_ABOVE, false);
     viewStyle.setScaleProteinAsCdna(Cache.getDefault(
-            Preferences.SCALE_PROTEIN_TO_CDNA, false));
+            Preferences.SCALE_PROTEIN_TO_CDNA, true));
   }
 
   void init()
@@ -366,112 +362,6 @@ public class AlignViewport extends AlignmentViewport implements
     return sq;
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public int getStartRes()
-  {
-    return startRes;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public int getEndRes()
-  {
-    return endRes;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public int getStartSeq()
-  {
-    return startSeq;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param res
-   *          DOCUMENT ME!
-   */
-  public void setStartRes(int res)
-  {
-    this.startRes = res;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param seq
-   *          DOCUMENT ME!
-   */
-  public void setStartSeq(int seq)
-  {
-    this.startSeq = seq;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param res
-   *          DOCUMENT ME!
-   */
-  public void setEndRes(int res)
-  {
-    if (res > (alignment.getWidth() - 1))
-    {
-      // log.System.out.println(" Corrected res from " + res + " to maximum " +
-      // (alignment.getWidth()-1));
-      res = alignment.getWidth() - 1;
-    }
-
-    if (res < 0)
-    {
-      res = 0;
-    }
-
-    this.endRes = res;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param seq
-   *          DOCUMENT ME!
-   */
-  public void setEndSeq(int seq)
-  {
-    if (seq > alignment.getHeight())
-    {
-      seq = alignment.getHeight();
-    }
-
-    if (seq < 0)
-    {
-      seq = 0;
-    }
-
-    this.endSeq = seq;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
-  public int getEndSeq()
-  {
-    return endSeq;
-  }
-
   boolean validCharWidth;
 
   /**
@@ -510,6 +400,7 @@ public class AlignViewport extends AlignmentViewport implements
             viewStyle.getFontSize()), false);
 
   }
+
   /**
    * DOCUMENT ME!
    * 
@@ -528,16 +419,61 @@ public class AlignViewport extends AlignmentViewport implements
    */
   public void setAlignment(AlignmentI align)
   {
-    if (alignment != null && alignment.getCodonFrames() != null)
+    replaceMappings(align);
+    this.alignment = align;
+  }
+
+  /**
+   * Replace any codon mappings for this viewport with those for the given
+   * viewport
+   * 
+   * @param align
+   */
+  public void replaceMappings(AlignmentI align)
+  {
+
+    /*
+     * Deregister current mappings (if any)
+     */
+    deregisterMappings();
+
+    /*
+     * Register new mappings (if any)
+     */
+    if (align != null)
     {
-      StructureSelectionManager.getStructureSelectionManager(
-              Desktop.instance).removeMappings(alignment.getCodonFrames());
+      StructureSelectionManager ssm = StructureSelectionManager
+              .getStructureSelectionManager(Desktop.instance);
+      ssm.registerMappings(align.getCodonFrames());
     }
-    this.alignment = align;
-    if (alignment != null && alignment.getCodonFrames() != null)
+
+    /*
+     * replace mappings on our alignment
+     */
+    if (alignment != null && align != null)
+    {
+      alignment.setCodonFrames(align.getCodonFrames());
+    }
+  }
+
+  protected void deregisterMappings()
+  {
+    AlignmentI al = getAlignment();
+    if (al != null)
     {
-      StructureSelectionManager.getStructureSelectionManager(
-              Desktop.instance).addMappings(alignment.getCodonFrames());
+      Set<AlignedCodonFrame> mappings = al.getCodonFrames();
+      if (mappings != null)
+      {
+        StructureSelectionManager ssm = StructureSelectionManager
+                .getStructureSelectionManager(Desktop.instance);
+        for (AlignedCodonFrame acf : mappings)
+        {
+          if (noReferencesTo(acf))
+          {
+            ssm.deregisterMapping(acf);
+          }
+        }
+      }
     }
   }
 
@@ -631,11 +567,9 @@ public class AlignViewport extends AlignmentViewport implements
     // TODO: JAL-1126
     if (historyList == null || redoList == null)
     {
-      return new long[]
-      { -1, -1 };
+      return new long[] { -1, -1 };
     }
-    return new long[]
-    { historyList.hashCode(), this.redoList.hashCode() };
+    return new long[] { historyList.hashCode(), this.redoList.hashCode() };
   }
 
   /**
@@ -669,21 +603,6 @@ public class AlignViewport extends AlignmentViewport implements
     return false;
   }
 
-  /**
-   * when set, view will scroll to show the highlighted position
-   */
-  public boolean followHighlight = true;
-
-  /**
-   * @return true if view should scroll to show the highlighted region of a
-   *         sequence
-   * @return
-   */
-  public boolean getFollowHighlight()
-  {
-    return followHighlight;
-  }
-
   public boolean followSelection = true;
 
   /**
@@ -784,35 +703,31 @@ public class AlignViewport extends AlignmentViewport implements
   /**
    * 
    * @param pdbEntries
-   * @return a series of SequenceI arrays, one for each PDBEntry, listing which
-   *         sequence in the alignment holds a reference to it
+   * @return an array of SequenceI arrays, one for each PDBEntry, listing which
+   *         sequences in the alignment hold a reference to it
    */
   public SequenceI[][] collateForPDB(PDBEntry[] pdbEntries)
   {
-    ArrayList<SequenceI[]> seqvectors = new ArrayList<SequenceI[]>();
+    List<SequenceI[]> seqvectors = new ArrayList<SequenceI[]>();
     for (PDBEntry pdb : pdbEntries)
     {
-      ArrayList<SequenceI> seqs = new ArrayList<SequenceI>();
-      for (int i = 0; i < alignment.getHeight(); i++)
+      List<SequenceI> seqs = new ArrayList<SequenceI>();
+      for (SequenceI sq : alignment.getSequences())
       {
-        Vector pdbs = alignment.getSequenceAt(i).getDatasetSequence()
-                .getPDBId();
+        Vector<PDBEntry> pdbs = sq.getDatasetSequence().getAllPDBEntries();
         if (pdbs == null)
         {
           continue;
         }
-        SequenceI sq;
-        for (int p = 0; p < pdbs.size(); p++)
+        for (PDBEntry p1 : pdbs)
         {
-          PDBEntry p1 = (PDBEntry) pdbs.elementAt(p);
           if (p1.getId().equals(pdb.getId()))
           {
-            if (!seqs.contains(sq = alignment.getSequenceAt(i)))
+            if (!seqs.contains(sq))
             {
               seqs.add(sq);
+              continue;
             }
-
-            continue;
           }
         }
       }
@@ -926,16 +841,34 @@ public class AlignViewport extends AlignmentViewport implements
     // TODO: create undo object for this JAL-1101
 
     /*
-     * If one alignment is protein and one nucleotide, with at least one
-     * sequence name in common, offer to open a linked alignment.
+     * If any cDNA/protein mappings can be made between the alignments, offer to
+     * open a linked alignment with split frame option.
      */
-    if (AlignmentUtils.isMappable(al, getAlignment()))
+    if (Cache.getDefault(Preferences.ENABLE_SPLIT_FRAME, true))
     {
-      if (openLinkedAlignment(al, title))
+      if (al.getDataset() == null)
       {
-        return;
+        // need to create ds seqs
+        for (SequenceI sq : al.getSequences())
+        {
+          if (sq.getDatasetSequence() == null)
+          {
+            sq.createDatasetSequence();
+          }
+        }
+      }
+      if (AlignmentUtils.isMappable(al, getAlignment()))
+      {
+        if (openLinkedAlignment(al, title))
+        {
+          return;
+        }
       }
     }
+
+    /*
+     * No mappings, or offer declined - add sequences to this alignment
+     */
     // TODO: JAL-407 regardless of above - identical sequences (based on ID and
     // provenance) should share the same dataset sequence
 
@@ -943,25 +876,24 @@ public class AlignViewport extends AlignmentViewport implements
     {
       getAlignment().addSequence(al.getSequenceAt(i));
     }
-    // TODO this call was done by SequenceFetcher but not FileLoader or
-    // CutAndPasteTransfer. Is it needed?
-    // JBPComment: this repositions the view to show the new sequences
-    // JBPComment: so it is needed for UX
+
     setEndSeq(getAlignment().getHeight());
     firePropertyChange("alignment", null, getAlignment().getSequences());
   }
 
   /**
    * Show a dialog with the option to open and link (cDNA <-> protein) as a new
-   * alignment. Returns true if the new alignment was opened, false if not,
-   * because the user declined the offer.
+   * alignment, either as a standalone alignment or in a split frame. Returns
+   * true if the new alignment was opened, false if not, because the user
+   * declined the offer.
    * 
+   * @param al
    * @param title
    */
   protected boolean openLinkedAlignment(AlignmentI al, String title)
   {
-    String[] options = new String[]
-    { MessageManager.getString("action.no"),
+    String[] options = new String[] {
+        MessageManager.getString("action.no"),
         MessageManager.getString("label.split_window"),
         MessageManager.getString("label.new_window"), };
     final String question = JvSwingUtils.wrapTooltip(true,
@@ -979,14 +911,6 @@ public class AlignViewport extends AlignmentViewport implements
     final boolean openInNewWindow = (response == 2);
 
     /*
-     * Create the AlignFrame first (which creates the new alignment's datasets),
-     * before attempting sequence mapping.
-     */
-    AlignFrame newAlignFrame = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
-            AlignFrame.DEFAULT_HEIGHT);
-    newAlignFrame.setTitle(title);
-
-    /*
      * Identify protein and dna alignments. Make a copy of this one if opening
      * in a new split pane.
      */
@@ -995,9 +919,24 @@ public class AlignViewport extends AlignmentViewport implements
     AlignmentI protein = al.isNucleotide() ? thisAlignment : al;
     final AlignmentI cdna = al.isNucleotide() ? al : thisAlignment;
 
+    /*
+     * Map sequences. At least one should get mapped as we have already passed
+     * the test for 'mappability'. Any mappings made will be added to the
+     * protein alignment. Note creating dataset sequences on the new alignment
+     * is a pre-requisite for building mappings.
+     */
+    al.setDataset(null);
+    AlignmentUtils.mapProteinToCdna(protein, cdna);
+
+    /*
+     * Create the AlignFrame for the added alignment. If it is protein, mappings
+     * are registered with StructureSelectionManager as a side-effect.
+     */
+    AlignFrame newAlignFrame = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
+            AlignFrame.DEFAULT_HEIGHT);
+    newAlignFrame.setTitle(title);
     newAlignFrame.statusBar.setText(MessageManager.formatMessage(
-            "label.successfully_loaded_file", new Object[]
-            { title }));
+            "label.successfully_loaded_file", new Object[] { title }));
 
     // TODO if we want this (e.g. to enable reload of the alignment from file),
     // we will need to add parameters to the stack.
@@ -1009,40 +948,23 @@ public class AlignViewport extends AlignmentViewport implements
     if (openInNewWindow)
     {
       Desktop.addInternalFrame(newAlignFrame, title,
-              AlignFrame.DEFAULT_WIDTH,
-              AlignFrame.DEFAULT_HEIGHT);
+              AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
     }
 
-    /*
-     * Map sequences. At least one should get mapped as we have already passed
-     * the test for 'mappability'. Any mappings made will be added to the
-     * protein alignment.
-     */
-    AlignmentUtils.mapProteinToCdna(protein, cdna);
-
     try
     {
       newAlignFrame.setMaximum(jalview.bin.Cache.getDefault(
-              "SHOW_FULLSCREEN",
-              false));
+              "SHOW_FULLSCREEN", false));
     } catch (java.beans.PropertyVetoException ex)
     {
     }
 
     if (openSplitPane)
     {
-      protein = openSplitFrame(newAlignFrame, thisAlignment,
-              protein.getCodonFrames());
+      al.alignAs(thisAlignment);
+      protein = openSplitFrame(newAlignFrame, thisAlignment);
     }
 
-    /*
-     * Register the mappings (held on the protein alignment) with the
-     * StructureSelectionManager (for mouseover linking).
-     */
-    final StructureSelectionManager ssm = StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance);
-    ssm.addMappings(protein.getCodonFrames());
-
     return true;
   }
 
@@ -1054,14 +976,15 @@ public class AlignViewport extends AlignmentViewport implements
    *          containing a new alignment to be shown
    * @param complement
    *          cdna/protein complement alignment to show in the other split half
-   * @param mappings
    * @return the protein alignment in the split frame
    */
   protected AlignmentI openSplitFrame(AlignFrame newAlignFrame,
-          AlignmentI complement, Set<AlignedCodonFrame> mappings)
+          AlignmentI complement)
   {
     /*
-     * Open in split pane. DNA sequence above, protein below.
+     * Make a new frame with a copy of the alignment we are adding to. If this
+     * is protein, the mappings to cDNA will be registered with
+     * StructureSelectionManager as a side-effect.
      */
     AlignFrame copyMe = new AlignFrame(complement,
             AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
@@ -1070,19 +993,19 @@ public class AlignViewport extends AlignmentViewport implements
     AlignmentI al = newAlignFrame.viewport.getAlignment();
     final AlignFrame proteinFrame = al.isNucleotide() ? copyMe
             : newAlignFrame;
-    final AlignFrame cdnaFrame = al.isNucleotide() ? newAlignFrame
-            : copyMe;
-    AlignmentI protein = proteinFrame.viewport.getAlignment();
-    protein.setCodonFrames(mappings);
-
+    final AlignFrame cdnaFrame = al.isNucleotide() ? newAlignFrame : copyMe;
     cdnaFrame.setVisible(true);
     proteinFrame.setVisible(true);
     String linkedTitle = MessageManager
             .getString("label.linked_view_title");
+
+    /*
+     * Open in split pane. DNA sequence above, protein below.
+     */
     JInternalFrame splitFrame = new SplitFrame(cdnaFrame, proteinFrame);
     Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
 
-    return protein;
+    return proteinFrame.viewport.getAlignment();
   }
 
   public AnnotationColumnChooser getAnnotationColumnSelectionState()
@@ -1130,4 +1053,58 @@ public class AlignViewport extends AlignmentViewport implements
   {
     this.gatherViewsHere = gatherViewsHere;
   }
+
+  /**
+   * If this viewport has a (Protein/cDNA) complement, then scroll the
+   * complementary alignment to match this one.
+   */
+  public void scrollComplementaryAlignment()
+  {
+    /*
+     * Populate a SearchResults object with the mapped location to scroll to. If
+     * there is no complement, or it is not following highlights, or no mapping
+     * is found, the result will be empty.
+     */
+    SearchResults sr = new SearchResults();
+    int verticalOffset = findComplementScrollTarget(sr);
+    if (!sr.isEmpty())
+    {
+      // TODO would like next line without cast but needs more refactoring...
+      final AlignmentPanel complementPanel = ((AlignViewport) getCodingComplement())
+              .getAlignPanel();
+      complementPanel.setFollowingComplementScroll(true);
+      complementPanel.scrollToCentre(sr, verticalOffset);
+    }
+  }
+
+  /**
+   * Answers true if no alignment holds a reference to the given mapping
+   * 
+   * @param acf
+   * @return
+   */
+  protected boolean noReferencesTo(AlignedCodonFrame acf)
+  {
+    AlignFrame[] frames = Desktop.getAlignFrames();
+    if (frames == null)
+    {
+      return true;
+    }
+    for (AlignFrame af : frames)
+    {
+      if (!af.isClosed())
+      {
+        for (AlignmentViewPanel ap : af.getAlignPanels())
+        {
+          AlignmentI al = ap.getAlignment();
+          if (al != null && al.getCodonFrames().contains(acf))
+          {
+            return false;
+          }
+        }
+      }
+    }
+    return true;
+  }
+
 }