JAL-845 code/refactoring/tests related to linking DNA and protein
[jalview.git] / src / jalview / gui / AlignViewport.java
index ff3e329..5a9fa6b 100644 (file)
  */
 package jalview.gui;
 
+import jalview.analysis.AlignmentUtils;
+import jalview.analysis.AlignmentUtils.MappingResult;
 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
 import jalview.analysis.NJTree;
 import jalview.api.AlignViewportI;
 import jalview.bin.Cache;
 import jalview.commands.CommandI;
-import jalview.commands.EditCommand;
+import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.PDBEntry;
@@ -56,6 +58,7 @@ import jalview.structure.CommandListener;
 import jalview.structure.SelectionSource;
 import jalview.structure.StructureSelectionManager;
 import jalview.structure.VamsasSource;
+import jalview.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.ws.params.AutoCalcSetting;
 
@@ -67,9 +70,12 @@ import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Deque;
 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!
  * 
@@ -1107,6 +1113,9 @@ public class AlignViewport extends AlignmentViewport implements
 
   boolean showSeqFeaturesHeight;
 
+  /**
+   * Send the current selection to be broadcast to any selection listeners.
+   */
   public void sendSelection()
   {
     jalview.structure.StructureSelectionManager
@@ -1317,23 +1326,30 @@ public class AlignViewport extends AlignmentViewport implements
    */
   @Override
   public void mirrorCommand(CommandI command, boolean undo,
-          StructureSelectionManager ssm)
+          StructureSelectionManager ssm, VamsasSource source)
   {
     /*
-     * Only EditCommand is currently handled by listeners.
+     * ...work in progress... do nothing unless we are a 'complement' of the
+     * source May replace this with direct calls not via SSM.
      */
-    if (!(command instanceof EditCommand))
+    if (source instanceof AlignViewportI
+            && ((AlignViewportI) source).getCodingComplement() == this)
+    {
+      // ok to continue;
+    }
+    else
     {
       return;
     }
-    EditCommand edit = (EditCommand) command;
 
-    List<SequenceI> seqs = getAlignment().getSequences();
-    EditCommand mappedCommand = ssm.mapEditCommand(edit, undo, seqs,
+    CommandI mappedCommand = ssm.mapCommand(command, undo, getAlignment(),
             getGapCharacter());
-    AlignmentI[] views = getAlignPanel().alignFrame.getViewAlignments();
-    mappedCommand.performEdit(0, views);
-    getAlignPanel().alignmentChanged();
+    if (mappedCommand != null)
+    {
+      AlignmentI[] views = getAlignPanel().alignFrame.getViewAlignments();
+      mappedCommand.doCommand(views);
+      getAlignPanel().alignmentChanged();
+    }
   }
 
   @Override
@@ -1405,4 +1421,180 @@ public class AlignViewport extends AlignmentViewport implements
   {
     return this.redoList;
   }
+
+  /**
+   * Add the sequences from the given alignment to this viewport. Optionally,
+   * may give the user the option to open a new frame, or split panel, with cDNA
+   * and protein linked.
+   * 
+   * @param al
+   * @param title
+   */
+  public void addAlignment(AlignmentI al, String title)
+  {
+    // TODO: promote to AlignViewportI? applet CutAndPasteTransfer is different
+
+    // refactored from FileLoader / CutAndPasteTransfer / SequenceFetcher with
+    // this comment:
+    // 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 (getAlignment().isNucleotide() != al.isNucleotide())
+    {
+      final Set<String> sequenceNames = getAlignment().getSequenceNames();
+      sequenceNames.retainAll(al.getSequenceNames());
+      if (!sequenceNames.isEmpty()) // at least one sequence name in both
+      {
+        if (openLinkedAlignment(al, title))
+        {
+          return;
+        }
+      }
+    }
+
+    for (int i = 0; i < al.getHeight(); i++)
+    {
+      getAlignment().addSequence(al.getSequenceAt(i));
+    }
+    // TODO this call was done by SequenceFetcher but not FileLoader or
+    // CutAndPasteTransfer. Is it needed?
+    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.
+   * 
+   * @param title
+   */
+  protected boolean openLinkedAlignment(AlignmentI al, String title)
+  {
+    String[] options = new String[]
+    { MessageManager.getString("action.no"),
+        MessageManager.getString("label.split_window"),
+        MessageManager.getString("label.new_window"), };
+    final String question = JvSwingUtils.wrapTooltip(true,
+            MessageManager.getString("label.open_linked_alignment?"));
+    int response = JOptionPane.showOptionDialog(Desktop.desktop, question,
+            MessageManager.getString("label.open_linked_alignment"),
+            JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null,
+            options, options[0]);
+
+    if (response != 1 && response != 2)
+    {
+      return false;
+    }
+    final boolean openSplitPane = (response == 1);
+    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);
+
+    /*
+     * Identify protein and dna alignments. Make a copy of this one if opening
+     * in a new split pane.
+     */
+    AlignmentI thisAlignment = openSplitPane ? new Alignment(getAlignment())
+            : getAlignment();
+    final AlignmentI protein = al.isNucleotide() ? thisAlignment : al;
+    final AlignmentI cdna = al.isNucleotide() ? al : thisAlignment;
+
+    newAlignFrame.statusBar.setText(MessageManager.formatMessage(
+            "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.
+    // if (!protocol.equals(AppletFormatAdapter.PASTE))
+    // {
+    // alignFrame.setFileName(file, format);
+    // }
+
+    if (openInNewWindow)
+    {
+      Desktop.addInternalFrame(newAlignFrame, title,
+              AlignFrame.DEFAULT_WIDTH,
+              AlignFrame.DEFAULT_HEIGHT);
+    }
+
+    /*
+     * Try to find mappings for at least one sequence.
+     */
+    MappingResult mapped = AlignmentUtils.mapProteinToCdna(protein, cdna);
+    final StructureSelectionManager ssm = StructureSelectionManager
+            .getStructureSelectionManager(Desktop.instance);
+    if (mapped == MappingResult.Mapped)
+    {
+
+      /*
+       * Register the mappings (held on the protein alignment) with the
+       * StructureSelectionManager (for mouseover linking).
+       */
+      ssm.addMappings(protein.getCodonFrames());
+    }
+    else
+    {
+
+      /*
+       * No mapping possible - warn the user, but leave window open.
+       */
+      final String msg = JvSwingUtils.wrapTooltip(true,
+              MessageManager.getString("label.mapping_failed"));
+      JOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
+              MessageManager.getString("label.no_mappings"),
+              JOptionPane.WARNING_MESSAGE);
+    }
+
+    try
+    {
+      newAlignFrame.setMaximum(jalview.bin.Cache.getDefault(
+              "SHOW_FULLSCREEN",
+              false));
+    } catch (java.beans.PropertyVetoException ex)
+    {
+    }
+
+    if (openSplitPane)
+    {
+      /*
+       * Open in split pane. DNA sequence above, protein below.
+       */
+      AlignFrame copyMe = new AlignFrame(thisAlignment,
+              AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+      copyMe.setTitle(""); // TODO would like this AlignFrame.title here
+      final AlignFrame proteinFrame = al.isNucleotide() ? copyMe
+              : newAlignFrame;
+      final AlignFrame cdnaFrame = al.isNucleotide() ? newAlignFrame
+              : copyMe;
+      newAlignFrame.setTitle(title);
+
+      cdnaFrame.setVisible(true);
+      proteinFrame.setVisible(true);
+      JInternalFrame splitFrame = new SplitFrame(cdnaFrame, proteinFrame);
+      Desktop.addInternalFrame(splitFrame, title, AlignFrame.DEFAULT_WIDTH,
+              AlignFrame.DEFAULT_HEIGHT);
+
+      /*
+       * Set the frames to list for each other's edit and sort commands.
+       */
+      ssm.addCommandListener(cdnaFrame.getViewport());
+      ssm.addCommandListener(proteinFrame.getViewport());
+
+      /*
+       * cDNA view will mirror edits, selection, sorting, show/hide on protein
+       */
+      proteinFrame.getViewport().setCodingComplement(cdnaFrame.getViewport());
+    }
+
+    return true;
+  }
 }