Merge branch 'kjvdh/features/PhylogenyViewer_tabbedsupport' into merge/2_11_2/kjvdh...
[jalview.git] / src / jalview / ext / archaeopteryx / JalviewBinding.java
diff --git a/src/jalview/ext/archaeopteryx/JalviewBinding.java b/src/jalview/ext/archaeopteryx/JalviewBinding.java
new file mode 100644 (file)
index 0000000..7a0668d
--- /dev/null
@@ -0,0 +1,452 @@
+package jalview.ext.archaeopteryx;
+
+import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.ext.treeviewer.ExternalTreeViewerBindingI;
+import jalview.gui.PaintRefresher;
+import jalview.structure.SelectionSource;
+import jalview.structure.StructureSelectionManager;
+import jalview.viewmodel.AlignmentViewport;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
+import java.awt.event.MouseEvent;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.swing.JTabbedPane;
+import javax.swing.SwingUtilities;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import org.forester.archaeopteryx.MainFrame;
+import org.forester.phylogeny.Phylogeny;
+import org.forester.phylogeny.PhylogenyMethods;
+import org.forester.phylogeny.PhylogenyNode;
+
+/**
+ * Class for binding the Archaeopteryx tree viewer to the Jalview alignment that
+ * it originates from, meaning that selecting sequences in the tree viewer also
+ * selects them in the alignment view and vice versa.
+ * 
+ * @author kjvanderheide
+ *
+ */
+public final class JalviewBinding
+        implements ExternalTreeViewerBindingI<PhylogenyNode>
+{
+  private final MainFrame aptxFrame;
+
+  private org.forester.archaeopteryx.TreePanel currentTreeView;
+
+  private final AlignmentViewport parentAvport;
+
+  private JTabbedPane treeTabs;
+
+  private final StructureSelectionManager ssm;
+
+  private Map<SequenceI, PhylogenyNode> sequencesBoundToNodes;
+
+  private Map<PhylogenyNode, SequenceI> nodesBoundToSequences;
+
+  /**
+   * 
+   * @param archaeopteryx
+   * 
+   * @param jalviewAlignmentViewport
+   *          alignment viewport from which the tree was calculated.
+   * 
+   * @param alignMappedToNodes
+   *          map with sequences used to calculate the tree and matching tree
+   *          nodes as key, value pair respectively.
+   * 
+   * @param nodesMappedToAlign
+   *          map with tree nodes and matching sequences used to calculate the
+   *          tree as key, value pair respectively.
+   */
+  public JalviewBinding(final MainFrame archaeopteryx,
+          final AlignmentViewport jalviewAlignmentViewport,
+          final Map<SequenceI, PhylogenyNode> alignMappedToNodes,
+          final Map<PhylogenyNode, SequenceI> nodesMappedToAlign)
+  {
+    // deal with/prohibit null values here as that will cause problems
+    aptxFrame = archaeopteryx;
+    parentAvport = jalviewAlignmentViewport;
+    sequencesBoundToNodes = alignMappedToNodes;
+    nodesBoundToSequences = nodesMappedToAlign;
+
+    treeTabs = aptxFrame.getMainPanel().getTabbedPane();
+    ssm = parentAvport.getStructureSelectionManager();
+
+    // archaeopteryx.getMainPanel().getControlPanel().setColorBranches(true);
+    
+    ssm.addSelectionListener(this);
+
+    int tabCount = treeTabs.getTabCount();
+
+    for (int i = 0; i < tabCount; i++)
+    {
+      // roundabout way to select each tree because getComponentAt(i) requires
+      // casting to TreePanel which doesn't work
+      treeTabs.setSelectedIndex(i);
+      currentTreeView = aptxFrame.getMainPanel().getCurrentTreePanel();
+      currentTreeView.addMouseListener(this);
+      PaintRefresher.Register(currentTreeView,
+              parentAvport.getSequenceSetId());
+    }
+
+
+    treeTabs.addChangeListener(new ChangeListener()
+    {
+
+      @Override
+      public void stateChanged(ChangeEvent e)
+      {
+
+        SwingUtilities.invokeLater(new Runnable()
+        {
+
+          @Override
+          /**
+           * Resend the selection to the tree view when tabs get switched, this
+           * has to be buried in invokeLater as Forester first resets the tree
+           * view on switching tabs, without invokeLater this would get called
+           * before Forester resets which would nullify the selection.
+           */
+          public void run()
+          {
+            currentTreeView = aptxFrame.getMainPanel()
+                    .getCurrentTreePanel();
+            parentAvport.sendSelection();
+
+          }
+        });
+
+      }
+      
+    });
+
+  }
+
+  @Override
+  public void actionPerformed(ActionEvent e)
+  {
+  }
+
+  @Override
+  public void mouseClicked(MouseEvent e)
+  {
+    SwingUtilities.invokeLater(new Runnable() {
+
+      @Override
+      /**
+       * invokeLater so that this always runs after Forester's mouseClicked
+       */
+      public void run()
+      {
+
+        final PhylogenyNode node = currentTreeView.findNode(e.getX(),
+                e.getY());
+        if (node != null)
+        {
+          if ((e.getModifiers() & InputEvent.SHIFT_MASK) == 0) // clear previous
+          // selection if shift
+          // IS NOT pressed
+          {
+
+            parentAvport.setSelectionGroup(null);
+
+          }
+          showNodeSelectionOnAlign(node);
+        }
+        else
+        {
+          partitionTree(e);
+        
+        
+      }
+      
+      }
+    });
+
+
+  }
+
+  @Override
+  public void mousePressed(final MouseEvent e)
+  {
+
+  }
+  @Override
+  public void mouseReleased(MouseEvent e)
+  {
+  }
+
+  @Override
+  public void mouseEntered(MouseEvent e)
+  {
+  }
+
+  @Override
+  public void mouseExited(MouseEvent e)
+  {
+  }
+
+
+  @Override
+  public void selection(final SequenceGroup seqsel,
+          final ColumnSelection colsel, final HiddenColumns hidden,
+          final SelectionSource source)
+  {
+    if (source == parentAvport) // check if source is alignment from where the
+    // tree originates
+    {
+      currentTreeView.setFoundNodes0(
+              new HashSet<Long>(seqsel.getSequences().size()));
+
+      for (SequenceI selectedSequence : seqsel.getSequences())
+      {
+        PhylogenyNode matchingNode = sequencesBoundToNodes.get(selectedSequence);
+        if (matchingNode != null)
+        {
+          currentTreeView.getFoundNodes0().add(matchingNode.getId());
+        }
+
+      }
+      aptxFrame.repaint();
+
+    }
+
+
+  }
+
+  /**
+   * Partially refactored from TreeCanvas
+   */
+  public void partitionTree(final MouseEvent e)
+  {
+    int x = e.getX();
+    int lineLength = currentTreeView.getHeight();
+
+    Phylogeny tree = currentTreeView.getPhylogeny();
+    double treeHeight = tree.calculateHeight(true);
+
+
+
+    if (treeHeight != 0)
+    {
+      int viewWidth = currentTreeView.getWidth();
+
+      // treeView.validate();
+
+      // System.out.println("selection");
+      // System.out.println(x);
+      // System.out.println("-------------");
+      // System.out.println("width");
+      // System.out.println(viewWidth);
+
+    }
+
+
+  }
+  
+
+
+  @Override
+  public void showNodeSelectionOnAlign(final PhylogenyNode node)
+  {
+
+      if (node.isInternal())
+      {
+        showMatchingChildSequences(node);
+      }
+
+      else
+      {
+        showMatchingSequence(node);
+      }
+
+    }
+
+
+
+
+
+  @Override
+  public void showMatchingSequence(final PhylogenyNode nodeToMatch)
+  {
+    SequenceI matchingSequence = nodesBoundToSequences.get(nodeToMatch);
+    if (matchingSequence != null)
+    {
+      long nodeId = nodeToMatch.getId();
+      addOrRemoveInSet(currentTreeView.getFoundNodes0(), nodeId);
+      treeSelectionChanged(matchingSequence);
+      parentAvport.sendSelection();
+
+    }
+  }
+
+  @Override
+  public void showMatchingChildSequences(final PhylogenyNode parentNode)
+  {
+    List<PhylogenyNode> childNodes = PhylogenyMethods
+            .getAllDescendants(parentNode);
+
+
+    for (PhylogenyNode childNode : childNodes)
+    {
+      // childNode.getBranchData().setBranchColor(new BranchColor(Color.BLUE));
+
+      SequenceI matchingSequence = nodesBoundToSequences.get(childNode);
+      if (matchingSequence != null)
+      {
+        long nodeId = childNode.getId();
+        addOrRemoveInSet(currentTreeView.getFoundNodes0(), nodeId);
+
+        treeSelectionChanged(matchingSequence);
+
+      }
+
+    }
+    parentAvport.sendSelection();
+
+
+  }
+
+  /**
+   * Refactored from TreeCanvas.
+   * 
+   * @param sequence
+   *          of the node selected in the tree viewer.
+   */
+  @Override
+  public void treeSelectionChanged(final SequenceI sequence)
+  {
+    if (!parentAvport.isClosed()) // alignment view could be closed
+    {
+      SequenceGroup selected = parentAvport.getSelectionGroup();
+
+      if (selected == null)
+      {
+        selected = new SequenceGroup();
+        parentAvport.setSelectionGroup(selected);
+      }
+
+      selected.setEndRes(parentAvport.getAlignment().getWidth() - 1);
+        selected.addOrRemove(sequence, true);
+    }
+
+  }
+  public void sortByTree_actionPerformed() {
+    // parentAvport.mirrorCommand(command, undo, ssm, source);
+
+    // alignFrame
+    // .addHistoryItem(sortAlignmentIn(treeCanvas.ap));
+    
+  }
+  
+
+  /**
+   * sort the associated alignment view by the current tree.
+   * 
+   * @param e
+   */
+  // @Override
+  // public void sortByTree_actionPerformed()// modify for Aptx
+  // {
+  //
+  // // if (treeCanvas.applyToAllViews)
+  //
+  // final ArrayList<CommandI> commands = new ArrayList<>();
+  // for (AlignmentPanel ap : PaintRefresher
+  // .getAssociatedPanels(parentAvport.getSequenceSetId()))
+  // {
+  // commands.add(sortAlignmentIn(ap.av.getAlignPanel()));
+  // }
+  // av.getAlignPanel().alignFrame.addHistoryItem(new CommandI()
+  // {
+  //
+  // @Override
+  // public void undoCommand(AlignmentI[] views)
+  // {
+  // for (CommandI tsort : commands)
+  // {
+  // tsort.undoCommand(views);
+  // }
+  // }
+  //
+  // @Override
+  // public int getSize()
+  // {
+  // return commands.size();
+  // }
+  //
+  // @Override
+  // public String getDescription()
+  // {
+  // return "Tree Sort (many views)";
+  // }
+  //
+  // @Override
+  // public void doCommand(AlignmentI[] views)
+  // {
+  //
+  // for (CommandI tsort : commands)
+  // {
+  // tsort.doCommand(views);
+  // }
+  // }
+  // });
+  // for (AlignmentPanel ap : PaintRefresher
+  // .getAssociatedPanels(av.getSequenceSetId()))
+  // {
+  // // ensure all the alignFrames refresh their GI after adding an undo item
+  // ap.alignFrame.updateEditMenuBar();
+  // }
+  // }
+  // else
+  // {
+  // treeCanvas.ap.alignFrame
+  // .addHistoryItem(sortAlignmentIn(treeCanvas.ap));
+  // }
+
+
+
+  /**
+   * TO BE MOVED
+   * 
+   * @param set
+   * @param objectToCheck
+   */
+  public static <E> void addOrRemoveInSet(Set<E> set, E objectToCheck)
+  {
+    if (set.contains(objectToCheck))
+    {
+      set.remove(objectToCheck);
+    }
+    else
+    {
+      set.add(objectToCheck);
+    }
+
+  }
+
+  public AlignmentViewport getParentAvport()
+  {
+    return parentAvport;
+  }
+
+  public org.forester.archaeopteryx.TreePanel getTreeView()
+  {
+    return currentTreeView;
+  }
+
+}
+
+
+
+