JAL-845 implement alignment of protein to match cDNA alignment
[jalview.git] / src / jalview / gui / AlignFrame.java
index 6a42a65..9604458 100644 (file)
@@ -23,7 +23,6 @@ package jalview.gui;
 import jalview.analysis.AAFrequency;
 import jalview.analysis.AlignmentSorter;
 import jalview.analysis.AlignmentUtils;
-import jalview.analysis.AlignmentUtils.MappingResult;
 import jalview.analysis.Conservation;
 import jalview.analysis.CrossRef;
 import jalview.analysis.Dna;
@@ -33,6 +32,8 @@ import jalview.api.AlignViewControllerGuiI;
 import jalview.api.AlignViewControllerI;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
+import jalview.api.SplitContainerI;
+import jalview.api.ViewStyleI;
 import jalview.api.analysis.ScoreModelI;
 import jalview.bin.Cache;
 import jalview.commands.CommandI;
@@ -88,7 +89,6 @@ import jalview.schemes.TaylorColourScheme;
 import jalview.schemes.TurnColourScheme;
 import jalview.schemes.UserColourScheme;
 import jalview.schemes.ZappoColourScheme;
-import jalview.structure.StructureSelectionManager;
 import jalview.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.ws.jws1.Discoverer;
@@ -382,14 +382,33 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               {
                 if (origview.size() > 0)
                 {
-                  origview.get(0)
-                          .getAlignViewport()
-                          .setViewStyle(
-                                  selviews.get(0).getAlignViewport()
-                                          .getViewStyle());
+                  ViewStyleI vs = selviews.get(0).getAlignViewport()
+                          .getViewStyle();
+                  origview.get(0).getAlignViewport().setViewStyle(vs);
+                  AlignViewportI complement = origview.get(0)
+                          .getAlignViewport().getCodingComplement();
+                  if (complement != null)
+                  {
+                    AlignFrame af = Desktop.getAlignFrameFor(complement);
+                    if (complement.isNucleotide())
+                    {
+                      complement.setViewStyle(vs);
+                      vs.setCharWidth(vs.getCharWidth() / 3);
+                    }
+                    else
+                    {
+                      int rw = vs.getCharWidth();
+                      vs.setCharWidth(rw * 3);
+                      complement.setViewStyle(vs);
+                      vs.setCharWidth(rw);
+                    }
+                    af.alignPanel.updateLayout();
+                    af.setMenusForViewport();
+                  }
                   origview.get(0).updateLayout();
                   origview.get(0).setSelected(true);
                   origview.get(0).alignFrame.setMenusForViewport();
+
                 }
               }
             });
@@ -409,7 +428,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void setFileName(String file, String format)
   {
     fileName = file;
-    currentFileFormat = format;
+    setFileFormat(format);
     reload.setEnabled(true);
   }
 
@@ -749,104 +768,28 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }).start();
   }
 
+  /**
+   * Configure menu items that vary according to whether the alignment is
+   * nucleotide or protein
+   * 
+   * @param nucleotide
+   */
   public void setGUINucleotide(boolean nucleotide)
   {
     showTranslation.setVisible(nucleotide);
-    cdna.setVisible(!nucleotide);
     conservationMenuItem.setEnabled(!nucleotide);
     modifyConservation.setEnabled(!nucleotide);
     showGroupConservation.setEnabled(!nucleotide);
     rnahelicesColour.setEnabled(nucleotide);
     purinePyrimidineColour.setEnabled(nucleotide);
+    showComplementMenuItem.setText(MessageManager
+            .getString(nucleotide ? "label.protein" : "label.nucleotide"));
+    setColourSelected(jalview.bin.Cache.getDefault(
+            nucleotide ? Preferences.DEFAULT_COLOUR_NUC
+                    : Preferences.DEFAULT_COLOUR_PROT, "None"));
   }
 
   /**
-   * Builds codon mappings from this (protein) alignment to any compatible
-   * nucleotide alignments. Mappings are built between sequences with the same
-   * name and compatible lengths. Also makes the cDNA alignment a
-   * CommandListener for the protein alignment so that edits are mirrored.
-   */
-  @Override
-  protected void linkCdna_actionPerformed()
-  {
-    int linkedCount = 0;
-    int alreadyLinkedCount = 0;
-    final AlignmentI thisAlignment = this.alignPanel.getAlignment();
-
-    for (AlignFrame af : Desktop.getAlignFrames())
-    {
-      if (af.alignPanel != null)
-      {
-        final AlignmentI thatAlignment = af.alignPanel.getAlignment();
-        if (thatAlignment.isNucleotide())
-        {
-          MappingResult mapped = AlignmentUtils.mapProteinToCdna(
-                  thisAlignment, thatAlignment);
-          if (mapped == MappingResult.AlreadyMapped)
-          {
-            alreadyLinkedCount++;
-          }
-          else if (mapped == MappingResult.Mapped)
-          {
-            final StructureSelectionManager ssm = StructureSelectionManager
-                    .getStructureSelectionManager(Desktop.instance);
-            ssm.addMappings(thisAlignment.getCodonFrames());
-            // enable the next line to enable linked editing
-            // ssm.addCommandListener(af.getViewport());
-            linkedCount++;
-          }
-        }
-      }
-    }
-    String msg = "";
-    if (linkedCount == 0 && alreadyLinkedCount == 0)
-    {
-      msg = MessageManager.getString("label.no_cdna");
-    }
-    else if (linkedCount > 0)
-    {
-      msg = MessageManager.formatMessage("label.linked_cdna", linkedCount);
-    }
-    else
-    {
-      msg = MessageManager.formatMessage("label.cdna_all_linked",
-              alreadyLinkedCount);
-    }
-    setStatus(msg);
-  }
-
-  /**
-   * Align any linked cDNA to match the alignment of this (protein) alignment.
-   * Any mapped sequence regions will be realigned, unmapped sequences are not
-   * affected.
-   */
-  @Override
-  protected void alignCdna_actionPerformed()
-  {
-    int seqCount = 0;
-    int alignCount = 0;
-    final AlignmentI thisAlignment = this.alignPanel.getAlignment();
-    for (AlignFrame af : Desktop.getAlignFrames())
-    {
-      if (af.alignPanel != null)
-      {
-        final AlignmentI thatAlignment = af.alignPanel.getAlignment();
-        if (thatAlignment.isNucleotide())
-        {
-          int seqsAligned = thatAlignment.alignAs(thisAlignment);
-          seqCount += seqsAligned;
-          if (seqsAligned > 0)
-          {
-            af.alignPanel.alignmentChanged();
-            alignCount++;
-          }
-        }
-      }
-    }
-    setStatus(MessageManager.formatMessage("label.cdna_aligned", seqCount,
-            alignCount));
-  }
-  /**
    * set up menus for the current viewport. This may be called after any
    * operation that affects the data in the current view (selection changed,
    * etc) to update the menus to reflect the new state.
@@ -1517,22 +1460,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   }
 
   /**
-   * close alignPanel2 and shuffle tabs appropriately.
+   * Close the specified panel and close up tabs appropriately.
    * 
-   * @param alignPanel2
+   * @param panelToClose
    */
-  public void closeView(AlignmentPanel alignPanel2)
+  public void closeView(AlignmentPanel panelToClose)
   {
     int index = tabbedPane.getSelectedIndex();
-    int closedindex = tabbedPane.indexOfComponent(alignPanel2);
-    alignPanels.remove(alignPanel2);
-    // Unnecessary
-    // if (viewport == alignPanel2.av)
-    // {
-    // viewport = null;
-    // }
-    alignPanel2.closePanel();
-    alignPanel2 = null;
+    int closedindex = tabbedPane.indexOfComponent(panelToClose);
+    alignPanels.remove(panelToClose);
+    panelToClose.closePanel();
+    panelToClose = null;
 
     tabbedPane.removeTabAt(closedindex);
     tabbedPane.validate();
@@ -1758,18 +1696,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     viewport.getAlignment().moveSelectedSequencesByOne(sg,
             viewport.getHiddenRepSequences(), up);
     alignPanel.paintAlignment(true);
-
-    final AlignViewportI peer = viewport.getCodingComplement();
-    if (peer != null)
-    {
-      final SequenceGroup selectionGroup = peer.getSelectionGroup();
-      if (selectionGroup != null)
-      {
-        peer.getAlignment().moveSelectedSequencesByOne(
-                peer.getSelectionGroup(), peer.getHiddenRepSequences(), up);
-        ((AlignViewport) peer).getAlignPanel().paintAlignment(true);
-      }
-    }
   }
 
   synchronized void slideSequences(boolean right, int size)
@@ -2383,15 +2309,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       return;
     }
 
-    List<SequenceI> seqs = new ArrayList<SequenceI>(sg.getSize());
-    SequenceI seq;
-    for (int i = 0; i < sg.getSize(); i++)
-    {
-      seq = sg.getSequenceAt(i);
-      seqs.add(seq);
-    }
-
-    // If the cut affects all sequences, warn, remove highlighted columns
+    /*
+     * If the cut affects all sequences, warn, remove highlighted columns
+     */
     if (sg.getSize() == viewport.getAlignment().getHeight())
     {
       int confirm = JOptionPane.showConfirmDialog(this,
@@ -2408,15 +2328,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               sg.getEndRes() + 1);
     }
 
-    SequenceI[] cut = new SequenceI[seqs.size()];
-    for (int i = 0; i < seqs.size(); i++)
-    {
-      cut[i] = seqs.get(i);
-    }
+    SequenceI[] cut = sg.getSequences()
+            .toArray(new SequenceI[sg.getSize()]);
 
-    /*
-     * //ADD HISTORY ITEM
-     */
     addHistoryItem(new EditCommand(
             MessageManager.getString("label.cut_sequences"), Action.CUT,
             cut, sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
@@ -2747,32 +2661,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void newView_actionPerformed(ActionEvent e)
   {
-    /*
-     * Note if the current view has a protein/cdna complementary view
-     */
-    AlignViewportI linkedView = this.viewport.getCodingComplement();
-
-    AlignmentPanel newPanel = newView(null, true);
-
-    /*
-     * If the original view has a protein/cdna linked view, make and link a new
-     * view there also.
-     */
-    // TODO refactor the hell out of this - move to a controller, lose the casts
-    // and direct member access, etc
-    if (linkedView != null)
-    {
-      AlignFrame linkedAlignFrame = ((AlignViewport) linkedView)
-              .getAlignPanel().alignFrame;
-      AlignmentPanel newLinkedPanel = linkedAlignFrame.newView(null, true);
-      newLinkedPanel.av.viewName = newPanel.av.viewName;
-      newPanel.av.setCodingComplement(newLinkedPanel.av);
-      final StructureSelectionManager ssm = StructureSelectionManager
-              .getStructureSelectionManager(Desktop.instance);
-      ssm.addCommandListener(newPanel.av);
-      ssm.addCommandListener(newLinkedPanel.av);
-
-    }
+    newView(null, true);
   }
 
   /**
@@ -2800,7 +2689,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       newap.av.getAlignment().deleteAllAnnotations(false);
     }
 
-    newap.av.gatherViewsHere = false;
+    newap.av.setGatherViewsHere(false);
 
     if (viewport.viewName == null)
     {
@@ -2823,7 +2712,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     if (alignPanels.size() == 2)
     {
-      viewport.gatherViewsHere = true;
+      viewport.setGatherViewsHere(true);
     }
     tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1);
     return newap;
@@ -2956,6 +2845,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void followHighlight_actionPerformed()
   {
+    /*
+     * Set the 'follow' flag on the Viewport (and scroll to position if now
+     * true).
+     */
     if (viewport.followHighlight = this.followHighlightMenuItem.getState())
     {
       alignPanel.scrollToPosition(
@@ -3009,7 +2902,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void hideSelSequences_actionPerformed(ActionEvent e)
   {
     viewport.hideAllSelectedSeqs();
-    alignPanel.paintAlignment(true);
+//    alignPanel.paintAlignment(true);
   }
 
   /**
@@ -4832,7 +4725,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           public void actionPerformed(ActionEvent e)
           {
             // TODO: new thread for this call with vis-delay
-            af.showProductsFor(af.viewport.getSequenceSelection(), ds,
+            af.showProductsFor(af.viewport.getSequenceSelection(),
                     isRegSel, dna, source);
           }
 
@@ -4851,14 +4744,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     return showp;
   }
 
-  protected void showProductsFor(SequenceI[] sel, Alignment ds,
-          boolean isRegSel, boolean dna, String source)
+  protected void showProductsFor(final SequenceI[] sel,
+          final boolean isRegSel, final boolean dna, final String source)
   {
-    final boolean fisRegSel = isRegSel;
-    final boolean fdna = dna;
-    final String fsrc = source;
-    final AlignFrame ths = this;
-    final SequenceI[] fsel = sel;
     Runnable foo = new Runnable()
     {
 
@@ -4866,15 +4754,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       public void run()
       {
         final long sttime = System.currentTimeMillis();
-        ths.setProgressBar(MessageManager.formatMessage("status.searching_for_sequences_from", new Object[]{fsrc}), sttime);
+        AlignFrame.this.setProgressBar(MessageManager.formatMessage(
+                "status.searching_for_sequences_from", new Object[]
+                { source }), sttime);
         try
         {
-          Alignment ds = ths.getViewport().getAlignment().getDataset(); // update
-          // our local
-          // dataset
-          // reference
+          // update our local dataset reference
+          Alignment ds = AlignFrame.this.getViewport().getAlignment()
+                  .getDataset();
           Alignment prods = CrossRef
-                  .findXrefSequences(fsel, fdna, fsrc, ds);
+                  .findXrefSequences(sel, dna, source, ds);
           if (prods != null)
           {
             SequenceI[] sprods = new SequenceI[prods.getHeight()];
@@ -4898,16 +4787,38 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             }
             AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH,
                     DEFAULT_HEIGHT);
-            String newtitle = "" + ((fdna) ? "Proteins " : "Nucleotides ")
-                    + " for " + ((fisRegSel) ? "selected region of " : "")
+            String newtitle = "" + ((dna) ? "Proteins" : "Nucleotides")
+                    + " for " + ((isRegSel) ? "selected region of " : "")
                     + getTitle();
-            Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
-                    DEFAULT_HEIGHT);
+            naf.setTitle(newtitle);
+
+            // remove this flag once confirmed we want a split view
+            boolean asSplitFrame = true;
+            if (asSplitFrame)
+            {
+              AlignFrame copyThis = new AlignFrame(
+                      AlignFrame.this.viewport.getAlignment(),
+                      AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+              copyThis.setTitle(AlignFrame.this.getTitle());
+              // SplitFrame with dna above, protein below
+              SplitFrame sf = new SplitFrame(dna ? copyThis : naf,
+                      dna ? naf : copyThis);
+              naf.setVisible(true);
+              copyThis.setVisible(true);
+              String linkedTitle = MessageManager
+                      .getString("label.linked_view_title");
+              Desktop.addInternalFrame(sf, linkedTitle, -1, -1);
+            }
+            else
+            {
+              Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
+                      DEFAULT_HEIGHT);
+            }
           }
           else
           {
             System.err.println("No Sequences generated for xRef type "
-                    + fsrc);
+                    + source);
           }
         } catch (Exception e)
         {
@@ -4921,7 +4832,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           jalview.bin.Cache.log.error("Error when finding crossreferences",
                   e);
         }
-        ths.setProgressBar(MessageManager.formatMessage("status.finished_searching_for_sequences_from", new Object[]{fsrc}),
+        AlignFrame.this.setProgressBar(MessageManager.formatMessage(
+                "status.finished_searching_for_sequences_from",
+                new Object[]
+                { source }),
                 sttime);
       }
 
@@ -4949,7 +4863,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
   /**
    * Construct and display a new frame containing the translation of this
-   * frame's cDNA sequences to their aligned protein (amino acid) equivalents.
+   * frame's DNA sequences to their aligned protein (amino acid) equivalents.
    */
   @Override
   public void showTranslation_actionPerformed(ActionEvent e)
@@ -4985,15 +4899,30 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     else
     {
       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
-      Desktop.addInternalFrame(af, MessageManager.formatMessage(
+      af.setFileFormat(this.currentFileFormat);
+      final String newTitle = MessageManager.formatMessage(
               "label.translation_of_params", new Object[]
-              { this.getTitle() }), DEFAULT_WIDTH, DEFAULT_HEIGHT);
-      // enable next line for linked editing
-      // viewport.getStructureSelectionManager().addCommandListener(viewport);
+              { this.getTitle() });
+      af.setTitle(newTitle);
+      final SequenceI[] seqs = viewport.getSelectionGroup() == null ? viewport
+              .getAlignment().getSequencesArray() : viewport
+              .getSelectionAsNewSequence();
+      viewport.openSplitFrame(af, seqs, al.getCodonFrames());
+      // Desktop.addInternalFrame(af, newTitle, DEFAULT_WIDTH, DEFAULT_HEIGHT);
     }
   }
 
   /**
+   * Set the file format
+   * 
+   * @param fileFormat
+   */
+  public void setFileFormat(String fileFormat)
+  {
+    this.currentFileFormat = fileFormat;
+  }
+
+  /**
    * Try to load a features file onto the alignment.
    * 
    * @param file
@@ -5423,6 +5352,23 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       avc.setViewportAndAlignmentPanel(viewport, alignPanel);
       setMenusFromViewport(viewport);
     }
+
+    /*
+     * If there is a frame linked to this one in a SplitPane, switch it to the
+     * same view tab index. No infinite recursion of calls should happen, since
+     * tabSelectionChanged() should not get invoked on setting the selected
+     * index to an unchanged value. Guard against setting an invalid index
+     * before the new view peer tab has been created.
+     */
+    final AlignViewportI peer = viewport.getCodingComplement();
+    if (peer != null)
+    {
+      AlignFrame linkedAlignFrame = ((AlignViewport) peer).getAlignPanel().alignFrame;
+      if (linkedAlignFrame.tabbedPane.getTabCount() > index)
+      {
+        linkedAlignFrame.tabbedPane.setSelectedIndex(index);
+      }
+    }
   }
 
   /**
@@ -5501,7 +5447,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * 
    * @param av
    */
-  public boolean closeView(AlignmentViewport av)
+  public boolean closeView(AlignViewportI av)
   {
     if (viewport == av)
     {
@@ -5991,9 +5937,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * Open a new alignment window, with the cDNA associated with this (protein)
    * alignment, aligned as is the protein.
    */
-  @Override
   protected void viewAsCdna_actionPerformed()
   {
+    // TODO no longer a menu action - refactor as required
     final AlignmentI alignment = getViewport().getAlignment();
     Set<AlignedCodonFrame> mappings = alignment.getCodonFrames();
     if (mappings == null)
@@ -6032,7 +5978,21 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     Desktop.addInternalFrame(alignFrame, newtitle,
             AlignFrame.DEFAULT_WIDTH,
             AlignFrame.DEFAULT_HEIGHT);
+  }
 
+  /**
+   * Set visibility of dna/protein complement view (available when shown in a
+   * split frame).
+   * 
+   * @param show
+   */
+  @Override
+  protected void showComplement_actionPerformed(boolean show)
+  {
+    SplitContainerI sf = getSplitViewContainer();
+    if (sf != null) {
+      sf.setComplementVisible(this, show);
+    }
   }
 }