2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
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
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.appletgui;
26 import java.awt.event.*;
28 import jalview.analysis.*;
29 import jalview.datamodel.*;
30 import jalview.schemes.*;
31 import jalview.util.*;
33 public class TreeCanvas extends Panel implements MouseListener,
38 ScrollPane scrollPane;
42 public static final String PLACEHOLDER = " * ";
46 boolean fitToWindow = true;
48 boolean showDistances = false;
50 boolean showBootstrap = false;
52 boolean markPlaceholders = false;
64 Hashtable nameHash = new Hashtable();
66 Hashtable nodeHash = new Hashtable();
68 SequenceNode highlightNode;
72 public TreeCanvas(AlignmentPanel ap, ScrollPane scroller)
77 scrollPane = scroller;
78 addMouseListener(this);
79 addMouseMotionListener(this);
82 PaintRefresher.Register(this, av.getSequenceSetId());
85 public void treeSelectionChanged(SequenceI sequence)
87 SequenceGroup selected = av.getSelectionGroup();
90 selected = new SequenceGroup();
91 av.setSelectionGroup(selected);
94 selected.setEndRes(av.getAlignment().getWidth() - 1);
95 selected.addOrRemove(sequence, true);
98 public void setTree(NJTree tree)
101 tree.findHeight(tree.getTopNode());
103 // Now have to calculate longest name based on the leaves
104 Vector leaves = tree.findLeaves(tree.getTopNode(), new Vector());
105 boolean has_placeholders = false;
108 for (int i = 0; i < leaves.size(); i++)
110 SequenceNode lf = (SequenceNode) leaves.elementAt(i);
112 if (lf.isPlaceholder())
114 has_placeholders = true;
117 if (longestName.length() < ((Sequence) lf.element()).getName()
120 longestName = TreeCanvas.PLACEHOLDER
121 + ((Sequence) lf.element()).getName();
125 setMarkPlaceholders(has_placeholders);
128 public void drawNode(Graphics g, SequenceNode node, float chunk,
129 float scale, int width, int offx, int offy)
136 if (node.left() == null && node.right() == null)
140 float height = node.height;
141 float dist = node.dist;
143 int xstart = (int) ((height - dist) * scale) + offx;
144 int xend = (int) (height * scale) + offx;
146 int ypos = (int) (node.ycount * chunk) + offy;
148 if (node.element() instanceof SequenceI)
150 SequenceI seq = (SequenceI) node.element();
152 if (av.getSequenceColour(seq) == Color.white)
154 g.setColor(Color.black);
158 g.setColor(av.getSequenceColour(seq).darker());
164 g.setColor(Color.black);
167 // Draw horizontal line
168 g.drawLine(xstart, ypos, xend, ypos);
170 String nodeLabel = "";
171 if (showDistances && node.dist > 0)
173 nodeLabel = new Format("%-.2f").form(node.dist);
177 int btstrap = node.getBootstrap();
182 nodeLabel = nodeLabel + " : ";
184 nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());
187 if (!nodeLabel.equals(""))
189 g.drawString(nodeLabel, xstart + 2, ypos - 2);
192 String name = (markPlaceholders && node.isPlaceholder()) ? (PLACEHOLDER + node
193 .getName()) : node.getName();
194 FontMetrics fm = g.getFontMetrics(font);
195 int charWidth = fm.stringWidth(name) + 3;
196 int charHeight = fm.getHeight();
198 Rectangle rect = new Rectangle(xend + 10, ypos - charHeight,
199 charWidth, charHeight);
201 nameHash.put(node.element(), rect);
203 // Colour selected leaves differently
204 SequenceGroup selected = av.getSelectionGroup();
206 && selected.getSequences(null).contains(node.element()))
208 g.setColor(Color.gray);
210 g.fillRect(xend + 10, ypos - charHeight + 3, charWidth, charHeight);
211 g.setColor(Color.white);
213 g.drawString(name, xend + 10, ypos);
214 g.setColor(Color.black);
218 drawNode(g, (SequenceNode) node.left(), chunk, scale, width, offx,
220 drawNode(g, (SequenceNode) node.right(), chunk, scale, width, offx,
223 float height = node.height;
224 float dist = node.dist;
226 int xstart = (int) ((height - dist) * scale) + offx;
227 int xend = (int) (height * scale) + offx;
228 int ypos = (int) (node.ycount * chunk) + offy;
230 g.setColor(node.color.darker());
232 // Draw horizontal line
233 g.drawLine(xstart, ypos, xend, ypos);
234 if (node == highlightNode)
236 g.fillRect(xend - 3, ypos - 3, 6, 6);
240 g.fillRect(xend - 2, ypos - 2, 4, 4);
243 int ystart = (int) (((SequenceNode) node.left()).ycount * chunk)
245 int yend = (int) (((SequenceNode) node.right()).ycount * chunk)
248 Rectangle pos = new Rectangle(xend - 2, ypos - 2, 5, 5);
249 nodeHash.put(node, pos);
251 g.drawLine((int) (height * scale) + offx, ystart,
252 (int) (height * scale) + offx, yend);
254 String nodeLabel = "";
256 if (showDistances && (node.dist > 0))
258 nodeLabel = new Format("%-.2f").form(node.dist);
263 int btstrap = node.getBootstrap();
268 nodeLabel = nodeLabel + " : ";
270 nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());
274 if (!nodeLabel.equals(""))
276 g.drawString(nodeLabel, xstart + 2, ypos - 2);
282 public Object findElement(int x, int y)
284 Enumeration keys = nameHash.keys();
286 while (keys.hasMoreElements())
288 Object ob = keys.nextElement();
289 Rectangle rect = (Rectangle) nameHash.get(ob);
291 if (x >= rect.x && x <= (rect.x + rect.width) && y >= rect.y
292 && y <= (rect.y + rect.height))
297 keys = nodeHash.keys();
299 while (keys.hasMoreElements())
301 Object ob = keys.nextElement();
302 Rectangle rect = (Rectangle) nodeHash.get(ob);
304 if (x >= rect.x && x <= (rect.x + rect.width) && y >= rect.y
305 && y <= (rect.y + rect.height))
314 public void pickNodes(Rectangle pickBox)
316 int width = getSize().width;
317 int height = getSize().height;
319 SequenceNode top = tree.getTopNode();
321 float wscale = (float) (width * .8 - offx * 2) / tree.getMaxHeight();
324 top.count = ((SequenceNode) top.left()).count
325 + ((SequenceNode) top.right()).count;
327 float chunk = (float) (height - offy) / top.count;
329 pickNode(pickBox, top, chunk, wscale, width, offx, offy);
332 public void pickNode(Rectangle pickBox, SequenceNode node, float chunk,
333 float scale, int width, int offx, int offy)
340 if (node.left() == null && node.right() == null)
342 float height = node.height;
343 // float dist = node.dist;
345 // int xstart = (int) ( (height - dist) * scale) + offx;
346 int xend = (int) (height * scale) + offx;
348 int ypos = (int) (node.ycount * chunk) + offy;
350 if (pickBox.contains(new Point(xend, ypos)))
352 if (node.element() instanceof SequenceI)
354 SequenceI seq = (SequenceI) node.element();
355 SequenceGroup sg = av.getSelectionGroup();
358 sg.addOrRemove(seq, true);
365 pickNode(pickBox, (SequenceNode) node.left(), chunk, scale, width,
367 pickNode(pickBox, (SequenceNode) node.right(), chunk, scale, width,
372 public void setColor(SequenceNode node, Color c)
379 if (node.left() == null && node.right() == null)
383 if (node.element() instanceof SequenceI)
385 av.setSequenceColour((SequenceI) node.element(), c);
391 setColor((SequenceNode) node.left(), c);
392 setColor((SequenceNode) node.right(), c);
397 public void update(Graphics g)
403 public void paint(Graphics g)
410 if (nameHash.size() == 0)
415 int width = scrollPane.getSize().width;
416 int height = scrollPane.getSize().height;
419 height = g.getFontMetrics(font).getHeight() * nameHash.size();
422 if (getSize().width > width)
424 setSize(new Dimension(width, height));
425 scrollPane.validate();
429 setSize(new Dimension(width, height));
432 draw(g, width, height);
436 public void draw(Graphics g, int width, int height)
438 offy = font.getSize() + 10;
440 g.setColor(Color.white);
441 g.fillRect(0, 0, width, height);
443 labelLength = g.getFontMetrics(font).stringWidth(longestName) + 20; // 20
448 float wscale = (width - labelLength - offx * 2) / tree.getMaxHeight();
450 SequenceNode top = tree.getTopNode();
454 top.count = ((SequenceNode) top.left()).count
455 + ((SequenceNode) top.right()).count;
457 float chunk = (float) (height - offy) / top.count;
459 drawNode(g, tree.getTopNode(), chunk, wscale, width, offx, offy);
463 if (av.getCurrentTree() == tree)
465 g.setColor(Color.red);
469 g.setColor(Color.gray);
472 int x = (int) (threshold * (getSize().width - labelLength - 2 * offx) + offx);
474 g.drawLine(x, 0, x, getSize().height);
480 public void mouseReleased(MouseEvent e)
485 public void mouseEntered(MouseEvent e)
490 public void mouseExited(MouseEvent e)
495 public void mouseClicked(MouseEvent evt)
497 if (highlightNode != null)
499 if (evt.getClickCount() > 1)
501 tree.swapNodes(highlightNode);
502 tree.reCount(tree.getTopNode());
503 tree.findHeight(tree.getTopNode());
507 Vector leaves = new Vector();
508 tree.findLeaves(highlightNode, leaves);
510 for (int i = 0; i < leaves.size(); i++)
512 SequenceI seq = (SequenceI) ((SequenceNode) leaves.elementAt(i))
514 treeSelectionChanged(seq);
518 PaintRefresher.Refresh(this, av.getSequenceSetId());
525 public void mouseDragged(MouseEvent ect)
530 public void mouseMoved(MouseEvent evt)
532 av.setCurrentTree(tree);
534 Object ob = findElement(evt.getX(), evt.getY());
536 if (ob instanceof SequenceNode)
538 highlightNode = (SequenceNode) ob;
543 if (highlightNode != null)
545 highlightNode = null;
552 public void mousePressed(MouseEvent e)
554 av.setCurrentTree(tree);
559 Object ob = findElement(x, y);
561 if (ob instanceof SequenceI)
563 treeSelectionChanged((Sequence) ob);
564 PaintRefresher.Refresh(this, av.getSequenceSetId());
569 else if (!(ob instanceof SequenceNode))
573 if (tree.getMaxHeight() != 0)
575 threshold = (float) (x - offx)
576 / (float) (getSize().width - labelLength - 2 * offx);
578 tree.getGroups().removeAllElements();
579 tree.groupNodes(tree.getTopNode(), threshold);
580 setColor(tree.getTopNode(), Color.black);
582 av.setSelectionGroup(null);
583 av.getAlignment().deleteAllGroups();
584 av.clearSequenceColours();
591 PaintRefresher.Refresh(this, av.getSequenceSetId());
598 for (int i = 0; i < tree.getGroups().size(); i++)
601 Color col = new Color((int) (Math.random() * 255),
602 (int) (Math.random() * 255), (int) (Math.random() * 255));
603 setColor((SequenceNode) tree.getGroups().elementAt(i), col.brighter());
605 Vector l = tree.findLeaves(
606 (SequenceNode) tree.getGroups().elementAt(i), new Vector());
608 Vector sequences = new Vector();
609 for (int j = 0; j < l.size(); j++)
611 SequenceI s1 = (SequenceI) ((SequenceNode) l.elementAt(j))
613 if (!sequences.contains(s1))
615 sequences.addElement(s1);
619 ColourSchemeI cs = null;
621 SequenceGroup sg = new SequenceGroup(sequences, "", cs, true, true,
622 false, 0, av.getAlignment().getWidth() - 1);
624 if (av.getGlobalColourScheme() != null)
626 if (av.getGlobalColourScheme() instanceof UserColourScheme)
628 cs = new UserColourScheme(
629 ((UserColourScheme) av.getGlobalColourScheme())
635 cs = ColourSchemeProperty.getColour(sg, ColourSchemeProperty
636 .getColourName(av.getGlobalColourScheme()));
638 // cs is null if shading is an annotationColourGradient
641 cs.setThreshold(av.getGlobalColourScheme().getThreshold(),
642 av.getIgnoreGapsConsensus());
645 // TODO: cs used to be initialized with a sequence collection and
646 // recalcConservation called automatically
647 // instead we set it manually - recalc called after updateAnnotation
650 sg.setName("JTreeGroup:" + sg.hashCode());
652 if (av.getGlobalColourScheme() != null
653 && av.getGlobalColourScheme().conservationApplied())
655 Conservation c = new Conservation("Group",
656 ResidueProperties.propHash, 3, sg.getSequences(null),
657 sg.getStartRes(), sg.getEndRes());
660 c.verdict(false, av.getConsPercGaps());
661 cs.setConservation(c);
667 av.getAlignment().addGroup(sg);
670 ap.updateAnnotation();
674 public void setShowDistances(boolean state)
676 this.showDistances = state;
680 public void setShowBootstrap(boolean state)
682 this.showBootstrap = state;
686 public void setMarkPlaceholders(boolean state)
688 this.markPlaceholders = state;