JAL-1705 refactor show products, set initial split frame divider
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Fri, 19 Feb 2016 16:58:45 +0000 (16:58 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Fri, 19 Feb 2016 16:58:45 +0000 (16:58 +0000)
resources/lang/Messages.properties
src/jalview/analysis/CrossRef.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/SplitFrame.java

index 5ce5f46..4ab8732 100644 (file)
@@ -218,6 +218,8 @@ label.above_identity_threshold = Above Identity Threshold
 label.show_sequence_features = Show Sequence Features
 label.nucleotide = Nucleotide
 label.protein = Protein
+label.nucleotides = Nucleotides
+label.proteins = Proteins
 label.to_new_alignment = To New Alignment
 label.to_this_alignment = Add To This Alignment
 label.apply_colour_to_all_groups = Apply Colour To All Groups
index d45750e..2f6076a 100644 (file)
@@ -219,30 +219,23 @@ public class CrossRef
 
   /**
    * 
-   * @param dna
-   * @param seqs
-   * @return
-   */
-  public static Alignment findXrefSequences(SequenceI[] seqs, boolean dna,
-          String source)
-  {
-    return findXrefSequences(seqs, dna, source, null);
-  }
-
-  /**
-   * 
    * @param seqs
    *          sequences whose xrefs are being retrieved
    * @param dna
    *          true if sequences are nucleotide
    * @param source
-   * @param dataset
-   *          alignment to search for product sequences.
+   * @param al
+   *          alignment to search for cross-referenced sequences (and possibly
+   *          add to)
+   * @param addedPeers
+   *          a list of sequences to add to if 'peers' to the original sequences
+   *          are found e.g. alternative protein products for a protein's gene
    * @return products (as dataset sequences)
    */
   public static Alignment findXrefSequences(SequenceI[] seqs, boolean dna,
-          String source, AlignmentI dataset)
+          String source, AlignmentI al, List<SequenceI> addedPeers)
   {
+    AlignmentI dataset = al.getDataset() == null ? al : al.getDataset();
     List<SequenceI> rseqs = new ArrayList<SequenceI>();
     AlignedCodonFrame cf = new AlignedCodonFrame();
     for (SequenceI seq : seqs)
@@ -389,10 +382,12 @@ public class CrossRef
                           int sf = map.getMap().getToLowest();
                           int st = map.getMap().getToHighest();
                           SequenceI mappedrg = ms.getSubSequence(sf, st);
-                          SequenceI loc = dss.getSubSequence(sf, st);
+                          // SequenceI loc = dss.getSubSequence(sf, st);
                           if (mappedrg.getLength() > 0
-                                  && mappedrg.getSequenceAsString().equals(
-                                          loc.getSequenceAsString()))
+                                  && ms.getSequenceAsString().equals(
+                                          dss.getSequenceAsString()))
+                          // && mappedrg.getSequenceAsString().equals(
+                          // loc.getSequenceAsString()))
                           {
                             String msg = "Mapping updated from "
                                     + ms.getName()
@@ -414,8 +409,8 @@ public class CrossRef
                               for (SequenceFeature feat : sfs)
                               {
                                 /* 
-                                 * we override the equality test here (but not
-                                 * elsewhere) to ignore Parent attribute
+                                 * we override SequenceFeature.equals here (but
+                                 * not elsewhere) to ignore Parent attribute
                                  * TODO not quite working yet!
                                  */
                                 if (!copiedFeatures
@@ -430,6 +425,12 @@ public class CrossRef
                             cf.addMap(retrieved[rs].getDatasetSequence(),
                                     dss, map.getMap());
                           }
+                          else
+                          {
+                            addedPeers.add(map.getTo());
+                            cf.addMap(retrieved[rs].getDatasetSequence(),
+                                    map.getTo(), map.getMap());
+                          }
                         } catch (Exception e)
                         {
                           System.err
@@ -452,9 +453,7 @@ public class CrossRef
     Alignment ral = null;
     if (rseqs.size() > 0)
     {
-      SequenceI[] rsqs = new SequenceI[rseqs.size()];
-      rseqs.toArray(rsqs);
-      ral = new Alignment(rsqs);
+      ral = new Alignment(rseqs.toArray(new SequenceI[rseqs.size()]));
       if (cf != null && !cf.isEmpty())
       {
         ral.addCodonFrame(cf);
index 85f7d19..c93b84b 100644 (file)
@@ -904,7 +904,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     rnahelicesColour.setEnabled(av.getAlignment().hasRNAStructure());
     rnahelicesColour
             .setSelected(av.getGlobalColourScheme() instanceof jalview.schemes.RNAHelicesColour);
-    setShowProductsEnabled();
+
+    showProducts.setEnabled(canShowProducts());
+
     updateEditMenuBar();
   }
 
@@ -4652,67 +4654,27 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
   }
 
-  /*
-   * public void vamsasStore_actionPerformed(ActionEvent e) { JalviewFileChooser
-   * chooser = new JalviewFileChooser(jalview.bin.Cache.
-   * getProperty("LAST_DIRECTORY"));
-   * 
-   * chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle("Export
-   * to Vamsas file"); chooser.setToolTipText("Export");
-   * 
-   * int value = chooser.showSaveDialog(this);
-   * 
-   * if (value == JalviewFileChooser.APPROVE_OPTION) {
-   * jalview.io.VamsasDatastore vs = new jalview.io.VamsasDatastore(viewport);
-   * //vs.store(chooser.getSelectedFile().getAbsolutePath() ); vs.storeJalview(
-   * chooser.getSelectedFile().getAbsolutePath(), this); } }
-   */
   /**
-   * prototype of an automatically enabled/disabled analysis function
+   * Searches selected sequences for xRef products and builds the Show
+   * Cross-References menu (formerly called Show Products)
    * 
+   * @return true if Show Cross-references menu should be enabled.
    */
-  protected void setShowProductsEnabled()
+  public boolean canShowProducts()
   {
     SequenceI[] selection = viewport.getSequenceSelection();
-    if (canShowProducts(selection, viewport.getSelectionGroup() != null,
-            viewport.getAlignment().getDataset()))
-    {
-      showProducts.setEnabled(true);
-
-    }
-    else
-    {
-      showProducts.setEnabled(false);
-    }
-  }
-
-  /**
-   * search selection for sequence xRef products and build the show products
-   * menu.
-   * 
-   * @param selection
-   * @param dataset
-   * @return true if showProducts menu should be enabled.
-   */
-  public boolean canShowProducts(SequenceI[] selection,
-          boolean isRegionSelection, Alignment dataset)
-  {
+    AlignmentI dataset = viewport.getAlignment().getDataset();
     boolean showp = false;
     try
     {
       showProducts.removeAll();
       final boolean dna = viewport.getAlignment().isNucleotide();
-      final Alignment ds = dataset;
       String[] ptypes = (selection == null || selection.length == 0) ? null
               : CrossRef.findSequenceXrefTypes(dna, selection, dataset);
-      // Object[] prods =
-      // CrossRef.buildXProductsList(viewport.getAlignment().isNucleotide(),
-      // selection, dataset, true);
-      final SequenceI[] sel = selection;
+
       for (int t = 0; ptypes != null && t < ptypes.length; t++)
       {
         showp = true;
-        final boolean isRegSel = isRegionSelection;
         final AlignFrame af = this;
         final String source = ptypes[t];
         JMenuItem xtype = new JMenuItem(ptypes[t]);
@@ -4722,9 +4684,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           @Override
           public void actionPerformed(ActionEvent e)
           {
-            // TODO: new thread for this call with vis-delay
-            af.showProductsFor(af.viewport.getSequenceSelection(),
-                    isRegSel, dna, source);
+            showProductsFor(af.viewport.getSequenceSelection(), dna, source);
           }
 
         });
@@ -4735,15 +4695,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     } catch (Exception e)
     {
       jalview.bin.Cache.log
-              .warn("canTranslate threw an exception - please report to help@jalview.org",
+              .warn("canShowProducts threw an exception - please report to help@jalview.org",
                       e);
       return false;
     }
     return showp;
   }
 
-  protected void showProductsFor(final SequenceI[] sel,
-          final boolean isRegSel, final boolean dna, final String source)
+  protected void showProductsFor(final SequenceI[] sel, final boolean dna,
+          final String source)
   {
     Runnable foo = new Runnable()
     {
@@ -4757,27 +4717,18 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 new Object[] { source }), sttime);
         try
         {
-          // update our local dataset reference
-          Alignment ds = AlignFrame.this.getViewport().getAlignment()
-                  .getDataset();
-          Alignment prods = CrossRef
-                  .findXrefSequences(sel, dna, source, ds);
-          if (prods != null)
+          /*
+           * 'peer' sequences are any to add to this alignment, for example
+           * alternative protein products for my protein's gene
+           */
+          List<SequenceI> addedPeers = new ArrayList<SequenceI>();
+          AlignmentI alignment = AlignFrame.this.getViewport().getAlignment();
+          Alignment xrefs = CrossRef.findXrefSequences(sel, dna, source,
+                  alignment, addedPeers);
+          if (xrefs != null)
           {
-            SequenceI[] sprods = new SequenceI[prods.getHeight()];
-            for (int s = 0; s < sprods.length; s++)
-            {
-              sprods[s] = (prods.getSequenceAt(s)).deriveSequence();
-              if (ds.getSequences() == null
-                      || !ds.getSequences().contains(
-                              sprods[s].getDatasetSequence()))
-              {
-                ds.addSequence(sprods[s].getDatasetSequence());
-              }
-              sprods[s].updatePDBIds();
-            }
-            Alignment al = new Alignment(sprods);
-            al.setDataset(ds);
+            Alignment al = makeCrossReferencesAlignment(
+                    alignment.getDataset(), xrefs);
 
             /*
              * Copy dna-to-protein mappings to new alignment
@@ -4785,16 +4736,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             // TODO 1: no mappings are set up for EMBL product
             // TODO 2: if they were, should add them to protein alignment, not
             // dna
-            List<AlignedCodonFrame> cf = prods.getCodonFrames();
-            for (AlignedCodonFrame acf : cf)
-            {
-              al.addCodonFrame(acf);
-            }
+            // List<AlignedCodonFrame> cf = xrefs.getCodonFrames();
+            // for (AlignedCodonFrame acf : cf)
+            // {
+            // al.addCodonFrame(acf);
+            // }
             AlignFrame newFrame = new AlignFrame(al, DEFAULT_WIDTH,
                     DEFAULT_HEIGHT);
-            String newtitle = "" + (dna ? "Proteins" : "Nucleotides")
-                    + " for " + (isRegSel ? "selected region of " : "")
-                    + getTitle();
+            String newtitle = String.format("%s %s %s",
+                    MessageManager.getString(dna ? "label.proteins"
+                            : "label.nucleotides"), MessageManager
+                            .getString("label.for"), getTitle());
             newFrame.setTitle(newtitle);
 
             boolean asSplitFrame = Cache.getDefault(
@@ -4808,25 +4760,50 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               AlignmentI copyAlignment = null;
               final SequenceI[] sequenceSelection = AlignFrame.this.viewport
                       .getSequenceSelection();
+              List<AlignedCodonFrame> cf = xrefs.getCodonFrames();
               if (dna)
               {
                 copyAlignment = AlignmentUtils.makeCdsAlignment(
                         sequenceSelection, cf);
                 al.getCodonFrames().clear();
                 al.getCodonFrames().addAll(cf);
-                final StructureSelectionManager ssm = StructureSelectionManager
-                        .getStructureSelectionManager(Desktop.instance);
-                ssm.registerMappings(cf);
               }
               else
               {
                 copyAlignment = new Alignment(new Alignment(
                         sequenceSelection));
+                copyAlignment.getCodonFrames().addAll(cf);
               }
+              StructureSelectionManager ssm = StructureSelectionManager
+                      .getStructureSelectionManager(Desktop.instance);
+              ssm.registerMappings(cf);
+
+              /*
+               * add in any extra 'peer' sequences discovered
+               * (e.g. alternative protein products)
+               */
+              for (SequenceI peer : addedPeers)
+              {
+                copyAlignment.addSequence(peer);
+              }
+
+              /*
+               * align protein to dna
+               */
+              // TODO needs debugging
+              // if (dna)
+              // {
+              // al.alignAs(copyAlignment);
+              // }
+              // else
+              // {
+              // copyAlignment.alignAs(al);
+              // }
+
               AlignFrame copyThis = new AlignFrame(copyAlignment,
                       AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
               copyThis.setTitle(AlignFrame.this.getTitle());
-              // SplitFrame with dna above, protein below
+
               boolean showSequenceFeatures = viewport
                       .isShowSequenceFeatures();
               newFrame.setShowSeqFeatures(showSequenceFeatures);
@@ -4849,6 +4826,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               String linkedTitle = MessageManager
                       .getString("label.linked_view_title");
               Desktop.addInternalFrame(sf, linkedTitle, -1, -1);
+              sf.adjustDivider();
             }
             else
             {
@@ -4878,6 +4856,32 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 new Object[] { source }), sttime);
       }
 
+      /**
+       * @param alignment
+       * @param prods
+       * @return
+       */
+      protected Alignment makeCrossReferencesAlignment(
+Alignment dataset,
+              Alignment prods)
+      {
+        SequenceI[] sprods = new SequenceI[prods.getHeight()];
+        for (int s = 0; s < sprods.length; s++)
+        {
+          sprods[s] = (prods.getSequenceAt(s)).deriveSequence();
+          if (dataset.getSequences() == null
+                  || !dataset.getSequences().contains(
+                          sprods[s].getDatasetSequence()))
+          {
+            dataset.addSequence(sprods[s].getDatasetSequence());
+          }
+          sprods[s].updatePDBIds();
+        }
+        Alignment al = new Alignment(sprods);
+        al.setDataset(dataset);
+        return al;
+      }
+
     };
     Thread frunner = new Thread(foo);
     frunner.start();
index 083c7ec..617224f 100644 (file)
@@ -61,6 +61,14 @@ import javax.swing.event.InternalFrameEvent;
  */
 public class SplitFrame extends GSplitFrame implements SplitContainerI
 {
+  private static final int WINDOWS_INSETS_WIDTH = 28; // tbc
+
+  private static final int MAC_INSETS_WIDTH = 28;
+
+  private static final int WINDOWS_INSETS_HEIGHT = 50; // tbc
+
+  private static final int MAC_INSETS_HEIGHT = 50;
+  private static final int DESKTOP_DECORATORS_HEIGHT = 65;
   private static final long serialVersionUID = 1L;
 
   public SplitFrame(GAlignFrame top, GAlignFrame bottom)
@@ -86,8 +94,10 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
      * estimate width and height of SplitFrame; this.getInsets() doesn't seem to
      * give the full additional size (a few pixels short)
      */
-    int widthFudge = Platform.isAMac() ? 28 : 28; // Windows tbc
-    int heightFudge = Platform.isAMac() ? 50 : 50; // tbc
+    int widthFudge = Platform.isAMac() ? MAC_INSETS_WIDTH
+            : WINDOWS_INSETS_WIDTH;
+    int heightFudge = Platform.isAMac() ? MAC_INSETS_HEIGHT
+            : WINDOWS_INSETS_HEIGHT;
     int width = ((AlignFrame) getTopFrame()).getWidth() + widthFudge;
     int height = ((AlignFrame) getTopFrame()).getHeight()
             + ((AlignFrame) getBottomFrame()).getHeight() + DIVIDER_SIZE
@@ -118,7 +128,8 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
   {
     // allow about 65 pixels for Desktop decorators on Windows
 
-    int newHeight = Math.min(height, Desktop.instance.getHeight() - 65);
+    int newHeight = Math.min(height, Desktop.instance.getHeight()
+            - DESKTOP_DECORATORS_HEIGHT);
     if (newHeight != height)
     {
       int oldDividerLocation = getDividerLocation();
@@ -182,6 +193,40 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
   }
 
   /**
+   * Adjust the divider for a sensible split of the real estate (for example,
+   * when many transcripts are shown with a single protein). This should only be
+   * called after the split pane has been laid out (made visible) so it has a
+   * height.
+   */
+  protected void adjustDivider()
+  {
+    final AlignViewport topViewport = ((AlignFrame) getTopFrame()).viewport;
+    final AlignViewport bottomViewport = ((AlignFrame) getBottomFrame()).viewport;
+    final AlignmentI topAlignment = topViewport.getAlignment();
+    final AlignmentI bottomAlignment = bottomViewport.getAlignment();
+    boolean topAnnotations = topViewport.isShowAnnotation();
+    boolean bottomAnnotations = bottomViewport.isShowAnnotation();
+    int topCount = topAlignment.getHeight();
+    int bottomCount = bottomAlignment.getHeight();
+    int topCharHeight = topViewport.getViewStyle().getCharHeight();
+    int bottomCharHeight = bottomViewport.getViewStyle().getCharHeight();
+
+    /*
+     * estimate ratio of (topFrameContent / bottomFrameContent)
+     */
+    int insets = Platform.isAMac() ? MAC_INSETS_HEIGHT
+            : WINDOWS_INSETS_HEIGHT;
+    // allow 3 'rows' for scale, scrollbar, status bar
+    int topHeight = insets + (3 + topCount) * topCharHeight
+            + (topAnnotations ? topViewport.calcPanelHeight() : 0);
+    int bottomHeight = insets + (3 + bottomCount) * bottomCharHeight
+            + (bottomAnnotations ? bottomViewport.calcPanelHeight() : 0);
+    double ratio = ((double) topHeight) / (topHeight + bottomHeight);
+
+    setRelativeDividerLocation(ratio);
+  }
+
+  /**
    * Add a listener to tidy up when the frame is closed.
    */
   protected void addCloseFrameListener()