JAL-2847 various error popups for db querying now have formatted strings
[jalview.git] / src / jalview / ext / archaeopteryx / JalviewBinding.java
index 2331204..80b2e50 100644 (file)
@@ -1,33 +1,51 @@
 package jalview.ext.archaeopteryx;
 
+import jalview.analysis.AlignmentSorter;
+import jalview.analysis.Conservation;
+import jalview.api.AlignViewportI;
+import jalview.commands.CommandI;
+import jalview.commands.OrderCommand;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.ext.treeviewer.ExternalTreeViewerBindingI;
+import jalview.gui.AlignViewport;
+import jalview.gui.AlignmentPanel;
 import jalview.gui.Desktop;
 import jalview.gui.JvOptionPane;
 import jalview.gui.PaintRefresher;
+import jalview.schemes.ColourSchemeI;
+import jalview.schemes.ColourSchemeProperty;
+import jalview.schemes.UserColourScheme;
 import jalview.structure.SelectionSource;
 import jalview.structure.StructureSelectionManager;
+import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
 
-import java.awt.Graphics;
+import java.awt.Color;
 import java.awt.event.ActionEvent;
 import java.awt.event.InputEvent;
 import java.awt.event.MouseEvent;
+import java.util.ArrayList;
 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 javax.swing.event.InternalFrameAdapter;
+import javax.swing.event.InternalFrameEvent;
 
 import org.forester.archaeopteryx.MainFrame;
 import org.forester.phylogeny.Phylogeny;
 import org.forester.phylogeny.PhylogenyMethods;
 import org.forester.phylogeny.PhylogenyNode;
+import org.forester.phylogeny.data.BranchColor;
 
 /**
  * Class for binding the Archaeopteryx tree viewer to the Jalview alignment that
@@ -44,13 +62,24 @@ public final class JalviewBinding
 
   private AlignmentViewport parentAvport;
 
+  private final JTabbedPane treeTabs;
 
   private final StructureSelectionManager ssm;
 
+  private AlignmentPanel[] associatedPanels;
+
   private Map<SequenceI, PhylogenyNode> sequencesBoundToNodes;
 
   private Map<PhylogenyNode, SequenceI> nodesBoundToSequences;
 
+  private float rootX;
+
+  private float furthestNodeX;
+
+  private int nrTreeGroups = 0;
+
+  private boolean applyToAllViews = false;
+
   /**
    * 
    * @param archaeopteryx
@@ -87,46 +116,56 @@ public final class JalviewBinding
     nodesBoundToSequences = nodesMappedToAlign;
 
     treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
+    treeTabs = archaeopteryx.getMainPanel().getTabbedPane();
     ssm = parentAvport.getStructureSelectionManager();
-
-
-    // archaeopteryx.getMainPanel().getControlPanel().setColorBranches(true);
     
     ssm.addSelectionListener(this);
     treeView.addMouseListener(this);
     PaintRefresher.Register(treeView, parentAvport.getSequenceSetId());
+    associatedPanels = PaintRefresher
+            .getAssociatedPanels(parentAvport.getSequenceSetId());
 
+    archaeopteryx.addInternalFrameListener(new InternalFrameAdapter()
+    {
 
-    // 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()
-    // {
-    // treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
-    // parentAvport.sendSelection();
-    // // PaintRefresher.Refresh(treeView,
-    // // parentAvport.getSequenceSetId());
-    //
-    // }
-    // });
-    //
-    // }
-    //
-    // });
+      @Override
+      public void internalFrameClosed(InternalFrameEvent e)
+      {
+        AptxInit.getAllAptxFrames().remove(archaeopteryx);
+      }
+
+    });
+
+    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()
+          {
+            treeView = archaeopteryx.getMainPanel().getCurrentTreePanel();
+            parentAvport.sendSelection();
+            // PaintRefresher.Refresh(treeView,
+            // parentAvport.getSequenceSetId());
+
+          }
+        });
+
+      }
+
+    });
 
   }
 
@@ -160,11 +199,14 @@ public final class JalviewBinding
         }
         else
         {
-          partitionTree(e.getX());
 
+          partitionTree(e.getX());
       }
-      
         PaintRefresher.Refresh(treeView, parentAvport.getSequenceSetId());
+        treeView.repaint();
+
+
+
       }
     });
 
@@ -209,11 +251,22 @@ public final class JalviewBinding
         if (matchingNode != null)
         {
           treeView.getFoundNodes0().add(matchingNode.getId());
+
+
+          if (!matchingNode.getBranchData().isHasBranchColor())
+          {
+            Color foundNodesColour = treeView.getTreeColorSet()
+                    .getFoundColor0();
+          matchingNode.getBranchData()
+                    .setBranchColor(new BranchColor(foundNodesColour));
+
+          }
+
         }
 
       }
-      treeView.repaint();
 
+      treeView.repaint();
     }
 
 
@@ -225,29 +278,212 @@ public final class JalviewBinding
   public void partitionTree(final int x)
   {
     Phylogeny tree = treeView.getPhylogeny();
+
     if (!tree.isEmpty())
     {
-      double treeDepth = tree.calculateHeight(true);
-      if (treeDepth != 0)
+      // should be calculated on each partition as the tree can theoretically
+      // change in the meantime
+      PhylogenyNode furthestNode = PhylogenyMethods
+              .calculateNodeWithMaxDistanceToRoot(tree);
+      furthestNodeX = furthestNode.getXcoord();
+      rootX = tree.getRoot().getXcoord();
+
+      if (furthestNodeX != rootX && !(x < rootX || x > furthestNodeX)) // don't
+                                                                       // bother
+                                                                       // if 0
+                                                       // distance tree or
+                                                       // clicked x lies outside
+                                                       // of tree
+      {
+
+        float threshold = (x - rootX) / (furthestNodeX - rootX);
+        List<PhylogenyNode> foundNodes = getNodesAboveThreshold(threshold,
+                tree.getRoot());
+
+      }
+    }
+
+
+  }
+
+  public List<PhylogenyNode> getNodesAboveThreshold(double threshold,
+          PhylogenyNode node)
+  {
+
+    List<PhylogenyNode> nodesAboveThreshold = new ArrayList<>();
+
+    parentAvport.setSelectionGroup(null);
+    parentAvport.getAlignment().deleteAllGroups();
+    parentAvport.clearSequenceColours();
+    if (parentAvport.getCodingComplement() != null)
+    {
+      parentAvport.getCodingComplement().setSelectionGroup(null);
+      parentAvport.getCodingComplement().getAlignment().deleteAllGroups();
+      parentAvport.getCodingComplement().clearSequenceColours();
+    }
+
+
+    colourNodesAboveThreshold(nodesAboveThreshold, threshold,
+            node);
+    return nodesAboveThreshold;
+
+  }
+
+  /**
+   * Partially refactored from TreeCanvas colourGroups (can be made nicer).
+   * 
+   * @param nodeList
+   * @param threshold
+   * @param treeLength
+   * @param node
+   * @return
+   */
+  private List<PhylogenyNode> colourNodesAboveThreshold(
+          List<PhylogenyNode> nodeList, double threshold,
+          PhylogenyNode node)
+  {
+
+    for (PhylogenyNode childNode : node.getDescendants())
+    {
+      childNode.getBranchData()
+              .setBranchColor(new BranchColor(Color.black));
+      float nodeCutoff = (childNode.getXcoord() - rootX)
+              / (furthestNodeX - rootX);
+
+      if (nodeCutoff > threshold)
       {
-        Graphics g = treeView.getGraphics();
-        int panelHeight = treeView.getHeight();
-        g.drawLine(x, 0, x, panelHeight);
+        nodeList.add(childNode);
 
+        Color randomColour = new Color((int) (Math.random() * 255),
+                (int) (Math.random() * 255), (int) (Math.random() * 255));
+        childNode.getBranchData()
+                .setBranchColor(new BranchColor(randomColour));
+
+        List<SequenceI> groupSeqs = new ArrayList<>();
+        SequenceI seq = nodesBoundToSequences.get(childNode);
+        if (seq != null)
+        {
+          groupSeqs.add(seq);
+          parentAvport.setSequenceColour(seq, randomColour);
+        }
 
-        // int viewWidth = treeView.getWidth();
+        List<PhylogenyNode> descendantNodes = PhylogenyMethods
+                .getAllDescendants(childNode);
+        // .forEach instead?
+        for (PhylogenyNode descNode : descendantNodes)
+        {
+          seq = nodesBoundToSequences.get(descNode);
+          if (seq != null)
+          {
+            groupSeqs.add(seq);
+            parentAvport.setSequenceColour(seq, randomColour);
+          }
+
+          descNode.getBranchData()
+                  .setBranchColor(new BranchColor(randomColour));
+        }
 
-        // System.out.println("selection");
-        // System.out.println(x);
-        // System.out.println("-------------");
-        // System.out.println("width");
-        // System.out.println(viewWidth);
+        if (groupSeqs != null)
+        {
+          nrTreeGroups++;
+          groupThresholdSequences(groupSeqs, randomColour);
+        }}
+
+      else
+      {
+        colourNodesAboveThreshold(nodeList, threshold, childNode);
       }
     }
 
 
+        for (AlignmentPanel associatedPanel : associatedPanels) {
+
+        associatedPanel.updateAnnotation();
+
+        final AlignViewportI codingComplement = associatedPanel.getAlignViewport()
+                .getCodingComplement();
+        if (codingComplement != null)
+        {
+          // GROSS
+          ((AlignViewport) codingComplement).getAlignPanel()
+                  .updateAnnotation();
+        }
+      }
+
+
+    return nodeList;
+  }
+
+  public void groupThresholdSequences(List<SequenceI> groupedSeqs,
+          Color groupColour)
+  {
+    SequenceGroup treeGroup = new SequenceGroup(groupedSeqs, null, null,
+            true, true, false, 0,
+            parentAvport.getAlignment().getWidth() - 1);
+
+    ColourSchemeI cs = null;
+    if (parentAvport.getGlobalColourScheme() != null)
+    {
+      if (parentAvport.getGlobalColourScheme() instanceof UserColourScheme)
+      {
+        cs = new UserColourScheme(
+                ((UserColourScheme) parentAvport.getGlobalColourScheme())
+                        .getColours());
+      }
+      else
+      {
+        cs = ColourSchemeProperty.getColourScheme(treeGroup,
+                ColourSchemeProperty.getColourName(
+                        parentAvport.getGlobalColourScheme()));
+      }
+
+    }
+    treeGroup.setColourScheme(cs);
+    treeGroup.getGroupColourScheme().setThreshold(
+            parentAvport.getResidueShading().getThreshold(),
+            parentAvport.isIgnoreGapsConsensus());
+
+    treeGroup.setName("Tree Group " + nrTreeGroups);
+    treeGroup.setIdColour(groupColour);
+
+    for (AlignmentPanel associatedPanel : associatedPanels)
+    {
+      AlignViewportI altViewport = associatedPanel
+              .getAlignViewport();
+
+      if (altViewport.getGlobalColourScheme() != null
+              && altViewport.getResidueShading()
+                      .conservationApplied())
+      {
+        Conservation conserv = new Conservation(treeGroup.getName(),
+                treeGroup.getSequences(null), treeGroup.getStartRes(),
+                treeGroup.getEndRes());
+        conserv.calculate();
+        conserv.verdict(false, altViewport.getConsPercGaps());
+        treeGroup.getGroupColourScheme().setConservation(conserv);
+      }
+
+      altViewport.getAlignment().addGroup(treeGroup);
+      // TODO can we push all of the below into AlignViewportI?
+      final AlignViewportI codingComplement = altViewport
+              .getCodingComplement();
+      if (codingComplement != null)
+      {
+        SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(treeGroup,
+                parentAvport, codingComplement);
+        if (mappedGroup.getSequences().size() > 0)
+        {
+          codingComplement.getAlignment().addGroup(mappedGroup);
+          for (SequenceI seq : mappedGroup.getSequences())
+          {
+            codingComplement.setSequenceColour(seq, groupColour.brighter());
+          }
+        }
+      }
+
+    }
+
   }
-  
 
   /**
    * may or may not need an extra repaint on the alignment view (check what kira
@@ -339,79 +575,82 @@ public final class JalviewBinding
     }
 
   }
-  public void sortByTree_actionPerformed() {
-    // parentAvport.mirrorCommand(command, undo, ssm, source);
 
-    // alignFrame
+  @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()));
+    // }
+    // parentAvport.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));
-    
-  }
-  
+    // }
 
-  /**
-   * 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));
-  // }
+  }
 
+  public CommandI sortAlignmentIn(AlignmentPanel ap)
+  {
+    // TODO: move to alignment view controller
+    AlignmentViewport viewport = ap.av;
+    SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
+    AlignmentSorter.sortByTree(viewport.getAlignment(),
+            treeView.getPhylogeny());
+    CommandI undo;
+    undo = new OrderCommand("Tree Sort", oldOrder, viewport.getAlignment());
+
+    ap.paintAlignment(true, false);
+    return undo;
+  }
+  
 
 
   /**
@@ -442,6 +681,17 @@ public final class JalviewBinding
   {
     this.parentAvport = parentAvport;
   }
+
+  public AlignmentPanel[] getAssociatedPanels()
+  {
+    return associatedPanels;
+  }
+
+  public void setAssociatedPanels(AlignmentPanel[] associatedPanels)
+  {
+    this.associatedPanels = associatedPanels;
+  }
+
 }