2 * Jalview - A Sequence Alignment Editor and Viewer
3 * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
20 package jalview.appletgui;
25 import java.awt.event.*;
27 import jalview.analysis.*;
28 import jalview.datamodel.*;
29 import jalview.schemes.*;
30 import jalview.util.*;
32 public class TreeCanvas
33 extends Panel implements MouseListener, MouseMotionListener
36 ScrollPane scrollPane;
38 public static final String PLACEHOLDER = " * ";
40 boolean fitToWindow = true;
41 boolean showDistances = false;
42 boolean showBootstrap = false;
43 boolean markPlaceholders = false;
53 Hashtable nameHash = new Hashtable();
54 Hashtable nodeHash = new Hashtable();
56 SequenceNode highlightNode;
58 public TreeCanvas(AlignViewport av, ScrollPane scroller)
62 scrollPane = scroller;
63 addMouseListener(this);
64 addMouseMotionListener(this);
67 PaintRefresher.Register(this, av.getSequenceSetId());
70 public void treeSelectionChanged(SequenceI sequence)
72 SequenceGroup selected = av.getSelectionGroup();
75 selected = new SequenceGroup();
76 av.setSelectionGroup(selected);
79 selected.setEndRes(av.alignment.getWidth() - 1);
80 selected.addOrRemove(sequence, true);
83 public void setTree(NJTree tree)
86 tree.findHeight(tree.getTopNode());
88 // Now have to calculate longest name based on the leaves
89 Vector leaves = tree.findLeaves(tree.getTopNode(), new Vector());
90 boolean has_placeholders = false;
93 for (int i = 0; i < leaves.size(); i++)
95 SequenceNode lf = (SequenceNode) leaves.elementAt(i);
97 if (lf.isPlaceholder())
99 has_placeholders = true;
102 if (longestName.length() < ( (Sequence) lf.element()).getName()
105 longestName = TreeCanvas.PLACEHOLDER +
106 ( (Sequence) lf.element()).getName();
110 setMarkPlaceholders(has_placeholders);
113 public void drawNode(Graphics g, SequenceNode node, float chunk, float scale,
114 int width, int offx, int offy)
121 if (node.left() == null && node.right() == null)
125 float height = node.height;
126 float dist = node.dist;
128 int xstart = (int) ( (height - dist) * scale) + offx;
129 int xend = (int) (height * scale) + offx;
131 int ypos = (int) (node.ycount * chunk) + offy;
133 if (node.element() instanceof SequenceI)
135 SequenceI seq = (SequenceI) ( (SequenceNode) node).element();
137 if (av.getSequenceColour(seq) == Color.white)
139 g.setColor(Color.black);
143 g.setColor(av.getSequenceColour(seq).darker());
149 g.setColor(Color.black);
152 // Draw horizontal line
153 g.drawLine(xstart, ypos, xend, ypos);
155 String nodeLabel = "";
156 if (showDistances && node.dist > 0)
158 nodeLabel = new Format("%-.2f").form(node.dist);
162 int btstrap = node.getBootstrap();
167 nodeLabel = nodeLabel + " : ";
169 nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());
172 if (!nodeLabel.equals(""))
174 g.drawString(nodeLabel, xstart + 2, ypos - 2);
177 String name = (markPlaceholders && node.isPlaceholder()) ?
178 (PLACEHOLDER + node.getName()) : node.getName();
179 FontMetrics fm = g.getFontMetrics(font);
180 int charWidth = fm.stringWidth(name) + 3;
181 int charHeight = fm.getHeight();
183 Rectangle rect = new Rectangle(xend + 10, ypos - charHeight,
184 charWidth, charHeight);
186 nameHash.put( (SequenceI) node.element(), rect);
188 // Colour selected leaves differently
189 SequenceGroup selected = av.getSelectionGroup();
190 if (selected != null &&
191 selected.getSequences(null).contains( (SequenceI) node.element()))
193 g.setColor(Color.gray);
195 g.fillRect(xend + 10, ypos - charHeight + 3, charWidth, charHeight);
196 g.setColor(Color.white);
198 g.drawString(name, xend + 10, ypos);
199 g.setColor(Color.black);
203 drawNode(g, (SequenceNode) node.left(), chunk, scale, width, offx, offy);
204 drawNode(g, (SequenceNode) node.right(), chunk, scale, width, offx, offy);
206 float height = node.height;
207 float dist = node.dist;
209 int xstart = (int) ( (height - dist) * scale) + offx;
210 int xend = (int) (height * scale) + offx;
211 int ypos = (int) (node.ycount * chunk) + offy;
213 g.setColor( ( (SequenceNode) node).color.darker());
215 // Draw horizontal line
216 g.drawLine(xstart, ypos, xend, ypos);
217 if (node == highlightNode)
219 g.fillRect(xend - 3, ypos - 3, 6, 6);
223 g.fillRect(xend - 2, ypos - 2, 4, 4);
226 int ystart = (int) ( ( (SequenceNode) node.left()).ycount * chunk) + offy;
227 int yend = (int) ( ( (SequenceNode) node.right()).ycount * chunk) + offy;
229 Rectangle pos = new Rectangle(xend - 2, ypos - 2, 5, 5);
230 nodeHash.put(node, pos);
232 g.drawLine( (int) (height * scale) + offx, ystart,
233 (int) (height * scale) + offx, yend);
235 String nodeLabel = "";
237 if (showDistances && (node.dist > 0))
239 nodeLabel = new Format("%-.2f").form(node.dist);
244 int btstrap = node.getBootstrap();
249 nodeLabel = nodeLabel + " : ";
251 nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());
255 if (!nodeLabel.equals(""))
257 g.drawString(nodeLabel, xstart + 2, ypos - 2);
263 public Object findElement(int x, int y)
265 Enumeration keys = nameHash.keys();
267 while (keys.hasMoreElements())
269 Object ob = keys.nextElement();
270 Rectangle rect = (Rectangle) nameHash.get(ob);
272 if (x >= rect.x && x <= (rect.x + rect.width) &&
273 y >= rect.y && y <= (rect.y + rect.height))
278 keys = nodeHash.keys();
280 while (keys.hasMoreElements())
282 Object ob = keys.nextElement();
283 Rectangle rect = (Rectangle) nodeHash.get(ob);
285 if (x >= rect.x && x <= (rect.x + rect.width) &&
286 y >= rect.y && y <= (rect.y + rect.height))
295 public void pickNodes(Rectangle pickBox)
297 int width = getSize().width;
298 int height = getSize().height;
300 SequenceNode top = tree.getTopNode();
302 float wscale = (float) (width * .8 - offx * 2) / tree.getMaxHeight()
306 top.count = ( (SequenceNode) top.left()).count +
307 ( (SequenceNode) top.right()).count;
309 float chunk = (float) (height - offy) / top.count;
311 pickNode(pickBox, top, chunk, wscale, width, offx, offy);
314 public void pickNode(Rectangle pickBox, SequenceNode node, float chunk,
315 float scale, int width, int offx, int offy)
322 if (node.left() == null && node.right() == null)
324 float height = node.height;
325 //float dist = node.dist;
327 //int xstart = (int) ( (height - dist) * scale) + offx;
328 int xend = (int) (height * scale) + offx;
330 int ypos = (int) (node.ycount * chunk) + offy;
332 if (pickBox.contains(new Point(xend, ypos)))
334 if (node.element() instanceof SequenceI)
336 SequenceI seq = (SequenceI) node.element();
337 SequenceGroup sg = av.getSelectionGroup();
340 sg.addOrRemove(seq, true);
347 pickNode(pickBox, (SequenceNode) node.left(), chunk, scale, width, offx,
349 pickNode(pickBox, (SequenceNode) node.right(), chunk, scale, width, offx,
354 public void setColor(SequenceNode node, Color c)
361 if (node.left() == null && node.right() == null)
365 if (node.element() instanceof SequenceI)
367 av.setSequenceColour( (SequenceI) node.element(), c);
373 setColor( (SequenceNode) node.left(), c);
374 setColor( (SequenceNode) node.right(), c);
378 public void update(Graphics g)
383 public void paint(Graphics g)
390 if (nameHash.size() == 0)
395 int width = scrollPane.getSize().width;
396 int height = scrollPane.getSize().height;
399 height = g.getFontMetrics(font).getHeight() * nameHash.size();
402 if (getSize().width > width)
404 setSize(new Dimension(width, height));
405 scrollPane.validate();
409 setSize(new Dimension(width, height));
413 draw(g, width, height);
417 public void draw(Graphics g, int width, int height)
419 offy = font.getSize() + 10;
421 g.setColor(Color.white);
422 g.fillRect(0, 0, width, height);
424 labelLength = g.getFontMetrics(font).stringWidth(longestName) + 20; //20 allows for scrollbar
426 float wscale = (float) (width - labelLength - offx * 2) / tree.getMaxHeight();
428 SequenceNode top = tree.getTopNode();
432 top.count = ( (SequenceNode) top.left()).count +
433 ( (SequenceNode) top.right()).count;
435 float chunk = (float) (height - offy) / top.count;
437 drawNode(g, tree.getTopNode(), chunk, wscale, width, offx, offy);
441 if (av.getCurrentTree() == tree)
443 g.setColor(Color.red);
447 g.setColor(Color.gray);
450 int x = (int) (threshold *
451 (float) (getSize().width - labelLength - 2 * offx) + offx);
453 g.drawLine(x, 0, x, getSize().height);
458 public void mouseReleased(MouseEvent e)
461 public void mouseEntered(MouseEvent e)
464 public void mouseExited(MouseEvent e)
467 public void mouseClicked(MouseEvent evt)
469 if (highlightNode != null)
471 if (evt.getClickCount() > 1)
473 tree.swapNodes(highlightNode);
474 tree.reCount(tree.getTopNode());
475 tree.findHeight(tree.getTopNode());
479 Vector leaves = new Vector();
480 tree.findLeaves(highlightNode, leaves);
482 for (int i = 0; i < leaves.size(); i++)
485 (SequenceI) ( (SequenceNode) leaves.elementAt(i)).element();
486 treeSelectionChanged(seq);
490 PaintRefresher.Refresh(this, av.getSequenceSetId());
495 public void mouseDragged(MouseEvent ect)
498 public void mouseMoved(MouseEvent evt)
500 av.setCurrentTree(tree);
502 Object ob = findElement(evt.getX(), evt.getY());
504 if (ob instanceof SequenceNode)
506 highlightNode = (SequenceNode) ob;
511 if (highlightNode != null)
513 highlightNode = null;
519 public void mousePressed(MouseEvent e)
521 av.setCurrentTree(tree);
526 Object ob = findElement(x, y);
528 if (ob instanceof SequenceI)
530 treeSelectionChanged( (Sequence) ob);
531 PaintRefresher.Refresh(this, av.getSequenceSetId());
535 else if (! (ob instanceof SequenceNode))
539 if (tree.getMaxHeight() != 0)
541 threshold = (float) (x - offx) /
542 (float) (getSize().width - labelLength - 2 * offx);
544 tree.getGroups().removeAllElements();
545 tree.groupNodes(tree.getTopNode(), threshold);
546 setColor(tree.getTopNode(), Color.black);
548 av.setSelectionGroup(null);
549 av.alignment.deleteAllGroups();
550 av.sequenceColours = null;
557 PaintRefresher.Refresh(this, av.getSequenceSetId());
564 for (int i = 0; i < tree.getGroups().size(); i++)
567 Color col = new Color( (int) (Math.random() * 255),
568 (int) (Math.random() * 255),
569 (int) (Math.random() * 255));
570 setColor( (SequenceNode) tree.getGroups().elementAt(i), col.brighter());
572 Vector l = tree.findLeaves( (SequenceNode) tree.getGroups().elementAt(
575 Vector sequences = new Vector();
576 for (int j = 0; j < l.size(); j++)
578 SequenceI s1 = (SequenceI) ( (SequenceNode) l.elementAt(j)).element();
579 if (!sequences.contains(s1))
581 sequences.addElement(s1);
585 ColourSchemeI cs = null;
587 if (av.getGlobalColourScheme() != null)
589 if (av.getGlobalColourScheme() instanceof UserColourScheme)
591 cs = new UserColourScheme(
592 ( (UserColourScheme) av.getGlobalColourScheme()).getColours());
597 cs = ColourSchemeProperty.getColour(sequences,
598 av.alignment.getWidth(),
599 ColourSchemeProperty.
601 av.getGlobalColourScheme()));
604 cs.setThreshold(av.getGlobalColourScheme().getThreshold(),
605 av.getIgnoreGapsConsensus());
608 SequenceGroup sg = new SequenceGroup(sequences, "",
611 av.alignment.getWidth() - 1);
613 sg.setName("JTreeGroup:" + sg.hashCode());
615 if (av.getGlobalColourScheme() != null
616 && av.getGlobalColourScheme().conservationApplied())
618 Conservation c = new Conservation("Group",
619 ResidueProperties.propHash, 3,
620 sg.getSequences(null),
625 c.verdict(false, av.ConsPercGaps);
626 cs.setConservation(c);
632 av.alignment.addGroup(sg);
638 public void setShowDistances(boolean state)
640 this.showDistances = state;
644 public void setShowBootstrap(boolean state)
646 this.showBootstrap = state;
650 public void setMarkPlaceholders(boolean state)
652 this.markPlaceholders = state;