2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
3 * Copyright (C) 2008 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
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
32 extends Panel implements MouseListener, MouseMotionListener
35 ScrollPane scrollPane;
37 public static final String PLACEHOLDER = " * ";
39 boolean fitToWindow = true;
40 boolean showDistances = false;
41 boolean showBootstrap = false;
42 boolean markPlaceholders = false;
52 Hashtable nameHash = new Hashtable();
53 Hashtable nodeHash = new Hashtable();
55 SequenceNode highlightNode;
57 public TreeCanvas(AlignViewport av, ScrollPane scroller)
61 scrollPane = scroller;
62 addMouseListener(this);
63 addMouseMotionListener(this);
66 PaintRefresher.Register(this, av.getSequenceSetId());
69 public void treeSelectionChanged(SequenceI sequence)
71 SequenceGroup selected = av.getSelectionGroup();
74 selected = new SequenceGroup();
75 av.setSelectionGroup(selected);
78 selected.setEndRes(av.alignment.getWidth() - 1);
79 selected.addOrRemove(sequence, true);
82 public void setTree(NJTree tree)
85 tree.findHeight(tree.getTopNode());
87 // Now have to calculate longest name based on the leaves
88 Vector leaves = tree.findLeaves(tree.getTopNode(), new Vector());
89 boolean has_placeholders = false;
92 for (int i = 0; i < leaves.size(); i++)
94 SequenceNode lf = (SequenceNode) leaves.elementAt(i);
96 if (lf.isPlaceholder())
98 has_placeholders = true;
101 if (longestName.length() < ( (Sequence) lf.element()).getName()
104 longestName = TreeCanvas.PLACEHOLDER +
105 ( (Sequence) lf.element()).getName();
109 setMarkPlaceholders(has_placeholders);
112 public void drawNode(Graphics g, SequenceNode node, float chunk, float scale,
113 int width, int offx, int offy)
120 if (node.left() == null && node.right() == null)
124 float height = node.height;
125 float dist = node.dist;
127 int xstart = (int) ( (height - dist) * scale) + offx;
128 int xend = (int) (height * scale) + offx;
130 int ypos = (int) (node.ycount * chunk) + offy;
132 if (node.element() instanceof SequenceI)
134 SequenceI seq = (SequenceI) ( (SequenceNode) node).element();
136 if (av.getSequenceColour(seq) == Color.white)
138 g.setColor(Color.black);
142 g.setColor(av.getSequenceColour(seq).darker());
148 g.setColor(Color.black);
151 // Draw horizontal line
152 g.drawLine(xstart, ypos, xend, ypos);
154 String nodeLabel = "";
155 if (showDistances && node.dist > 0)
157 nodeLabel = new Format("%-.2f").form(node.dist);
161 int btstrap = node.getBootstrap();
166 nodeLabel = nodeLabel + " : ";
168 nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());
171 if (!nodeLabel.equals(""))
173 g.drawString(nodeLabel, xstart + 2, ypos - 2);
176 String name = (markPlaceholders && node.isPlaceholder()) ?
177 (PLACEHOLDER + node.getName()) : node.getName();
178 FontMetrics fm = g.getFontMetrics(font);
179 int charWidth = fm.stringWidth(name) + 3;
180 int charHeight = fm.getHeight();
182 Rectangle rect = new Rectangle(xend + 10, ypos - charHeight,
183 charWidth, charHeight);
185 nameHash.put( (SequenceI) node.element(), rect);
187 // Colour selected leaves differently
188 SequenceGroup selected = av.getSelectionGroup();
189 if (selected != null &&
190 selected.getSequences(null).contains( (SequenceI) node.element()))
192 g.setColor(Color.gray);
194 g.fillRect(xend + 10, ypos - charHeight + 3, charWidth, charHeight);
195 g.setColor(Color.white);
197 g.drawString(name, xend + 10, ypos);
198 g.setColor(Color.black);
202 drawNode(g, (SequenceNode) node.left(), chunk, scale, width, offx, offy);
203 drawNode(g, (SequenceNode) node.right(), chunk, scale, width, offx, offy);
205 float height = node.height;
206 float dist = node.dist;
208 int xstart = (int) ( (height - dist) * scale) + offx;
209 int xend = (int) (height * scale) + offx;
210 int ypos = (int) (node.ycount * chunk) + offy;
212 g.setColor( ( (SequenceNode) node).color.darker());
214 // Draw horizontal line
215 g.drawLine(xstart, ypos, xend, ypos);
216 if (node == highlightNode)
218 g.fillRect(xend - 3, ypos - 3, 6, 6);
222 g.fillRect(xend - 2, ypos - 2, 4, 4);
225 int ystart = (int) ( ( (SequenceNode) node.left()).ycount * chunk) + offy;
226 int yend = (int) ( ( (SequenceNode) node.right()).ycount * chunk) + offy;
228 Rectangle pos = new Rectangle(xend - 2, ypos - 2, 5, 5);
229 nodeHash.put(node, pos);
231 g.drawLine( (int) (height * scale) + offx, ystart,
232 (int) (height * scale) + offx, yend);
234 String nodeLabel = "";
236 if (showDistances && (node.dist > 0))
238 nodeLabel = new Format("%-.2f").form(node.dist);
243 int btstrap = node.getBootstrap();
248 nodeLabel = nodeLabel + " : ";
250 nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());
254 if (!nodeLabel.equals(""))
256 g.drawString(nodeLabel, xstart + 2, ypos - 2);
262 public Object findElement(int x, int y)
264 Enumeration keys = nameHash.keys();
266 while (keys.hasMoreElements())
268 Object ob = keys.nextElement();
269 Rectangle rect = (Rectangle) nameHash.get(ob);
271 if (x >= rect.x && x <= (rect.x + rect.width) &&
272 y >= rect.y && y <= (rect.y + rect.height))
277 keys = nodeHash.keys();
279 while (keys.hasMoreElements())
281 Object ob = keys.nextElement();
282 Rectangle rect = (Rectangle) nodeHash.get(ob);
284 if (x >= rect.x && x <= (rect.x + rect.width) &&
285 y >= rect.y && y <= (rect.y + rect.height))
294 public void pickNodes(Rectangle pickBox)
296 int width = getSize().width;
297 int height = getSize().height;
299 SequenceNode top = tree.getTopNode();
301 float wscale = (float) (width * .8 - offx * 2) / tree.getMaxHeight()
305 top.count = ( (SequenceNode) top.left()).count +
306 ( (SequenceNode) top.right()).count;
308 float chunk = (float) (height - offy) / top.count;
310 pickNode(pickBox, top, chunk, wscale, width, offx, offy);
313 public void pickNode(Rectangle pickBox, SequenceNode node, float chunk,
314 float scale, int width, int offx, int offy)
321 if (node.left() == null && node.right() == null)
323 float height = node.height;
324 //float dist = node.dist;
326 //int xstart = (int) ( (height - dist) * scale) + offx;
327 int xend = (int) (height * scale) + offx;
329 int ypos = (int) (node.ycount * chunk) + offy;
331 if (pickBox.contains(new Point(xend, ypos)))
333 if (node.element() instanceof SequenceI)
335 SequenceI seq = (SequenceI) node.element();
336 SequenceGroup sg = av.getSelectionGroup();
339 sg.addOrRemove(seq, true);
346 pickNode(pickBox, (SequenceNode) node.left(), chunk, scale, width, offx,
348 pickNode(pickBox, (SequenceNode) node.right(), chunk, scale, width, offx,
353 public void setColor(SequenceNode node, Color c)
360 if (node.left() == null && node.right() == null)
364 if (node.element() instanceof SequenceI)
366 av.setSequenceColour( (SequenceI) node.element(), c);
372 setColor( (SequenceNode) node.left(), c);
373 setColor( (SequenceNode) node.right(), c);
377 public void update(Graphics g)
382 public void paint(Graphics g)
389 if (nameHash.size() == 0)
394 int width = scrollPane.getSize().width;
395 int height = scrollPane.getSize().height;
398 height = g.getFontMetrics(font).getHeight() * nameHash.size();
401 if (getSize().width > width)
403 setSize(new Dimension(width, height));
404 scrollPane.validate();
408 setSize(new Dimension(width, height));
412 draw(g, width, height);
416 public void draw(Graphics g, int width, int height)
418 offy = font.getSize() + 10;
420 g.setColor(Color.white);
421 g.fillRect(0, 0, width, height);
423 labelLength = g.getFontMetrics(font).stringWidth(longestName) + 20; //20 allows for scrollbar
425 float wscale = (float) (width - labelLength - offx * 2) / tree.getMaxHeight();
427 SequenceNode top = tree.getTopNode();
431 top.count = ( (SequenceNode) top.left()).count +
432 ( (SequenceNode) top.right()).count;
434 float chunk = (float) (height - offy) / top.count;
436 drawNode(g, tree.getTopNode(), chunk, wscale, width, offx, offy);
440 if (av.getCurrentTree() == tree)
442 g.setColor(Color.red);
446 g.setColor(Color.gray);
449 int x = (int) (threshold *
450 (float) (getSize().width - labelLength - 2 * offx) + offx);
452 g.drawLine(x, 0, x, getSize().height);
457 public void mouseReleased(MouseEvent e)
460 public void mouseEntered(MouseEvent e)
463 public void mouseExited(MouseEvent e)
466 public void mouseClicked(MouseEvent evt)
468 if (highlightNode != null)
470 if (evt.getClickCount() > 1)
472 tree.swapNodes(highlightNode);
473 tree.reCount(tree.getTopNode());
474 tree.findHeight(tree.getTopNode());
478 Vector leaves = new Vector();
479 tree.findLeaves(highlightNode, leaves);
481 for (int i = 0; i < leaves.size(); i++)
484 (SequenceI) ( (SequenceNode) leaves.elementAt(i)).element();
485 treeSelectionChanged(seq);
489 PaintRefresher.Refresh(this, av.getSequenceSetId());
494 public void mouseDragged(MouseEvent ect)
497 public void mouseMoved(MouseEvent evt)
499 av.setCurrentTree(tree);
501 Object ob = findElement(evt.getX(), evt.getY());
503 if (ob instanceof SequenceNode)
505 highlightNode = (SequenceNode) ob;
510 if (highlightNode != null)
512 highlightNode = null;
518 public void mousePressed(MouseEvent e)
520 av.setCurrentTree(tree);
525 Object ob = findElement(x, y);
527 if (ob instanceof SequenceI)
529 treeSelectionChanged( (Sequence) ob);
530 PaintRefresher.Refresh(this, av.getSequenceSetId());
534 else if (! (ob instanceof SequenceNode))
538 if (tree.getMaxHeight() != 0)
540 threshold = (float) (x - offx) /
541 (float) (getSize().width - labelLength - 2 * offx);
543 tree.getGroups().removeAllElements();
544 tree.groupNodes(tree.getTopNode(), threshold);
545 setColor(tree.getTopNode(), Color.black);
547 av.setSelectionGroup(null);
548 av.alignment.deleteAllGroups();
549 av.sequenceColours = null;
556 PaintRefresher.Refresh(this, av.getSequenceSetId());
563 for (int i = 0; i < tree.getGroups().size(); i++)
566 Color col = new Color( (int) (Math.random() * 255),
567 (int) (Math.random() * 255),
568 (int) (Math.random() * 255));
569 setColor( (SequenceNode) tree.getGroups().elementAt(i), col.brighter());
571 Vector l = tree.findLeaves( (SequenceNode) tree.getGroups().elementAt(
574 Vector sequences = new Vector();
575 for (int j = 0; j < l.size(); j++)
577 SequenceI s1 = (SequenceI) ( (SequenceNode) l.elementAt(j)).element();
578 if (!sequences.contains(s1))
580 sequences.addElement(s1);
584 ColourSchemeI cs = null;
586 if (av.getGlobalColourScheme() != null)
588 if (av.getGlobalColourScheme() instanceof UserColourScheme)
590 cs = new UserColourScheme(
591 ( (UserColourScheme) av.getGlobalColourScheme()).getColours());
596 cs = ColourSchemeProperty.getColour(sequences,
597 av.alignment.getWidth(),
598 ColourSchemeProperty.
600 av.getGlobalColourScheme()));
603 cs.setThreshold(av.getGlobalColourScheme().getThreshold(),
604 av.getIgnoreGapsConsensus());
607 SequenceGroup sg = new SequenceGroup(sequences, "",
610 av.alignment.getWidth() - 1);
612 sg.setName("JTreeGroup:" + sg.hashCode());
614 if (av.getGlobalColourScheme() != null
615 && av.getGlobalColourScheme().conservationApplied())
617 Conservation c = new Conservation("Group",
618 ResidueProperties.propHash, 3,
619 sg.getSequences(null),
624 c.verdict(false, av.ConsPercGaps);
625 cs.setConservation(c);
631 av.alignment.addGroup(sg);
637 public void setShowDistances(boolean state)
639 this.showDistances = state;
643 public void setShowBootstrap(boolean state)
645 this.showBootstrap = state;
649 public void setMarkPlaceholders(boolean state)
651 this.markPlaceholders = state;