JAL-1807 still testing
[jalviewjs.git] / unused / appletgui / TreeCanvas.java
index 786a59f..b8c2406 100644 (file)
-/*
- * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
- * Copyright (C) $$Year-Rel$$ The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
- * PURPOSE.  See the GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.appletgui;
-
-import jalview.analysis.Conservation;
-import jalview.analysis.NJTree;
-import jalview.api.AlignViewportI;
-import jalview.datamodel.Sequence;
-import jalview.datamodel.SequenceGroup;
-import jalview.datamodel.SequenceI;
-import jalview.datamodel.SequenceNode;
-import jalview.schemes.ColourSchemeI;
-import jalview.schemes.ColourSchemeProperty;
-import jalview.schemes.ResidueProperties;
-import jalview.schemes.UserColourScheme;
-import jalview.util.Format;
-import jalview.util.MappingUtils;
-import jalview.viewmodel.AlignmentViewport;
-
-import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.Font;
-import java.awt.FontMetrics;
-import java.awt.Graphics;
-import javax.swing.JPanel;
-import java.awt.Point;
-import java.awt.Rectangle;
-import javax.swing.JScrollPane;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
-import java.awt.event.MouseMotionListener;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Vector;
-
-public class TreeCanvas extends JPanel implements MouseListener,
-        MouseMotionListener
-{
-  NJTree tree;
-
-  JScrollPane scrollPane;
-
-  AlignViewport av;
-
-  public static final String PLACEHOLDER = " * ";
-
-  Font font;
-
-  boolean fitToWindow = true;
-
-  boolean showDistances = false;
-
-  boolean showBootstrap = false;
-
-  boolean markPlaceholders = false;
-
-  int offx = 20;
-
-  int offy;
-
-  float threshold;
-
-  String longestName;
-
-  int labelLength = -1;
-
-  Hashtable nameHash = new Hashtable();
-
-  Hashtable nodeHash = new Hashtable();
-
-  SequenceNode highlightNode;
-
-  AlignmentPanel ap;
-
-  public TreeCanvas(AlignmentPanel ap, JScrollPane scroller)
-  {
-    this.ap = ap;
-    this.av = ap.av;
-    font = av.getFont();
-    scrollPane = scroller;
-    addMouseListener(this);
-    addMouseMotionListener(this);
-    setLayout(null);
-
-    PaintRefresher.Register(this, av.getSequenceSetId());
-  }
-
-  public void treeSelectionChanged(SequenceI sequence)
-  {
-    SequenceGroup selected = av.getSelectionGroup();
-    if (selected == null)
-    {
-      selected = new SequenceGroup();
-      av.setSelectionGroup(selected);
-    }
-
-    selected.setEndRes(av.getAlignment().getWidth() - 1);
-    selected.addOrRemove(sequence, true);
-  }
-
-  public void setTree(NJTree tree)
-  {
-    this.tree = tree;
-    tree.findHeight(tree.getTopNode());
-
-    // Now have to calculate longest name based on the leaves
-    Vector leaves = tree.findLeaves(tree.getTopNode(), new Vector());
-    boolean has_placeholders = false;
-    longestName = "";
-
-    for (int i = 0; i < leaves.size(); i++)
-    {
-      SequenceNode lf = (SequenceNode) leaves.elementAt(i);
-
-      if (lf.isPlaceholder())
-      {
-        has_placeholders = true;
-      }
-
-      if (longestName.length() < ((Sequence) lf.element()).getName()
-              .length())
-      {
-        longestName = TreeCanvas.PLACEHOLDER
-                + ((Sequence) lf.element()).getName();
-      }
-    }
-
-    setMarkPlaceholders(has_placeholders);
-  }
-
-  public void drawNode(Graphics g, SequenceNode node, float chunk,
-          float scale, int width, int offx, int offy)
-  {
-    if (node == null)
-    {
-      return;
-    }
-
-    if (node.left() == null && node.right() == null)
-    {
-      // Drawing leaf node
-
-      float height = node.height;
-      float dist = node.dist;
-
-      int xstart = (int) ((height - dist) * scale) + offx;
-      int xend = (int) (height * scale) + offx;
-
-      int ypos = (int) (node.ycount * chunk) + offy;
-
-      if (node.element() instanceof SequenceI)
-      {
-        SequenceI seq = (SequenceI) node.element();
-
-        if (av.getSequenceColour(seq) == Color.white)
-        {
-          g.setColor(Color.black);
-        }
-        else
-        {
-          g.setColor(av.getSequenceColour(seq).darker());
-        }
-
-      }
-      else
-      {
-        g.setColor(Color.black);
-      }
-
-      // Draw horizontal line
-      g.drawLine(xstart, ypos, xend, ypos);
-
-      String nodeLabel = "";
-      if (showDistances && node.dist > 0)
-      {
-        nodeLabel = new Format("%-.2f").formDouble(node.dist);
-      }
-      if (showBootstrap)
-      {
-        int btstrap = node.getBootstrap();
-        if (btstrap > -1)
-        {
-          if (showDistances)
-          {
-            nodeLabel = nodeLabel + " : ";
-          }
-          nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());
-        }
-      }
-      if (!nodeLabel.equals(""))
-      {
-        g.drawString(nodeLabel, xstart + 2, ypos - 2);
-      }
-
-      String name = (markPlaceholders && node.isPlaceholder()) ? (PLACEHOLDER + node
-              .getName()) : node.getName();
-      FontMetrics fm = g.getFontMetrics(font);
-      int charWidth = fm.stringWidth(name) + 3;
-      int charHeight = fm.getHeight();
-
-      Rectangle rect = new Rectangle(xend + 10, ypos - charHeight,
-              charWidth, charHeight);
-
-      nameHash.put(node.element(), rect);
-
-      // Colour selected leaves differently
-      SequenceGroup selected = av.getSelectionGroup();
-      if (selected != null
-              && selected.getSequences(null).contains(node.element()))
-      {
-        g.setColor(Color.gray);
-
-        g.fillRect(xend + 10, ypos - charHeight + 3, charWidth, charHeight);
-        g.setColor(Color.white);
-      }
-      g.drawString(name, xend + 10, ypos);
-      g.setColor(Color.black);
-    }
-    else
-    {
-      drawNode(g, (SequenceNode) node.left(), chunk, scale, width, offx,
-              offy);
-      drawNode(g, (SequenceNode) node.right(), chunk, scale, width, offx,
-              offy);
-
-      float height = node.height;
-      float dist = node.dist;
-
-      int xstart = (int) ((height - dist) * scale) + offx;
-      int xend = (int) (height * scale) + offx;
-      int ypos = (int) (node.ycount * chunk) + offy;
-
-      g.setColor(node.color.darker());
-
-      // Draw horizontal line
-      g.drawLine(xstart, ypos, xend, ypos);
-      if (node == highlightNode)
-      {
-        g.fillRect(xend - 3, ypos - 3, 6, 6);
-      }
-      else
-      {
-        g.fillRect(xend - 2, ypos - 2, 4, 4);
-      }
-
-      int ystart = (int) (((SequenceNode) node.left()).ycount * chunk)
-              + offy;
-      int yend = (int) (((SequenceNode) node.right()).ycount * chunk)
-              + offy;
-
-      Rectangle pos = new Rectangle(xend - 2, ypos - 2, 5, 5);
-      nodeHash.put(node, pos);
-
-      g.drawLine((int) (height * scale) + offx, ystart,
-              (int) (height * scale) + offx, yend);
-
-      String nodeLabel = "";
-
-      if (showDistances && (node.dist > 0))
-      {
-        nodeLabel = new Format("%-.2f").formDouble(node.dist);
-      }
-
-      if (showBootstrap)
-      {
-        int btstrap = node.getBootstrap();
-        if (btstrap > -1)
-        {
-          if (showDistances)
-          {
-            nodeLabel = nodeLabel + " : ";
-          }
-          nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());
-        }
-      }
-
-      if (!nodeLabel.equals(""))
-      {
-        g.drawString(nodeLabel, xstart + 2, ypos - 2);
-      }
-
-    }
-  }
-
-  public Object findElement(int x, int y)
-  {
-    Enumeration keys = nameHash.keys();
-
-    while (keys.hasMoreElements())
-    {
-      Object ob = keys.nextElement();
-      Rectangle rect = (Rectangle) nameHash.get(ob);
-
-      if (x >= rect.x && x <= (rect.x + rect.width) && y >= rect.y
-              && y <= (rect.y + rect.height))
-      {
-        return ob;
-      }
-    }
-    keys = nodeHash.keys();
-
-    while (keys.hasMoreElements())
-    {
-      Object ob = keys.nextElement();
-      Rectangle rect = (Rectangle) nodeHash.get(ob);
-
-      if (x >= rect.x && x <= (rect.x + rect.width) && y >= rect.y
-              && y <= (rect.y + rect.height))
-      {
-        return ob;
-      }
-    }
-    return null;
-
-  }
-
-  public void pickNodes(Rectangle pickBox)
-  {
-    int width = getSize().width;
-    int height = getSize().height;
-
-    SequenceNode top = tree.getTopNode();
-
-    float wscale = (float) (width * .8 - offx * 2) / tree.getMaxHeight();
-    if (top.count == 0)
-    {
-      top.count = ((SequenceNode) top.left()).count
-              + ((SequenceNode) top.right()).count;
-    }
-    float chunk = (float) (height - offy) / top.count;
-
-    pickNode(pickBox, top, chunk, wscale, width, offx, offy);
-  }
-
-  public void pickNode(Rectangle pickBox, SequenceNode node, float chunk,
-          float scale, int width, int offx, int offy)
-  {
-    if (node == null)
-    {
-      return;
-    }
-
-    if (node.left() == null && node.right() == null)
-    {
-      float height = node.height;
-      // float dist = node.dist;
-
-      // int xstart = (int) ( (height - dist) * scale) + offx;
-      int xend = (int) (height * scale) + offx;
-
-      int ypos = (int) (node.ycount * chunk) + offy;
-
-      if (pickBox.contains(new Point(xend, ypos)))
-      {
-        if (node.element() instanceof SequenceI)
-        {
-          SequenceI seq = (SequenceI) node.element();
-          SequenceGroup sg = av.getSelectionGroup();
-          if (sg != null)
-          {
-            sg.addOrRemove(seq, true);
-          }
-        }
-      }
-    }
-    else
-    {
-      pickNode(pickBox, (SequenceNode) node.left(), chunk, scale, width,
-              offx, offy);
-      pickNode(pickBox, (SequenceNode) node.right(), chunk, scale, width,
-              offx, offy);
-    }
-  }
-
-  public void setColor(SequenceNode node, Color c)
-  {
-    if (node == null)
-    {
-      return;
-    }
-
-    if (node.left() == null && node.right() == null)
-    {
-      node.color = c;
-
-      if (node.element() instanceof SequenceI)
-      {
-        av.setSequenceColour((SequenceI) node.element(), c);
-      }
-    }
-    else
-    {
-      node.color = c;
-      setColor((SequenceNode) node.left(), c);
-      setColor((SequenceNode) node.right(), c);
-    }
-  }
-
-  @Override
-  public void update(Graphics g)
-  {
-    paint(g);
-  }
-
-  @Override
-  public void paint(Graphics g)
-  {
-    if (tree == null)
-    {
-      return;
-    }
-
-    if (nameHash.size() == 0)
-    {
-      repaint();
-    }
-
-    int width = scrollPane.getSize().width;
-    int height = scrollPane.getSize().height;
-    if (!fitToWindow)
-    {
-      height = g.getFontMetrics(font).getHeight() * nameHash.size();
-    }
-
-    if (getSize().width > width)
-    {
-      setSize(new Dimension(width, height));
-      scrollPane.validate();
-      return;
-    }
-
-    setSize(new Dimension(width, height));
-
-    g.setFont(font);
-    draw(g, width, height);
-    validate();
-  }
-
-  public void draw(Graphics g, int width, int height)
-  {
-    offy = font.getSize() + 10;
-
-    g.setColor(Color.white);
-    g.fillRect(0, 0, width, height);
-
-    labelLength = g.getFontMetrics(font).stringWidth(longestName) + 20; // 20
-    // allows
-    // for
-    // scrollbar
-
-    float wscale = (width - labelLength - offx * 2) / tree.getMaxHeight();
-
-    SequenceNode top = tree.getTopNode();
-
-    if (top.count == 0)
-    {
-      top.count = ((SequenceNode) top.left()).count
-              + ((SequenceNode) top.right()).count;
-    }
-    float chunk = (float) (height - offy) / top.count;
-
-    drawNode(g, tree.getTopNode(), chunk, wscale, width, offx, offy);
-
-    if (threshold != 0)
-    {
-      if (av.getCurrentTree() == tree)
-      {
-        g.setColor(Color.red);
-      }
-      else
-      {
-        g.setColor(Color.gray);
-      }
-
-      int x = (int) (threshold * (getSize().width - labelLength - 2 * offx) + offx);
-
-      g.drawLine(x, 0, x, getSize().height);
-    }
-
-  }
-
-  @Override
-  public void mouseReleased(MouseEvent e)
-  {
-  }
-
-  @Override
-  public void mouseEntered(MouseEvent e)
-  {
-  }
-
-  @Override
-  public void mouseExited(MouseEvent e)
-  {
-  }
-
-  @Override
-  public void mouseClicked(MouseEvent evt)
-  {
-    if (highlightNode != null)
-    {
-      if (evt.getClickCount() > 1)
-      {
-        tree.swapNodes(highlightNode);
-        tree.reCount(tree.getTopNode());
-        tree.findHeight(tree.getTopNode());
-      }
-      else
-      {
-        Vector leaves = new Vector();
-        tree.findLeaves(highlightNode, leaves);
-
-        for (int i = 0; i < leaves.size(); i++)
-        {
-          SequenceI seq = (SequenceI) ((SequenceNode) leaves.elementAt(i))
-                  .element();
-          treeSelectionChanged(seq);
-        }
-      }
-
-      PaintRefresher.Refresh(this, av.getSequenceSetId());
-      repaint();
-      av.sendSelection();
-    }
-  }
-
-  @Override
-  public void mouseDragged(MouseEvent ect)
-  {
-  }
-
-  @Override
-  public void mouseMoved(MouseEvent evt)
-  {
-    av.setCurrentTree(tree);
-
-    Object ob = findElement(evt.getX(), evt.getY());
-
-    if (ob instanceof SequenceNode)
-    {
-      highlightNode = (SequenceNode) ob;
-      repaint();
-    }
-    else
-    {
-      if (highlightNode != null)
-      {
-        highlightNode = null;
-        repaint();
-      }
-    }
-  }
-
-  @Override
-  public void mousePressed(MouseEvent e)
-  {
-    av.setCurrentTree(tree);
-
-    int x = e.getX();
-    int y = e.getY();
-
-    Object ob = findElement(x, y);
-
-    if (ob instanceof SequenceI)
-    {
-      treeSelectionChanged((Sequence) ob);
-      PaintRefresher.Refresh(this, av.getSequenceSetId());
-      repaint();
-      av.sendSelection();
-      return;
-    }
-    else if (!(ob instanceof SequenceNode))
-    {
-      // Find threshold
-
-      if (tree.getMaxHeight() != 0)
-      {
-        threshold = (float) (x - offx)
-                / (float) (getSize().width - labelLength - 2 * offx);
-
-        tree.getGroups().removeAllElements();
-        tree.groupNodes(tree.getTopNode(), threshold);
-        setColor(tree.getTopNode(), Color.black);
-
-        av.setSelectionGroup(null);
-        av.getAlignment().deleteAllGroups();
-        av.clearSequenceColours();
-        final AlignViewportI codingComplement = av.getCodingComplement();
-        if (codingComplement != null)
-        {
-          codingComplement.setSelectionGroup(null);
-          codingComplement.getAlignment().deleteAllGroups();
-          codingComplement.clearSequenceColours();
-        }
-
-        colourGroups();
-
-      }
-    }
-
-    PaintRefresher.Refresh(this, av.getSequenceSetId());
-    repaint();
-
-  }
-
-  void colourGroups()
-  {
-    for (int i = 0; i < tree.getGroups().size(); i++)
-    {
-
-      Color col = new Color((int) (Math.random() * 255),
-              (int) (Math.random() * 255), (int) (Math.random() * 255));
-      setColor((SequenceNode) tree.getGroups().elementAt(i), col.brighter());
-
-      Vector l = tree.findLeaves(
-              (SequenceNode) tree.getGroups().elementAt(i), new Vector());
-
-      Vector sequences = new Vector();
-      for (int j = 0; j < l.size(); j++)
-      {
-        SequenceI s1 = (SequenceI) ((SequenceNode) l.elementAt(j))
-                .element();
-        if (!sequences.contains(s1))
-        {
-          sequences.addElement(s1);
-        }
-      }
-
-      ColourSchemeI cs = null;
-
-      SequenceGroup sg = new SequenceGroup(sequences, "", cs, true, true,
-              false, 0, av.getAlignment().getWidth() - 1);
-
-      if (av.getGlobalColourScheme() != null)
-      {
-        if (av.getGlobalColourScheme() instanceof UserColourScheme)
-        {
-          cs = new UserColourScheme(
-                  ((UserColourScheme) av.getGlobalColourScheme())
-                          .getColours());
-
-        }
-        else
-        {
-          cs = ColourSchemeProperty.getColour(sg, ColourSchemeProperty
-                  .getColourName(av.getGlobalColourScheme()));
-        }
-        // cs is null if shading is an annotationColourGradient
-        if (cs != null)
-        {
-          cs.setThreshold(av.getGlobalColourScheme().getThreshold(),
-                  av.isIgnoreGapsConsensus());
-        }
-      }
-      // TODO: cs used to be initialized with a sequence collection and
-      // recalcConservation called automatically
-      // instead we set it manually - recalc called after updateAnnotation
-      sg.cs = cs;
-
-      sg.setName("JTreeGroup:" + sg.hashCode());
-      sg.setIdColour(col);
-      if (av.getGlobalColourScheme() != null
-              && av.getGlobalColourScheme().conservationApplied())
-      {
-        Conservation c = new Conservation("Group",
-                ResidueProperties.propHash, 3, sg.getSequences(null),
-                sg.getStartRes(), sg.getEndRes());
-
-        c.calculate();
-        c.verdict(false, av.getConsPercGaps());
-        cs.setConservation(c);
-
-        sg.cs = cs;
-
-      }
-
-      av.getAlignment().addGroup(sg);
-
-      // TODO this is duplicated with gui TreeCanvas - refactor
-      av.getAlignment().addGroup(sg);
-      final AlignViewportI codingComplement = av.getCodingComplement();
-      if (codingComplement != null)
-      {
-        SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg, av,
-                codingComplement);
-        if (mappedGroup.getSequences().size() > 0)
-        {
-          codingComplement.getAlignment().addGroup(mappedGroup);
-          for (SequenceI seq : mappedGroup.getSequences())
-          {
-            // TODO why does gui require col.brighter() here??
-            codingComplement.setSequenceColour(seq, col);
-          }
-        }
-      }
-
-    }
-    ap.updateAnnotation();
-    if (av.getCodingComplement() != null)
-    {
-      ((AlignmentViewport) av.getCodingComplement()).firePropertyChange(
-              "alignment", null, ap.av.getAlignment().getSequences());
-    }
-  }
-
-  public void setShowDistances(boolean state)
-  {
-    this.showDistances = state;
-    repaint();
-  }
-
-  public void setShowBootstrap(boolean state)
-  {
-    this.showBootstrap = state;
-    repaint();
-  }
-
-  public void setMarkPlaceholders(boolean state)
-  {
-    this.markPlaceholders = state;
-    repaint();
-  }
-
-}
+/*\r
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)\r
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors\r
+ * \r
+ * This file is part of Jalview.\r
+ * \r
+ * Jalview is free software: you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License \r
+ * as published by the Free Software Foundation, either version 3\r
+ * of the License, or (at your option) any later version.\r
+ *  \r
+ * Jalview is distributed in the hope that it will be useful, but \r
+ * WITHOUT ANY WARRANTY; without even the implied warranty \r
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR \r
+ * PURPOSE.  See the GNU General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU General Public License\r
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.\r
+ * The Jalview Authors are detailed in the 'AUTHORS' file.\r
+ */\r
+package jalview.appletgui;\r
+\r
+import jalview.analysis.Conservation;\r
+import jalview.analysis.NJTree;\r
+import jalview.api.AlignViewportI;\r
+import jalview.datamodel.Sequence;\r
+import jalview.datamodel.SequenceGroup;\r
+import jalview.datamodel.SequenceI;\r
+import jalview.datamodel.SequenceNode;\r
+import jalview.schemes.ColourSchemeI;\r
+import jalview.schemes.ColourSchemeProperty;\r
+import jalview.schemes.ResidueProperties;\r
+import jalview.schemes.UserColourScheme;\r
+import jalview.util.Format;\r
+import jalview.util.MappingUtils;\r
+import jalview.viewmodel.AlignmentViewport;\r
+\r
+import java.awt.Color;\r
+import java.awt.Dimension;\r
+import java.awt.Font;\r
+import java.awt.FontMetrics;\r
+import java.awt.Graphics;\r
+import javax.swing.JPanel;\r
+import java.awt.Point;\r
+import java.awt.Rectangle;\r
+import javax.swing.JScrollPane;\r
+import java.awt.event.MouseEvent;\r
+import java.awt.event.MouseListener;\r
+import java.awt.event.MouseMotionListener;\r
+import java.util.Enumeration;\r
+import java.util.Hashtable;\r
+import java.util.Vector;\r
+\r
+public class TreeCanvas extends JPanel implements MouseListener,\r
+        MouseMotionListener\r
+{\r
+  NJTree tree;\r
+\r
+  JScrollPane scrollPane;\r
+\r
+  AlignViewport av;\r
+\r
+  public static final String PLACEHOLDER = " * ";\r
+\r
+  Font font;\r
+\r
+  boolean fitToWindow = true;\r
+\r
+  boolean showDistances = false;\r
+\r
+  boolean showBootstrap = false;\r
+\r
+  boolean markPlaceholders = false;\r
+\r
+  int offx = 20;\r
+\r
+  int offy;\r
+\r
+  float threshold;\r
+\r
+  String longestName;\r
+\r
+  int labelLength = -1;\r
+\r
+  Hashtable nameHash = new Hashtable();\r
+\r
+  Hashtable nodeHash = new Hashtable();\r
+\r
+  SequenceNode highlightNode;\r
+\r
+  AlignmentPanel ap;\r
+\r
+  public TreeCanvas(AlignmentPanel ap, JScrollPane scroller)\r
+  {\r
+    this.ap = ap;\r
+    this.av = ap.av;\r
+    font = av.getFont();\r
+    scrollPane = scroller;\r
+    addMouseListener(this);\r
+    addMouseMotionListener(this);\r
+    setLayout(null);\r
+\r
+    PaintRefresher.Register(this, av.getSequenceSetId());\r
+  }\r
+\r
+  public void treeSelectionChanged(SequenceI sequence)\r
+  {\r
+    SequenceGroup selected = av.getSelectionGroup();\r
+    if (selected == null)\r
+    {\r
+      selected = new SequenceGroup();\r
+      av.setSelectionGroup(selected);\r
+    }\r
+\r
+    selected.setEndRes(av.getAlignment().getWidth() - 1);\r
+    selected.addOrRemove(sequence, true);\r
+  }\r
+\r
+  public void setTree(NJTree tree)\r
+  {\r
+    this.tree = tree;\r
+    tree.findHeight(tree.getTopNode());\r
+\r
+    // Now have to calculate longest name based on the leaves\r
+    Vector leaves = tree.findLeaves(tree.getTopNode(), new Vector());\r
+    boolean has_placeholders = false;\r
+    longestName = "";\r
+\r
+    for (int i = 0; i < leaves.size(); i++)\r
+    {\r
+      SequenceNode lf = (SequenceNode) leaves.elementAt(i);\r
+\r
+      if (lf.isPlaceholder())\r
+      {\r
+        has_placeholders = true;\r
+      }\r
+\r
+      if (longestName.length() < ((Sequence) lf.element()).getName()\r
+              .length())\r
+      {\r
+        longestName = TreeCanvas.PLACEHOLDER\r
+                + ((Sequence) lf.element()).getName();\r
+      }\r
+    }\r
+\r
+    setMarkPlaceholders(has_placeholders);\r
+  }\r
+\r
+  public void drawNode(Graphics g, SequenceNode node, float chunk,\r
+          float scale, int width, int offx, int offy)\r
+  {\r
+    if (node == null)\r
+    {\r
+      return;\r
+    }\r
+\r
+    if (node.left() == null && node.right() == null)\r
+    {\r
+      // Drawing leaf node\r
+\r
+      float height = node.height;\r
+      float dist = node.dist;\r
+\r
+      int xstart = (int) ((height - dist) * scale) + offx;\r
+      int xend = (int) (height * scale) + offx;\r
+\r
+      int ypos = (int) (node.ycount * chunk) + offy;\r
+\r
+      if (node.element() instanceof SequenceI)\r
+      {\r
+        SequenceI seq = (SequenceI) node.element();\r
+\r
+        if (av.getSequenceColour(seq) == Color.white)\r
+        {\r
+          g.setColor(Color.black);\r
+        }\r
+        else\r
+        {\r
+          g.setColor(av.getSequenceColour(seq).darker());\r
+        }\r
+\r
+      }\r
+      else\r
+      {\r
+        g.setColor(Color.black);\r
+      }\r
+\r
+      // Draw horizontal line\r
+      g.drawLine(xstart, ypos, xend, ypos);\r
+\r
+      String nodeLabel = "";\r
+      if (showDistances && node.dist > 0)\r
+      {\r
+        nodeLabel = new Format("%-.2f").formDouble(node.dist);\r
+      }\r
+      if (showBootstrap)\r
+      {\r
+        int btstrap = node.getBootstrap();\r
+        if (btstrap > -1)\r
+        {\r
+          if (showDistances)\r
+          {\r
+            nodeLabel = nodeLabel + " : ";\r
+          }\r
+          nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());\r
+        }\r
+      }\r
+      if (!nodeLabel.equals(""))\r
+      {\r
+        g.drawString(nodeLabel, xstart + 2, ypos - 2);\r
+      }\r
+\r
+      String name = (markPlaceholders && node.isPlaceholder()) ? (PLACEHOLDER + node\r
+              .getName()) : node.getName();\r
+      FontMetrics fm = g.getFontMetrics(font);\r
+      int charWidth = fm.stringWidth(name) + 3;\r
+      int charHeight = fm.getHeight();\r
+\r
+      Rectangle rect = new Rectangle(xend + 10, ypos - charHeight,\r
+              charWidth, charHeight);\r
+\r
+      nameHash.put(node.element(), rect);\r
+\r
+      // Colour selected leaves differently\r
+      SequenceGroup selected = av.getSelectionGroup();\r
+      if (selected != null\r
+              && selected.getSequences(null).contains(node.element()))\r
+      {\r
+        g.setColor(Color.gray);\r
+\r
+        g.fillRect(xend + 10, ypos - charHeight + 3, charWidth, charHeight);\r
+        g.setColor(Color.white);\r
+      }\r
+      g.drawString(name, xend + 10, ypos);\r
+      g.setColor(Color.black);\r
+    }\r
+    else\r
+    {\r
+      drawNode(g, (SequenceNode) node.left(), chunk, scale, width, offx,\r
+              offy);\r
+      drawNode(g, (SequenceNode) node.right(), chunk, scale, width, offx,\r
+              offy);\r
+\r
+      float height = node.height;\r
+      float dist = node.dist;\r
+\r
+      int xstart = (int) ((height - dist) * scale) + offx;\r
+      int xend = (int) (height * scale) + offx;\r
+      int ypos = (int) (node.ycount * chunk) + offy;\r
+\r
+      g.setColor(node.color.darker());\r
+\r
+      // Draw horizontal line\r
+      g.drawLine(xstart, ypos, xend, ypos);\r
+      if (node == highlightNode)\r
+      {\r
+        g.fillRect(xend - 3, ypos - 3, 6, 6);\r
+      }\r
+      else\r
+      {\r
+        g.fillRect(xend - 2, ypos - 2, 4, 4);\r
+      }\r
+\r
+      int ystart = (int) (((SequenceNode) node.left()).ycount * chunk)\r
+              + offy;\r
+      int yend = (int) (((SequenceNode) node.right()).ycount * chunk)\r
+              + offy;\r
+\r
+      Rectangle pos = new Rectangle(xend - 2, ypos - 2, 5, 5);\r
+      nodeHash.put(node, pos);\r
+\r
+      g.drawLine((int) (height * scale) + offx, ystart,\r
+              (int) (height * scale) + offx, yend);\r
+\r
+      String nodeLabel = "";\r
+\r
+      if (showDistances && (node.dist > 0))\r
+      {\r
+        nodeLabel = new Format("%-.2f").formDouble(node.dist);\r
+      }\r
+\r
+      if (showBootstrap)\r
+      {\r
+        int btstrap = node.getBootstrap();\r
+        if (btstrap > -1)\r
+        {\r
+          if (showDistances)\r
+          {\r
+            nodeLabel = nodeLabel + " : ";\r
+          }\r
+          nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());\r
+        }\r
+      }\r
+\r
+      if (!nodeLabel.equals(""))\r
+      {\r
+        g.drawString(nodeLabel, xstart + 2, ypos - 2);\r
+      }\r
+\r
+    }\r
+  }\r
+\r
+  public Object findElement(int x, int y)\r
+  {\r
+    Enumeration keys = nameHash.keys();\r
+\r
+    while (keys.hasMoreElements())\r
+    {\r
+      Object ob = keys.nextElement();\r
+      Rectangle rect = (Rectangle) nameHash.get(ob);\r
+\r
+      if (x >= rect.x && x <= (rect.x + rect.width) && y >= rect.y\r
+              && y <= (rect.y + rect.height))\r
+      {\r
+        return ob;\r
+      }\r
+    }\r
+    keys = nodeHash.keys();\r
+\r
+    while (keys.hasMoreElements())\r
+    {\r
+      Object ob = keys.nextElement();\r
+      Rectangle rect = (Rectangle) nodeHash.get(ob);\r
+\r
+      if (x >= rect.x && x <= (rect.x + rect.width) && y >= rect.y\r
+              && y <= (rect.y + rect.height))\r
+      {\r
+        return ob;\r
+      }\r
+    }\r
+    return null;\r
+\r
+  }\r
+\r
+  public void pickNodes(Rectangle pickBox)\r
+  {\r
+    int width = getSize().width;\r
+    int height = getSize().height;\r
+\r
+    SequenceNode top = tree.getTopNode();\r
+\r
+    float wscale = (float) (width * .8 - offx * 2) / tree.getMaxHeight();\r
+    if (top.count == 0)\r
+    {\r
+      top.count = ((SequenceNode) top.left()).count\r
+              + ((SequenceNode) top.right()).count;\r
+    }\r
+    float chunk = (float) (height - offy) / top.count;\r
+\r
+    pickNode(pickBox, top, chunk, wscale, width, offx, offy);\r
+  }\r
+\r
+  public void pickNode(Rectangle pickBox, SequenceNode node, float chunk,\r
+          float scale, int width, int offx, int offy)\r
+  {\r
+    if (node == null)\r
+    {\r
+      return;\r
+    }\r
+\r
+    if (node.left() == null && node.right() == null)\r
+    {\r
+      float height = node.height;\r
+      // float dist = node.dist;\r
+\r
+      // int xstart = (int) ( (height - dist) * scale) + offx;\r
+      int xend = (int) (height * scale) + offx;\r
+\r
+      int ypos = (int) (node.ycount * chunk) + offy;\r
+\r
+      if (pickBox.contains(new Point(xend, ypos)))\r
+      {\r
+        if (node.element() instanceof SequenceI)\r
+        {\r
+          SequenceI seq = (SequenceI) node.element();\r
+          SequenceGroup sg = av.getSelectionGroup();\r
+          if (sg != null)\r
+          {\r
+            sg.addOrRemove(seq, true);\r
+          }\r
+        }\r
+      }\r
+    }\r
+    else\r
+    {\r
+      pickNode(pickBox, (SequenceNode) node.left(), chunk, scale, width,\r
+              offx, offy);\r
+      pickNode(pickBox, (SequenceNode) node.right(), chunk, scale, width,\r
+              offx, offy);\r
+    }\r
+  }\r
+\r
+  public void setColor(SequenceNode node, Color c)\r
+  {\r
+    if (node == null)\r
+    {\r
+      return;\r
+    }\r
+\r
+    if (node.left() == null && node.right() == null)\r
+    {\r
+      node.color = c;\r
+\r
+      if (node.element() instanceof SequenceI)\r
+      {\r
+        av.setSequenceColour((SequenceI) node.element(), c);\r
+      }\r
+    }\r
+    else\r
+    {\r
+      node.color = c;\r
+      setColor((SequenceNode) node.left(), c);\r
+      setColor((SequenceNode) node.right(), c);\r
+    }\r
+  }\r
+\r
+  @Override\r
+  public void update(Graphics g)\r
+  {\r
+    paint(g);\r
+  }\r
+\r
+  @Override\r
+  public void paint(Graphics g)\r
+  {\r
+    if (tree == null)\r
+    {\r
+      return;\r
+    }\r
+\r
+    if (nameHash.size() == 0)\r
+    {\r
+      repaint();\r
+    }\r
+\r
+    int width = scrollPane.getSize().width;\r
+    int height = scrollPane.getSize().height;\r
+    if (!fitToWindow)\r
+    {\r
+      height = g.getFontMetrics(font).getHeight() * nameHash.size();\r
+    }\r
+\r
+    if (getSize().width > width)\r
+    {\r
+      setSize(new Dimension(width, height));\r
+      scrollPane.validate();\r
+      return;\r
+    }\r
+\r
+    setSize(new Dimension(width, height));\r
+\r
+    g.setFont(font);\r
+    draw(g, width, height);\r
+    validate();\r
+  }\r
+\r
+  public void draw(Graphics g, int width, int height)\r
+  {\r
+    offy = font.getSize() + 10;\r
+\r
+    g.setColor(Color.white);\r
+    g.fillRect(0, 0, width, height);\r
+\r
+    labelLength = g.getFontMetrics(font).stringWidth(longestName) + 20; // 20\r
+    // allows\r
+    // for\r
+    // scrollbar\r
+\r
+    float wscale = (width - labelLength - offx * 2) / tree.getMaxHeight();\r
+\r
+    SequenceNode top = tree.getTopNode();\r
+\r
+    if (top.count == 0)\r
+    {\r
+      top.count = ((SequenceNode) top.left()).count\r
+              + ((SequenceNode) top.right()).count;\r
+    }\r
+    float chunk = (float) (height - offy) / top.count;\r
+\r
+    drawNode(g, tree.getTopNode(), chunk, wscale, width, offx, offy);\r
+\r
+    if (threshold != 0)\r
+    {\r
+      if (av.getCurrentTree() == tree)\r
+      {\r
+        g.setColor(Color.red);\r
+      }\r
+      else\r
+      {\r
+        g.setColor(Color.gray);\r
+      }\r
+\r
+      int x = (int) (threshold * (getSize().width - labelLength - 2 * offx) + offx);\r
+\r
+      g.drawLine(x, 0, x, getSize().height);\r
+    }\r
+\r
+  }\r
+\r
+  @Override\r
+  public void mouseReleased(MouseEvent e)\r
+  {\r
+  }\r
+\r
+  @Override\r
+  public void mouseEntered(MouseEvent e)\r
+  {\r
+  }\r
+\r
+  @Override\r
+  public void mouseExited(MouseEvent e)\r
+  {\r
+  }\r
+\r
+  @Override\r
+  public void mouseClicked(MouseEvent evt)\r
+  {\r
+    if (highlightNode != null)\r
+    {\r
+      if (evt.getClickCount() > 1)\r
+      {\r
+        tree.swapNodes(highlightNode);\r
+        tree.reCount(tree.getTopNode());\r
+        tree.findHeight(tree.getTopNode());\r
+      }\r
+      else\r
+      {\r
+        Vector leaves = new Vector();\r
+        tree.findLeaves(highlightNode, leaves);\r
+\r
+        for (int i = 0; i < leaves.size(); i++)\r
+        {\r
+          SequenceI seq = (SequenceI) ((SequenceNode) leaves.elementAt(i))\r
+                  .element();\r
+          treeSelectionChanged(seq);\r
+        }\r
+      }\r
+\r
+      PaintRefresher.Refresh(this, av.getSequenceSetId());\r
+      repaint();\r
+      av.sendSelection();\r
+    }\r
+  }\r
+\r
+  @Override\r
+  public void mouseDragged(MouseEvent ect)\r
+  {\r
+  }\r
+\r
+  @Override\r
+  public void mouseMoved(MouseEvent evt)\r
+  {\r
+    av.setCurrentTree(tree);\r
+\r
+    Object ob = findElement(evt.getX(), evt.getY());\r
+\r
+    if (ob instanceof SequenceNode)\r
+    {\r
+      highlightNode = (SequenceNode) ob;\r
+      repaint();\r
+    }\r
+    else\r
+    {\r
+      if (highlightNode != null)\r
+      {\r
+        highlightNode = null;\r
+        repaint();\r
+      }\r
+    }\r
+  }\r
+\r
+  @Override\r
+  public void mousePressed(MouseEvent e)\r
+  {\r
+    av.setCurrentTree(tree);\r
+\r
+    int x = e.getX();\r
+    int y = e.getY();\r
+\r
+    Object ob = findElement(x, y);\r
+\r
+    if (ob instanceof SequenceI)\r
+    {\r
+      treeSelectionChanged((Sequence) ob);\r
+      PaintRefresher.Refresh(this, av.getSequenceSetId());\r
+      repaint();\r
+      av.sendSelection();\r
+      return;\r
+    }\r
+    else if (!(ob instanceof SequenceNode))\r
+    {\r
+      // Find threshold\r
+\r
+      if (tree.getMaxHeight() != 0)\r
+      {\r
+        threshold = (float) (x - offx)\r
+                / (float) (getSize().width - labelLength - 2 * offx);\r
+\r
+        tree.getGroups().removeAllElements();\r
+        tree.groupNodes(tree.getTopNode(), threshold);\r
+        setColor(tree.getTopNode(), Color.black);\r
+\r
+        av.setSelectionGroup(null);\r
+        av.getAlignment().deleteAllGroups();\r
+        av.clearSequenceColours();\r
+        final AlignViewportI codingComplement = av.getCodingComplement();\r
+        if (codingComplement != null)\r
+        {\r
+          codingComplement.setSelectionGroup(null);\r
+          codingComplement.getAlignment().deleteAllGroups();\r
+          codingComplement.clearSequenceColours();\r
+        }\r
+\r
+        colourGroups();\r
+\r
+      }\r
+    }\r
+\r
+    PaintRefresher.Refresh(this, av.getSequenceSetId());\r
+    repaint();\r
+\r
+  }\r
+\r
+  void colourGroups()\r
+  {\r
+    for (int i = 0; i < tree.getGroups().size(); i++)\r
+    {\r
+\r
+      Color col = new Color((int) (Math.random() * 255),\r
+              (int) (Math.random() * 255), (int) (Math.random() * 255));\r
+      setColor((SequenceNode) tree.getGroups().elementAt(i), col.brighter());\r
+\r
+      Vector l = tree.findLeaves(\r
+              (SequenceNode) tree.getGroups().elementAt(i), new Vector());\r
+\r
+      Vector sequences = new Vector();\r
+      for (int j = 0; j < l.size(); j++)\r
+      {\r
+        SequenceI s1 = (SequenceI) ((SequenceNode) l.elementAt(j))\r
+                .element();\r
+        if (!sequences.contains(s1))\r
+        {\r
+          sequences.addElement(s1);\r
+        }\r
+      }\r
+\r
+      ColourSchemeI cs = null;\r
+\r
+      SequenceGroup sg = new SequenceGroup(sequences, "", cs, true, true,\r
+              false, 0, av.getAlignment().getWidth() - 1);\r
+\r
+      if (av.getGlobalColourScheme() != null)\r
+      {\r
+        if (av.getGlobalColourScheme() instanceof UserColourScheme)\r
+        {\r
+          cs = new UserColourScheme(\r
+                  ((UserColourScheme) av.getGlobalColourScheme())\r
+                          .getColours());\r
+\r
+        }\r
+        else\r
+        {\r
+          cs = ColourSchemeProperty.getColour(sg, ColourSchemeProperty\r
+                  .getColourName(av.getGlobalColourScheme()));\r
+        }\r
+        // cs is null if shading is an annotationColourGradient\r
+        if (cs != null)\r
+        {\r
+          cs.setThreshold(av.getGlobalColourScheme().getThreshold(),\r
+                  av.isIgnoreGapsConsensus());\r
+        }\r
+      }\r
+      // TODO: cs used to be initialized with a sequence collection and\r
+      // recalcConservation called automatically\r
+      // instead we set it manually - recalc called after updateAnnotation\r
+      sg.cs = cs;\r
+\r
+      sg.setName("JTreeGroup:" + sg.hashCode());\r
+      sg.setIdColour(col);\r
+      if (av.getGlobalColourScheme() != null\r
+              && av.getGlobalColourScheme().conservationApplied())\r
+      {\r
+        Conservation c = new Conservation("Group",\r
+                ResidueProperties.propHash, 3, sg.getSequences(null),\r
+                sg.getStartRes(), sg.getEndRes());\r
+\r
+        c.calculate();\r
+        c.verdict(false, av.getConsPercGaps());\r
+        cs.setConservation(c);\r
+\r
+        sg.cs = cs;\r
+\r
+      }\r
+\r
+      av.getAlignment().addGroup(sg);\r
+\r
+      // TODO this is duplicated with gui TreeCanvas - refactor\r
+      av.getAlignment().addGroup(sg);\r
+      final AlignViewportI codingComplement = av.getCodingComplement();\r
+      if (codingComplement != null)\r
+      {\r
+        SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg, av,\r
+                codingComplement);\r
+        if (mappedGroup.getSequences().size() > 0)\r
+        {\r
+          codingComplement.getAlignment().addGroup(mappedGroup);\r
+          for (SequenceI seq : mappedGroup.getSequences())\r
+          {\r
+            // TODO why does gui require col.brighter() here??\r
+            codingComplement.setSequenceColour(seq, col);\r
+          }\r
+        }\r
+      }\r
+\r
+    }\r
+    ap.updateAnnotation();\r
+    if (av.getCodingComplement() != null)\r
+    {\r
+      ((AlignmentViewport) av.getCodingComplement()).firePropertyChange(\r
+              "alignment", null, ap.av.getAlignment().getSequences());\r
+    }\r
+  }\r
+\r
+  public void setShowDistances(boolean state)\r
+  {\r
+    this.showDistances = state;\r
+    repaint();\r
+  }\r
+\r
+  public void setShowBootstrap(boolean state)\r
+  {\r
+    this.showBootstrap = state;\r
+    repaint();\r
+  }\r
+\r
+  public void setMarkPlaceholders(boolean state)\r
+  {\r
+    this.markPlaceholders = state;\r
+    repaint();\r
+  }\r
+\r
+}\r