2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.0b1)
3 * Copyright (C) 2014 The Jalview Authors
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/>.
17 * The Jalview Authors are detailed in the 'AUTHORS' file.
19 package jalview.appletgui;
24 import java.awt.event.*;
26 import jalview.analysis.*;
27 import jalview.datamodel.*;
28 import jalview.schemes.*;
29 import jalview.util.*;
31 public class TreeCanvas extends Panel implements MouseListener,
36 ScrollPane scrollPane;
40 public static final String PLACEHOLDER = " * ";
44 boolean fitToWindow = true;
46 boolean showDistances = false;
48 boolean showBootstrap = false;
50 boolean markPlaceholders = false;
62 Hashtable nameHash = new Hashtable();
64 Hashtable nodeHash = new Hashtable();
66 SequenceNode highlightNode;
70 public TreeCanvas(AlignmentPanel ap, ScrollPane scroller)
75 scrollPane = scroller;
76 addMouseListener(this);
77 addMouseMotionListener(this);
80 PaintRefresher.Register(this, av.getSequenceSetId());
83 public void treeSelectionChanged(SequenceI sequence)
85 SequenceGroup selected = av.getSelectionGroup();
88 selected = new SequenceGroup();
89 av.setSelectionGroup(selected);
92 selected.setEndRes(av.getAlignment().getWidth() - 1);
93 selected.addOrRemove(sequence, true);
96 public void setTree(NJTree tree)
99 tree.findHeight(tree.getTopNode());
101 // Now have to calculate longest name based on the leaves
102 Vector leaves = tree.findLeaves(tree.getTopNode(), new Vector());
103 boolean has_placeholders = false;
106 for (int i = 0; i < leaves.size(); i++)
108 SequenceNode lf = (SequenceNode) leaves.elementAt(i);
110 if (lf.isPlaceholder())
112 has_placeholders = true;
115 if (longestName.length() < ((Sequence) lf.element()).getName()
118 longestName = TreeCanvas.PLACEHOLDER
119 + ((Sequence) lf.element()).getName();
123 setMarkPlaceholders(has_placeholders);
126 public void drawNode(Graphics g, SequenceNode node, float chunk,
127 float scale, int width, int offx, int offy)
134 if (node.left() == null && node.right() == null)
138 float height = node.height;
139 float dist = node.dist;
141 int xstart = (int) ((height - dist) * scale) + offx;
142 int xend = (int) (height * scale) + offx;
144 int ypos = (int) (node.ycount * chunk) + offy;
146 if (node.element() instanceof SequenceI)
148 SequenceI seq = (SequenceI) node.element();
150 if (av.getSequenceColour(seq) == Color.white)
152 g.setColor(Color.black);
156 g.setColor(av.getSequenceColour(seq).darker());
162 g.setColor(Color.black);
165 // Draw horizontal line
166 g.drawLine(xstart, ypos, xend, ypos);
168 String nodeLabel = "";
169 if (showDistances && node.dist > 0)
171 nodeLabel = new Format("%-.2f").form(node.dist);
175 int btstrap = node.getBootstrap();
180 nodeLabel = nodeLabel + " : ";
182 nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());
185 if (!nodeLabel.equals(""))
187 g.drawString(nodeLabel, xstart + 2, ypos - 2);
190 String name = (markPlaceholders && node.isPlaceholder()) ? (PLACEHOLDER + node
191 .getName()) : node.getName();
192 FontMetrics fm = g.getFontMetrics(font);
193 int charWidth = fm.stringWidth(name) + 3;
194 int charHeight = fm.getHeight();
196 Rectangle rect = new Rectangle(xend + 10, ypos - charHeight,
197 charWidth, charHeight);
199 nameHash.put(node.element(), rect);
201 // Colour selected leaves differently
202 SequenceGroup selected = av.getSelectionGroup();
204 && selected.getSequences(null).contains(node.element()))
206 g.setColor(Color.gray);
208 g.fillRect(xend + 10, ypos - charHeight + 3, charWidth, charHeight);
209 g.setColor(Color.white);
211 g.drawString(name, xend + 10, ypos);
212 g.setColor(Color.black);
216 drawNode(g, (SequenceNode) node.left(), chunk, scale, width, offx,
218 drawNode(g, (SequenceNode) node.right(), chunk, scale, width, offx,
221 float height = node.height;
222 float dist = node.dist;
224 int xstart = (int) ((height - dist) * scale) + offx;
225 int xend = (int) (height * scale) + offx;
226 int ypos = (int) (node.ycount * chunk) + offy;
228 g.setColor(node.color.darker());
230 // Draw horizontal line
231 g.drawLine(xstart, ypos, xend, ypos);
232 if (node == highlightNode)
234 g.fillRect(xend - 3, ypos - 3, 6, 6);
238 g.fillRect(xend - 2, ypos - 2, 4, 4);
241 int ystart = (int) (((SequenceNode) node.left()).ycount * chunk)
243 int yend = (int) (((SequenceNode) node.right()).ycount * chunk)
246 Rectangle pos = new Rectangle(xend - 2, ypos - 2, 5, 5);
247 nodeHash.put(node, pos);
249 g.drawLine((int) (height * scale) + offx, ystart,
250 (int) (height * scale) + offx, yend);
252 String nodeLabel = "";
254 if (showDistances && (node.dist > 0))
256 nodeLabel = new Format("%-.2f").form(node.dist);
261 int btstrap = node.getBootstrap();
266 nodeLabel = nodeLabel + " : ";
268 nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());
272 if (!nodeLabel.equals(""))
274 g.drawString(nodeLabel, xstart + 2, ypos - 2);
280 public Object findElement(int x, int y)
282 Enumeration keys = nameHash.keys();
284 while (keys.hasMoreElements())
286 Object ob = keys.nextElement();
287 Rectangle rect = (Rectangle) nameHash.get(ob);
289 if (x >= rect.x && x <= (rect.x + rect.width) && y >= rect.y
290 && y <= (rect.y + rect.height))
295 keys = nodeHash.keys();
297 while (keys.hasMoreElements())
299 Object ob = keys.nextElement();
300 Rectangle rect = (Rectangle) nodeHash.get(ob);
302 if (x >= rect.x && x <= (rect.x + rect.width) && y >= rect.y
303 && y <= (rect.y + rect.height))
312 public void pickNodes(Rectangle pickBox)
314 int width = getSize().width;
315 int height = getSize().height;
317 SequenceNode top = tree.getTopNode();
319 float wscale = (float) (width * .8 - offx * 2) / tree.getMaxHeight();
322 top.count = ((SequenceNode) top.left()).count
323 + ((SequenceNode) top.right()).count;
325 float chunk = (float) (height - offy) / top.count;
327 pickNode(pickBox, top, chunk, wscale, width, offx, offy);
330 public void pickNode(Rectangle pickBox, SequenceNode node, float chunk,
331 float scale, int width, int offx, int offy)
338 if (node.left() == null && node.right() == null)
340 float height = node.height;
341 // float dist = node.dist;
343 // int xstart = (int) ( (height - dist) * scale) + offx;
344 int xend = (int) (height * scale) + offx;
346 int ypos = (int) (node.ycount * chunk) + offy;
348 if (pickBox.contains(new Point(xend, ypos)))
350 if (node.element() instanceof SequenceI)
352 SequenceI seq = (SequenceI) node.element();
353 SequenceGroup sg = av.getSelectionGroup();
356 sg.addOrRemove(seq, true);
363 pickNode(pickBox, (SequenceNode) node.left(), chunk, scale, width,
365 pickNode(pickBox, (SequenceNode) node.right(), chunk, scale, width,
370 public void setColor(SequenceNode node, Color c)
377 if (node.left() == null && node.right() == null)
381 if (node.element() instanceof SequenceI)
383 av.setSequenceColour((SequenceI) node.element(), c);
389 setColor((SequenceNode) node.left(), c);
390 setColor((SequenceNode) node.right(), c);
395 public void update(Graphics g)
401 public void paint(Graphics g)
408 if (nameHash.size() == 0)
413 int width = scrollPane.getSize().width;
414 int height = scrollPane.getSize().height;
417 height = g.getFontMetrics(font).getHeight() * nameHash.size();
420 if (getSize().width > width)
422 setSize(new Dimension(width, height));
423 scrollPane.validate();
427 setSize(new Dimension(width, height));
430 draw(g, width, height);
434 public void draw(Graphics g, int width, int height)
436 offy = font.getSize() + 10;
438 g.setColor(Color.white);
439 g.fillRect(0, 0, width, height);
441 labelLength = g.getFontMetrics(font).stringWidth(longestName) + 20; // 20
446 float wscale = (width - labelLength - offx * 2) / tree.getMaxHeight();
448 SequenceNode top = tree.getTopNode();
452 top.count = ((SequenceNode) top.left()).count
453 + ((SequenceNode) top.right()).count;
455 float chunk = (float) (height - offy) / top.count;
457 drawNode(g, tree.getTopNode(), chunk, wscale, width, offx, offy);
461 if (av.getCurrentTree() == tree)
463 g.setColor(Color.red);
467 g.setColor(Color.gray);
470 int x = (int) (threshold * (getSize().width - labelLength - 2 * offx) + offx);
472 g.drawLine(x, 0, x, getSize().height);
478 public void mouseReleased(MouseEvent e)
483 public void mouseEntered(MouseEvent e)
488 public void mouseExited(MouseEvent e)
493 public void mouseClicked(MouseEvent evt)
495 if (highlightNode != null)
497 if (evt.getClickCount() > 1)
499 tree.swapNodes(highlightNode);
500 tree.reCount(tree.getTopNode());
501 tree.findHeight(tree.getTopNode());
505 Vector leaves = new Vector();
506 tree.findLeaves(highlightNode, leaves);
508 for (int i = 0; i < leaves.size(); i++)
510 SequenceI seq = (SequenceI) ((SequenceNode) leaves.elementAt(i))
512 treeSelectionChanged(seq);
516 PaintRefresher.Refresh(this, av.getSequenceSetId());
523 public void mouseDragged(MouseEvent ect)
528 public void mouseMoved(MouseEvent evt)
530 av.setCurrentTree(tree);
532 Object ob = findElement(evt.getX(), evt.getY());
534 if (ob instanceof SequenceNode)
536 highlightNode = (SequenceNode) ob;
541 if (highlightNode != null)
543 highlightNode = null;
550 public void mousePressed(MouseEvent e)
552 av.setCurrentTree(tree);
557 Object ob = findElement(x, y);
559 if (ob instanceof SequenceI)
561 treeSelectionChanged((Sequence) ob);
562 PaintRefresher.Refresh(this, av.getSequenceSetId());
567 else if (!(ob instanceof SequenceNode))
571 if (tree.getMaxHeight() != 0)
573 threshold = (float) (x - offx)
574 / (float) (getSize().width - labelLength - 2 * offx);
576 tree.getGroups().removeAllElements();
577 tree.groupNodes(tree.getTopNode(), threshold);
578 setColor(tree.getTopNode(), Color.black);
580 av.setSelectionGroup(null);
581 av.getAlignment().deleteAllGroups();
582 av.clearSequenceColours();
589 PaintRefresher.Refresh(this, av.getSequenceSetId());
596 for (int i = 0; i < tree.getGroups().size(); i++)
599 Color col = new Color((int) (Math.random() * 255),
600 (int) (Math.random() * 255), (int) (Math.random() * 255));
601 setColor((SequenceNode) tree.getGroups().elementAt(i), col.brighter());
603 Vector l = tree.findLeaves(
604 (SequenceNode) tree.getGroups().elementAt(i), new Vector());
606 Vector sequences = new Vector();
607 for (int j = 0; j < l.size(); j++)
609 SequenceI s1 = (SequenceI) ((SequenceNode) l.elementAt(j))
611 if (!sequences.contains(s1))
613 sequences.addElement(s1);
617 ColourSchemeI cs = null;
619 SequenceGroup sg = new SequenceGroup(sequences, "", cs, true, true,
620 false, 0, av.getAlignment().getWidth() - 1);
622 if (av.getGlobalColourScheme() != null)
624 if (av.getGlobalColourScheme() instanceof UserColourScheme)
626 cs = new UserColourScheme(
627 ((UserColourScheme) av.getGlobalColourScheme())
633 cs = ColourSchemeProperty.getColour(sg, ColourSchemeProperty
634 .getColourName(av.getGlobalColourScheme()));
636 // cs is null if shading is an annotationColourGradient
639 cs.setThreshold(av.getGlobalColourScheme().getThreshold(),
640 av.getIgnoreGapsConsensus());
643 // TODO: cs used to be initialized with a sequence collection and
644 // recalcConservation called automatically
645 // instead we set it manually - recalc called after updateAnnotation
648 sg.setName("JTreeGroup:" + sg.hashCode());
650 if (av.getGlobalColourScheme() != null
651 && av.getGlobalColourScheme().conservationApplied())
653 Conservation c = new Conservation("Group",
654 ResidueProperties.propHash, 3, sg.getSequences(null),
655 sg.getStartRes(), sg.getEndRes());
658 c.verdict(false, av.getConsPercGaps());
659 cs.setConservation(c);
665 av.getAlignment().addGroup(sg);
668 ap.updateAnnotation();
672 public void setShowDistances(boolean state)
674 this.showDistances = state;
678 public void setShowBootstrap(boolean state)
680 this.showBootstrap = state;
684 public void setMarkPlaceholders(boolean state)
686 this.markPlaceholders = state;