2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5)
3 * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
11 * Jalview is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along with Jalview. If not, see <http://www.gnu.org/licenses/>.
18 package jalview.appletgui;
23 import java.awt.event.*;
25 import jalview.analysis.*;
26 import jalview.datamodel.*;
27 import jalview.schemes.*;
28 import jalview.util.*;
30 public class TreeCanvas extends Panel implements MouseListener,
35 ScrollPane scrollPane;
39 public static final String PLACEHOLDER = " * ";
43 boolean fitToWindow = true;
45 boolean showDistances = false;
47 boolean showBootstrap = false;
49 boolean markPlaceholders = false;
61 Hashtable nameHash = new Hashtable();
63 Hashtable nodeHash = new Hashtable();
65 SequenceNode highlightNode;
67 public TreeCanvas(AlignViewport av, ScrollPane scroller)
71 scrollPane = scroller;
72 addMouseListener(this);
73 addMouseMotionListener(this);
76 PaintRefresher.Register(this, av.getSequenceSetId());
79 public void treeSelectionChanged(SequenceI sequence)
81 SequenceGroup selected = av.getSelectionGroup();
84 selected = new SequenceGroup();
85 av.setSelectionGroup(selected);
88 selected.setEndRes(av.alignment.getWidth() - 1);
89 selected.addOrRemove(sequence, true);
92 public void setTree(NJTree tree)
95 tree.findHeight(tree.getTopNode());
97 // Now have to calculate longest name based on the leaves
98 Vector leaves = tree.findLeaves(tree.getTopNode(), new Vector());
99 boolean has_placeholders = false;
102 for (int i = 0; i < leaves.size(); i++)
104 SequenceNode lf = (SequenceNode) leaves.elementAt(i);
106 if (lf.isPlaceholder())
108 has_placeholders = true;
111 if (longestName.length() < ((Sequence) lf.element()).getName()
114 longestName = TreeCanvas.PLACEHOLDER
115 + ((Sequence) lf.element()).getName();
119 setMarkPlaceholders(has_placeholders);
122 public void drawNode(Graphics g, SequenceNode node, float chunk,
123 float scale, int width, int offx, int offy)
130 if (node.left() == null && node.right() == null)
134 float height = node.height;
135 float dist = node.dist;
137 int xstart = (int) ((height - dist) * scale) + offx;
138 int xend = (int) (height * scale) + offx;
140 int ypos = (int) (node.ycount * chunk) + offy;
142 if (node.element() instanceof SequenceI)
144 SequenceI seq = (SequenceI) ((SequenceNode) node).element();
146 if (av.getSequenceColour(seq) == Color.white)
148 g.setColor(Color.black);
152 g.setColor(av.getSequenceColour(seq).darker());
158 g.setColor(Color.black);
161 // Draw horizontal line
162 g.drawLine(xstart, ypos, xend, ypos);
164 String nodeLabel = "";
165 if (showDistances && node.dist > 0)
167 nodeLabel = new Format("%-.2f").form(node.dist);
171 int btstrap = node.getBootstrap();
176 nodeLabel = nodeLabel + " : ";
178 nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());
181 if (!nodeLabel.equals(""))
183 g.drawString(nodeLabel, xstart + 2, ypos - 2);
186 String name = (markPlaceholders && node.isPlaceholder()) ? (PLACEHOLDER + node
189 FontMetrics fm = g.getFontMetrics(font);
190 int charWidth = fm.stringWidth(name) + 3;
191 int charHeight = fm.getHeight();
193 Rectangle rect = new Rectangle(xend + 10, ypos - charHeight,
194 charWidth, charHeight);
196 nameHash.put((SequenceI) node.element(), rect);
198 // Colour selected leaves differently
199 SequenceGroup selected = av.getSelectionGroup();
201 && selected.getSequences(null).contains(
202 (SequenceI) node.element()))
204 g.setColor(Color.gray);
206 g.fillRect(xend + 10, ypos - charHeight + 3, charWidth, charHeight);
207 g.setColor(Color.white);
209 g.drawString(name, xend + 10, ypos);
210 g.setColor(Color.black);
214 drawNode(g, (SequenceNode) node.left(), chunk, scale, width, offx,
216 drawNode(g, (SequenceNode) node.right(), chunk, scale, width, offx,
219 float height = node.height;
220 float dist = node.dist;
222 int xstart = (int) ((height - dist) * scale) + offx;
223 int xend = (int) (height * scale) + offx;
224 int ypos = (int) (node.ycount * chunk) + offy;
226 g.setColor(((SequenceNode) node).color.darker());
228 // Draw horizontal line
229 g.drawLine(xstart, ypos, xend, ypos);
230 if (node == highlightNode)
232 g.fillRect(xend - 3, ypos - 3, 6, 6);
236 g.fillRect(xend - 2, ypos - 2, 4, 4);
239 int ystart = (int) (((SequenceNode) node.left()).ycount * chunk)
241 int yend = (int) (((SequenceNode) node.right()).ycount * chunk)
244 Rectangle pos = new Rectangle(xend - 2, ypos - 2, 5, 5);
245 nodeHash.put(node, pos);
247 g.drawLine((int) (height * scale) + offx, ystart,
248 (int) (height * scale) + offx, yend);
250 String nodeLabel = "";
252 if (showDistances && (node.dist > 0))
254 nodeLabel = new Format("%-.2f").form(node.dist);
259 int btstrap = node.getBootstrap();
264 nodeLabel = nodeLabel + " : ";
266 nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());
270 if (!nodeLabel.equals(""))
272 g.drawString(nodeLabel, xstart + 2, ypos - 2);
278 public Object findElement(int x, int y)
280 Enumeration keys = nameHash.keys();
282 while (keys.hasMoreElements())
284 Object ob = keys.nextElement();
285 Rectangle rect = (Rectangle) nameHash.get(ob);
287 if (x >= rect.x && x <= (rect.x + rect.width) && y >= rect.y
288 && y <= (rect.y + rect.height))
293 keys = nodeHash.keys();
295 while (keys.hasMoreElements())
297 Object ob = keys.nextElement();
298 Rectangle rect = (Rectangle) nodeHash.get(ob);
300 if (x >= rect.x && x <= (rect.x + rect.width) && y >= rect.y
301 && y <= (rect.y + rect.height))
310 public void pickNodes(Rectangle pickBox)
312 int width = getSize().width;
313 int height = getSize().height;
315 SequenceNode top = tree.getTopNode();
317 float wscale = (float) (width * .8 - offx * 2) / tree.getMaxHeight();
320 top.count = ((SequenceNode) top.left()).count
321 + ((SequenceNode) top.right()).count;
323 float chunk = (float) (height - offy) / top.count;
325 pickNode(pickBox, top, chunk, wscale, width, offx, offy);
328 public void pickNode(Rectangle pickBox, SequenceNode node, float chunk,
329 float scale, int width, int offx, int offy)
336 if (node.left() == null && node.right() == null)
338 float height = node.height;
339 // float dist = node.dist;
341 // int xstart = (int) ( (height - dist) * scale) + offx;
342 int xend = (int) (height * scale) + offx;
344 int ypos = (int) (node.ycount * chunk) + offy;
346 if (pickBox.contains(new Point(xend, ypos)))
348 if (node.element() instanceof SequenceI)
350 SequenceI seq = (SequenceI) node.element();
351 SequenceGroup sg = av.getSelectionGroup();
354 sg.addOrRemove(seq, true);
361 pickNode(pickBox, (SequenceNode) node.left(), chunk, scale, width,
363 pickNode(pickBox, (SequenceNode) node.right(), chunk, scale, width,
368 public void setColor(SequenceNode node, Color c)
375 if (node.left() == null && node.right() == null)
379 if (node.element() instanceof SequenceI)
381 av.setSequenceColour((SequenceI) node.element(), c);
387 setColor((SequenceNode) node.left(), c);
388 setColor((SequenceNode) node.right(), c);
392 public void update(Graphics g)
397 public void paint(Graphics g)
404 if (nameHash.size() == 0)
409 int width = scrollPane.getSize().width;
410 int height = scrollPane.getSize().height;
413 height = g.getFontMetrics(font).getHeight() * nameHash.size();
416 if (getSize().width > width)
418 setSize(new Dimension(width, height));
419 scrollPane.validate();
423 setSize(new Dimension(width, height));
426 draw(g, width, height);
430 public void draw(Graphics g, int width, int height)
432 offy = font.getSize() + 10;
434 g.setColor(Color.white);
435 g.fillRect(0, 0, width, height);
437 labelLength = g.getFontMetrics(font).stringWidth(longestName) + 20; // 20
442 float wscale = (float) (width - labelLength - offx * 2)
443 / tree.getMaxHeight();
445 SequenceNode top = tree.getTopNode();
449 top.count = ((SequenceNode) top.left()).count
450 + ((SequenceNode) top.right()).count;
452 float chunk = (float) (height - offy) / top.count;
454 drawNode(g, tree.getTopNode(), chunk, wscale, width, offx, offy);
458 if (av.getCurrentTree() == tree)
460 g.setColor(Color.red);
464 g.setColor(Color.gray);
467 int x = (int) (threshold
468 * (float) (getSize().width - labelLength - 2 * offx) + offx);
470 g.drawLine(x, 0, x, getSize().height);
475 public void mouseReleased(MouseEvent e)
479 public void mouseEntered(MouseEvent e)
483 public void mouseExited(MouseEvent e)
487 public void mouseClicked(MouseEvent evt)
489 if (highlightNode != null)
491 if (evt.getClickCount() > 1)
493 tree.swapNodes(highlightNode);
494 tree.reCount(tree.getTopNode());
495 tree.findHeight(tree.getTopNode());
499 Vector leaves = new Vector();
500 tree.findLeaves(highlightNode, leaves);
502 for (int i = 0; i < leaves.size(); i++)
504 SequenceI seq = (SequenceI) ((SequenceNode) leaves.elementAt(i))
506 treeSelectionChanged(seq);
510 PaintRefresher.Refresh(this, av.getSequenceSetId());
515 public void mouseDragged(MouseEvent ect)
519 public void mouseMoved(MouseEvent evt)
521 av.setCurrentTree(tree);
523 Object ob = findElement(evt.getX(), evt.getY());
525 if (ob instanceof SequenceNode)
527 highlightNode = (SequenceNode) ob;
532 if (highlightNode != null)
534 highlightNode = null;
540 public void mousePressed(MouseEvent e)
542 av.setCurrentTree(tree);
547 Object ob = findElement(x, y);
549 if (ob instanceof SequenceI)
551 treeSelectionChanged((Sequence) ob);
552 PaintRefresher.Refresh(this, av.getSequenceSetId());
556 else if (!(ob instanceof SequenceNode))
560 if (tree.getMaxHeight() != 0)
562 threshold = (float) (x - offx)
563 / (float) (getSize().width - labelLength - 2 * offx);
565 tree.getGroups().removeAllElements();
566 tree.groupNodes(tree.getTopNode(), threshold);
567 setColor(tree.getTopNode(), Color.black);
569 av.setSelectionGroup(null);
570 av.alignment.deleteAllGroups();
571 av.sequenceColours = null;
578 PaintRefresher.Refresh(this, av.getSequenceSetId());
585 for (int i = 0; i < tree.getGroups().size(); i++)
588 Color col = new Color((int) (Math.random() * 255), (int) (Math
589 .random() * 255), (int) (Math.random() * 255));
590 setColor((SequenceNode) tree.getGroups().elementAt(i), col.brighter());
592 Vector l = tree.findLeaves((SequenceNode) tree.getGroups().elementAt(
595 Vector sequences = new Vector();
596 for (int j = 0; j < l.size(); j++)
598 SequenceI s1 = (SequenceI) ((SequenceNode) l.elementAt(j))
600 if (!sequences.contains(s1))
602 sequences.addElement(s1);
606 ColourSchemeI cs = null;
608 if (av.getGlobalColourScheme() != null)
610 if (av.getGlobalColourScheme() instanceof UserColourScheme)
612 cs = new UserColourScheme(((UserColourScheme) av
613 .getGlobalColourScheme()).getColours());
618 cs = ColourSchemeProperty.getColour(sequences, av.alignment
619 .getWidth(), ColourSchemeProperty.getColourName(av
620 .getGlobalColourScheme()));
623 cs.setThreshold(av.getGlobalColourScheme().getThreshold(), av
624 .getIgnoreGapsConsensus());
627 SequenceGroup sg = new SequenceGroup(sequences, "", cs, true, true,
628 false, 0, av.alignment.getWidth() - 1);
630 sg.setName("JTreeGroup:" + sg.hashCode());
632 if (av.getGlobalColourScheme() != null
633 && av.getGlobalColourScheme().conservationApplied())
635 Conservation c = new Conservation("Group",
636 ResidueProperties.propHash, 3, sg.getSequences(null), sg
637 .getStartRes(), sg.getEndRes());
640 c.verdict(false, av.ConsPercGaps);
641 cs.setConservation(c);
647 av.alignment.addGroup(sg);
653 public void setShowDistances(boolean state)
655 this.showDistances = state;
659 public void setShowBootstrap(boolean state)
661 this.showBootstrap = state;
665 public void setMarkPlaceholders(boolean state)
667 this.markPlaceholders = state;