2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3 * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, 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;
69 public TreeCanvas(AlignmentPanel ap, ScrollPane scroller)
74 scrollPane = scroller;
75 addMouseListener(this);
76 addMouseMotionListener(this);
79 PaintRefresher.Register(this, av.getSequenceSetId());
82 public void treeSelectionChanged(SequenceI sequence)
84 SequenceGroup selected = av.getSelectionGroup();
87 selected = new SequenceGroup();
88 av.setSelectionGroup(selected);
91 selected.setEndRes(av.getAlignment().getWidth() - 1);
92 selected.addOrRemove(sequence, true);
95 public void setTree(NJTree tree)
98 tree.findHeight(tree.getTopNode());
100 // Now have to calculate longest name based on the leaves
101 Vector leaves = tree.findLeaves(tree.getTopNode(), new Vector());
102 boolean has_placeholders = false;
105 for (int i = 0; i < leaves.size(); i++)
107 SequenceNode lf = (SequenceNode) leaves.elementAt(i);
109 if (lf.isPlaceholder())
111 has_placeholders = true;
114 if (longestName.length() < ((Sequence) lf.element()).getName()
117 longestName = TreeCanvas.PLACEHOLDER
118 + ((Sequence) lf.element()).getName();
122 setMarkPlaceholders(has_placeholders);
125 public void drawNode(Graphics g, SequenceNode node, float chunk,
126 float scale, int width, int offx, int offy)
133 if (node.left() == null && node.right() == null)
137 float height = node.height;
138 float dist = node.dist;
140 int xstart = (int) ((height - dist) * scale) + offx;
141 int xend = (int) (height * scale) + offx;
143 int ypos = (int) (node.ycount * chunk) + offy;
145 if (node.element() instanceof SequenceI)
147 SequenceI seq = (SequenceI) node.element();
149 if (av.getSequenceColour(seq) == Color.white)
151 g.setColor(Color.black);
155 g.setColor(av.getSequenceColour(seq).darker());
161 g.setColor(Color.black);
164 // Draw horizontal line
165 g.drawLine(xstart, ypos, xend, ypos);
167 String nodeLabel = "";
168 if (showDistances && node.dist > 0)
170 nodeLabel = new Format("%-.2f").form(node.dist);
174 int btstrap = node.getBootstrap();
179 nodeLabel = nodeLabel + " : ";
181 nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());
184 if (!nodeLabel.equals(""))
186 g.drawString(nodeLabel, xstart + 2, ypos - 2);
189 String name = (markPlaceholders && node.isPlaceholder()) ? (PLACEHOLDER + node
190 .getName()) : node.getName();
191 FontMetrics fm = g.getFontMetrics(font);
192 int charWidth = fm.stringWidth(name) + 3;
193 int charHeight = fm.getHeight();
195 Rectangle rect = new Rectangle(xend + 10, ypos - charHeight,
196 charWidth, charHeight);
198 nameHash.put(node.element(), rect);
200 // Colour selected leaves differently
201 SequenceGroup selected = av.getSelectionGroup();
203 && selected.getSequences(null).contains(node.element()))
205 g.setColor(Color.gray);
207 g.fillRect(xend + 10, ypos - charHeight + 3, charWidth, charHeight);
208 g.setColor(Color.white);
210 g.drawString(name, xend + 10, ypos);
211 g.setColor(Color.black);
215 drawNode(g, (SequenceNode) node.left(), chunk, scale, width, offx,
217 drawNode(g, (SequenceNode) node.right(), chunk, scale, width, offx,
220 float height = node.height;
221 float dist = node.dist;
223 int xstart = (int) ((height - dist) * scale) + offx;
224 int xend = (int) (height * scale) + offx;
225 int ypos = (int) (node.ycount * chunk) + offy;
227 g.setColor(node.color.darker());
229 // Draw horizontal line
230 g.drawLine(xstart, ypos, xend, ypos);
231 if (node == highlightNode)
233 g.fillRect(xend - 3, ypos - 3, 6, 6);
237 g.fillRect(xend - 2, ypos - 2, 4, 4);
240 int ystart = (int) (((SequenceNode) node.left()).ycount * chunk)
242 int yend = (int) (((SequenceNode) node.right()).ycount * chunk)
245 Rectangle pos = new Rectangle(xend - 2, ypos - 2, 5, 5);
246 nodeHash.put(node, pos);
248 g.drawLine((int) (height * scale) + offx, ystart,
249 (int) (height * scale) + offx, yend);
251 String nodeLabel = "";
253 if (showDistances && (node.dist > 0))
255 nodeLabel = new Format("%-.2f").form(node.dist);
260 int btstrap = node.getBootstrap();
265 nodeLabel = nodeLabel + " : ";
267 nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());
271 if (!nodeLabel.equals(""))
273 g.drawString(nodeLabel, xstart + 2, ypos - 2);
279 public Object findElement(int x, int y)
281 Enumeration keys = nameHash.keys();
283 while (keys.hasMoreElements())
285 Object ob = keys.nextElement();
286 Rectangle rect = (Rectangle) nameHash.get(ob);
288 if (x >= rect.x && x <= (rect.x + rect.width) && y >= rect.y
289 && y <= (rect.y + rect.height))
294 keys = nodeHash.keys();
296 while (keys.hasMoreElements())
298 Object ob = keys.nextElement();
299 Rectangle rect = (Rectangle) nodeHash.get(ob);
301 if (x >= rect.x && x <= (rect.x + rect.width) && y >= rect.y
302 && y <= (rect.y + rect.height))
311 public void pickNodes(Rectangle pickBox)
313 int width = getSize().width;
314 int height = getSize().height;
316 SequenceNode top = tree.getTopNode();
318 float wscale = (float) (width * .8 - offx * 2) / tree.getMaxHeight();
321 top.count = ((SequenceNode) top.left()).count
322 + ((SequenceNode) top.right()).count;
324 float chunk = (float) (height - offy) / top.count;
326 pickNode(pickBox, top, chunk, wscale, width, offx, offy);
329 public void pickNode(Rectangle pickBox, SequenceNode node, float chunk,
330 float scale, int width, int offx, int offy)
337 if (node.left() == null && node.right() == null)
339 float height = node.height;
340 // float dist = node.dist;
342 // int xstart = (int) ( (height - dist) * scale) + offx;
343 int xend = (int) (height * scale) + offx;
345 int ypos = (int) (node.ycount * chunk) + offy;
347 if (pickBox.contains(new Point(xend, ypos)))
349 if (node.element() instanceof SequenceI)
351 SequenceI seq = (SequenceI) node.element();
352 SequenceGroup sg = av.getSelectionGroup();
355 sg.addOrRemove(seq, true);
362 pickNode(pickBox, (SequenceNode) node.left(), chunk, scale, width,
364 pickNode(pickBox, (SequenceNode) node.right(), chunk, scale, width,
369 public void setColor(SequenceNode node, Color c)
376 if (node.left() == null && node.right() == null)
380 if (node.element() instanceof SequenceI)
382 av.setSequenceColour((SequenceI) node.element(), c);
388 setColor((SequenceNode) node.left(), c);
389 setColor((SequenceNode) node.right(), c);
394 public void update(Graphics g)
400 public void paint(Graphics g)
407 if (nameHash.size() == 0)
412 int width = scrollPane.getSize().width;
413 int height = scrollPane.getSize().height;
416 height = g.getFontMetrics(font).getHeight() * nameHash.size();
419 if (getSize().width > width)
421 setSize(new Dimension(width, height));
422 scrollPane.validate();
426 setSize(new Dimension(width, height));
429 draw(g, width, height);
433 public void draw(Graphics g, int width, int height)
435 offy = font.getSize() + 10;
437 g.setColor(Color.white);
438 g.fillRect(0, 0, width, height);
440 labelLength = g.getFontMetrics(font).stringWidth(longestName) + 20; // 20
445 float wscale = (width - labelLength - offx * 2) / tree.getMaxHeight();
447 SequenceNode top = tree.getTopNode();
451 top.count = ((SequenceNode) top.left()).count
452 + ((SequenceNode) top.right()).count;
454 float chunk = (float) (height - offy) / top.count;
456 drawNode(g, tree.getTopNode(), chunk, wscale, width, offx, offy);
460 if (av.getCurrentTree() == tree)
462 g.setColor(Color.red);
466 g.setColor(Color.gray);
469 int x = (int) (threshold * (getSize().width - labelLength - 2 * offx) + offx);
471 g.drawLine(x, 0, x, getSize().height);
477 public void mouseReleased(MouseEvent e)
482 public void mouseEntered(MouseEvent e)
487 public void mouseExited(MouseEvent e)
492 public void mouseClicked(MouseEvent evt)
494 if (highlightNode != null)
496 if (evt.getClickCount() > 1)
498 tree.swapNodes(highlightNode);
499 tree.reCount(tree.getTopNode());
500 tree.findHeight(tree.getTopNode());
504 Vector leaves = new Vector();
505 tree.findLeaves(highlightNode, leaves);
507 for (int i = 0; i < leaves.size(); i++)
509 SequenceI seq = (SequenceI) ((SequenceNode) leaves.elementAt(i))
511 treeSelectionChanged(seq);
515 PaintRefresher.Refresh(this, av.getSequenceSetId());
522 public void mouseDragged(MouseEvent ect)
527 public void mouseMoved(MouseEvent evt)
529 av.setCurrentTree(tree);
531 Object ob = findElement(evt.getX(), evt.getY());
533 if (ob instanceof SequenceNode)
535 highlightNode = (SequenceNode) ob;
540 if (highlightNode != null)
542 highlightNode = null;
549 public void mousePressed(MouseEvent e)
551 av.setCurrentTree(tree);
556 Object ob = findElement(x, y);
558 if (ob instanceof SequenceI)
560 treeSelectionChanged((Sequence) ob);
561 PaintRefresher.Refresh(this, av.getSequenceSetId());
566 else if (!(ob instanceof SequenceNode))
570 if (tree.getMaxHeight() != 0)
572 threshold = (float) (x - offx)
573 / (float) (getSize().width - labelLength - 2 * offx);
575 tree.getGroups().removeAllElements();
576 tree.groupNodes(tree.getTopNode(), threshold);
577 setColor(tree.getTopNode(), Color.black);
579 av.setSelectionGroup(null);
580 av.getAlignment().deleteAllGroups();
581 av.sequenceColours = null;
588 PaintRefresher.Refresh(this, av.getSequenceSetId());
595 for (int i = 0; i < tree.getGroups().size(); i++)
598 Color col = new Color((int) (Math.random() * 255),
599 (int) (Math.random() * 255), (int) (Math.random() * 255));
600 setColor((SequenceNode) tree.getGroups().elementAt(i), col.brighter());
602 Vector l = tree.findLeaves(
603 (SequenceNode) tree.getGroups().elementAt(i), new Vector());
605 Vector sequences = new Vector();
606 for (int j = 0; j < l.size(); j++)
608 SequenceI s1 = (SequenceI) ((SequenceNode) l.elementAt(j))
610 if (!sequences.contains(s1))
612 sequences.addElement(s1);
616 ColourSchemeI cs = null;
618 SequenceGroup sg = new SequenceGroup(sequences, "", cs, true, true,
619 false, 0, av.getAlignment().getWidth() - 1);
621 if (av.getGlobalColourScheme() != null)
623 if (av.getGlobalColourScheme() instanceof UserColourScheme)
625 cs = new UserColourScheme(
626 ((UserColourScheme) av.getGlobalColourScheme())
632 cs = ColourSchemeProperty.getColour(sg, ColourSchemeProperty
633 .getColourName(av.getGlobalColourScheme()));
635 // cs is null if shading is an annotationColourGradient
638 cs.setThreshold(av.getGlobalColourScheme().getThreshold(),
639 av.getIgnoreGapsConsensus());
642 // TODO: cs used to be initialized with a sequence collection and
643 // recalcConservation called automatically
644 // instead we set it manually - recalc called after updateAnnotation
647 sg.setName("JTreeGroup:" + sg.hashCode());
649 if (av.getGlobalColourScheme() != null
650 && av.getGlobalColourScheme().conservationApplied())
652 Conservation c = new Conservation("Group",
653 ResidueProperties.propHash, 3, sg.getSequences(null),
654 sg.getStartRes(), sg.getEndRes());
657 c.verdict(false, av.getConsPercGaps());
658 cs.setConservation(c);
664 av.getAlignment().addGroup(sg);
667 ap.updateAnnotation();
671 public void setShowDistances(boolean state)
673 this.showDistances = state;
677 public void setShowBootstrap(boolean state)
679 this.showBootstrap = state;
683 public void setMarkPlaceholders(boolean state)
685 this.markPlaceholders = state;