2 * Jalview - A Sequence Alignment Editor and Viewer
\r
3 * Copyright (C) 2005 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
\r
5 * This program is free software; you can redistribute it and/or
\r
6 * modify it under the terms of the GNU General Public License
\r
7 * as published by the Free Software Foundation; either version 2
\r
8 * of the License, or (at your option) any later version.
\r
10 * This program is distributed in the hope that it will be useful,
\r
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 * GNU General Public License for more details.
\r
15 * You should have received a copy of the GNU General Public License
\r
16 * along with this program; if not, write to the Free Software
\r
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
\r
20 package jalview.appletgui;
\r
25 import java.awt.event.*;
\r
27 import jalview.analysis.*;
\r
28 import jalview.datamodel.*;
\r
29 import jalview.schemes.*;
\r
30 import jalview.util.*;
\r
32 public class TreeCanvas
\r
33 extends Panel implements MouseListener
\r
36 ScrollPane scrollPane;
\r
38 public static final String PLACEHOLDER = " * ";
\r
42 boolean fitToWindow = true;
\r
43 boolean showDistances = false;
\r
44 boolean showBootstrap = false;
\r
45 boolean markPlaceholders = false;
\r
53 int labelLength = -1;
\r
55 //RubberbandRectangle rubberband;
\r
59 Hashtable nameHash = new Hashtable();
\r
60 Hashtable nodeHash = new Hashtable();
\r
62 public TreeCanvas(AlignViewport av, NJTree tree, ScrollPane scroller,
\r
67 scrollPane = scroller;
\r
68 addMouseListener(this);
\r
69 tree.findHeight(tree.getTopNode());
\r
70 longestName = label;
\r
73 PaintRefresher.Register(this, av.alignment);
\r
76 public void TreeSelectionChanged(Sequence sequence)
\r
78 SequenceGroup selected = av.getSelectionGroup();
\r
79 if (selected == null)
\r
81 selected = new SequenceGroup();
\r
82 av.setSelectionGroup(selected);
\r
85 selected.setEndRes(av.alignment.getWidth()-1);
\r
86 selected.addOrRemove(sequence, true);
\r
88 PaintRefresher.Refresh(this, av.alignment);
\r
92 public void setTree(NJTree tree)
\r
95 tree.findHeight(tree.getTopNode());
\r
98 public void drawNode(Graphics g, SequenceNode node, float chunk, float scale,
\r
99 int width, int offx, int offy)
\r
106 if (node.left() == null && node.right() == null)
\r
108 // Drawing leaf node
\r
110 float height = node.height;
\r
111 float dist = node.dist;
\r
113 int xstart = (int) ( (height - dist) * scale) + offx;
\r
114 int xend = (int) (height * scale) + offx;
\r
116 int ypos = (int) (node.ycount * chunk) + offy;
\r
118 if (node.element() instanceof SequenceI)
\r
120 if ( ( (SequenceI) ( (SequenceNode) node).element()).getColor() ==
\r
123 g.setColor(Color.black);
\r
127 g.setColor( ( (SequenceI) ( (SequenceNode) node).element()).getColor().
\r
134 g.setColor(Color.black);
\r
137 // Draw horizontal line
\r
138 g.drawLine(xstart, ypos, xend, ypos);
\r
140 String nodeLabel = "";
\r
141 if (showDistances && node.dist > 0)
\r
143 nodeLabel = new Format("%5.2f").form(node.dist);
\r
149 nodeLabel = nodeLabel + " : ";
\r
151 nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());
\r
153 if (!nodeLabel.equals(""))
\r
155 g.drawString(nodeLabel, xstart, ypos - 10);
\r
158 String name = (markPlaceholders && node.isPlaceholder()) ?
\r
159 (PLACEHOLDER + node.getName()) : node.getName();
\r
160 FontMetrics fm = g.getFontMetrics(font);
\r
161 int charWidth = fm.stringWidth(name) + 3;
\r
162 int charHeight = fm.getHeight();
\r
164 Rectangle rect = new Rectangle(xend + 20, ypos - charHeight,
\r
165 charWidth, charHeight);
\r
167 nameHash.put( (SequenceI) node.element(), rect);
\r
169 // Colour selected leaves differently
\r
170 SequenceGroup selected = av.getSelectionGroup();
\r
171 if (selected != null &&
\r
172 selected.sequences.contains( (SequenceI) node.element()))
\r
174 g.setColor(Color.gray);
\r
176 g.fillRect(xend + 10, ypos - charHeight + 3, charWidth, charHeight);
\r
177 g.setColor(Color.white);
\r
179 g.drawString(name, xend + 10, ypos);
\r
180 g.setColor(Color.black);
\r
184 drawNode(g, (SequenceNode) node.left(), chunk, scale, width, offx, offy);
\r
185 drawNode(g, (SequenceNode) node.right(), chunk, scale, width, offx, offy);
\r
187 float height = node.height;
\r
188 float dist = node.dist;
\r
190 int xstart = (int) ( (height - dist) * scale) + offx;
\r
191 int xend = (int) (height * scale) + offx;
\r
192 int ypos = (int) (node.ycount * chunk) + offy;
\r
194 g.setColor( ( (SequenceNode) node).color.darker());
\r
196 // Draw horizontal line
\r
197 g.drawLine(xstart, ypos, xend, ypos);
\r
198 g.fillRect(xend - 2, ypos - 2, 4, 4);
\r
200 int ystart = (int) ( ( (SequenceNode) node.left()).ycount * chunk) + offy;
\r
201 int yend = (int) ( ( (SequenceNode) node.right()).ycount * chunk) + offy;
\r
203 Rectangle pos = new Rectangle(xend - 2, ypos - 2, 5, 5);
\r
204 nodeHash.put(node, pos);
\r
206 g.drawLine( (int) (height * scale) + offx, ystart,
\r
207 (int) (height * scale) + offx, yend);
\r
209 if (showDistances && node.dist > 0)
\r
211 g.drawString(new Format("%5.2f").form(node.dist), xstart, ypos - 5);
\r
217 public Object findElement(int x, int y)
\r
219 Enumeration keys = nameHash.keys();
\r
221 while (keys.hasMoreElements())
\r
223 Object ob = keys.nextElement();
\r
224 Rectangle rect = (Rectangle) nameHash.get(ob);
\r
226 if (x >= rect.x && x <= (rect.x + rect.width) &&
\r
227 y >= rect.y && y <= (rect.y + rect.height))
\r
232 keys = nodeHash.keys();
\r
234 while (keys.hasMoreElements())
\r
236 Object ob = keys.nextElement();
\r
237 Rectangle rect = (Rectangle) nodeHash.get(ob);
\r
239 if (x >= rect.x && x <= (rect.x + rect.width) &&
\r
240 y >= rect.y && y <= (rect.y + rect.height))
\r
249 public void pickNodes(Rectangle pickBox)
\r
251 int width = getSize().width;
\r
252 int height = getSize().height;
\r
254 SequenceNode top = tree.getTopNode();
\r
256 float wscale = (float) (width * .8 - offx * 2) / tree.getMaxHeight()
\r
258 if (top.count == 0)
\r
260 top.count = ( (SequenceNode) top.left()).count +
\r
261 ( (SequenceNode) top.right()).count;
\r
263 float chunk = (float) (height - offy * 2) / top.count;
\r
265 pickNode(pickBox, top, chunk, wscale, width, offx, offy);
\r
268 public void pickNode(Rectangle pickBox, SequenceNode node, float chunk,
\r
269 float scale, int width, int offx, int offy)
\r
276 if (node.left() == null && node.right() == null)
\r
278 float height = node.height;
\r
279 float dist = node.dist;
\r
281 int xstart = (int) ( (height - dist) * scale) + offx;
\r
282 int xend = (int) (height * scale) + offx;
\r
284 int ypos = (int) (node.ycount * chunk) + offy;
\r
286 if (pickBox.contains(new Point(xend, ypos)))
\r
288 if (node.element() instanceof SequenceI)
\r
290 SequenceI seq = (SequenceI) node.element();
\r
291 SequenceGroup sg = av.getSelectionGroup();
\r
294 sg.addOrRemove(seq, true);
\r
301 pickNode(pickBox, (SequenceNode) node.left(), chunk, scale, width, offx,
\r
303 pickNode(pickBox, (SequenceNode) node.right(), chunk, scale, width, offx,
\r
308 public void setColor(SequenceNode node, Color c)
\r
315 if (node.left() == null && node.right() == null)
\r
319 if (node.element() instanceof SequenceI)
\r
321 ( (SequenceI) node.element()).setColor(c);
\r
327 setColor( (SequenceNode) node.left(), c);
\r
328 setColor( (SequenceNode) node.right(), c);
\r
332 public void paint(Graphics g)
\r
335 font = new Font("Verdana", Font.PLAIN, fontSize);
\r
338 FontMetrics fm = g.getFontMetrics(font);
\r
340 if (nameHash.size() == 0)
\r
347 scrollPane.getSize().height > fm.getHeight() * nameHash.size() + offy))
\r
349 draw(g, scrollPane.getSize().width, scrollPane.getSize().height);
\r
353 setSize(new Dimension(scrollPane.getSize().width,
\r
354 fm.getHeight() * nameHash.size()));
\r
355 draw(g, scrollPane.getSize().width, fm.getHeight() * nameHash.size());
\r
358 scrollPane.validate();
\r
361 public int getFontSize()
\r
366 public void setFontSize(int fontSize)
\r
368 this.fontSize = fontSize;
\r
372 public void draw(Graphics g, int width, int height)
\r
375 g.setColor(Color.white);
\r
376 g.fillRect(0, 0, width, height);
\r
378 labelLength = g.getFontMetrics(font).stringWidth(longestName) + 20; //20 allows for scrollbar
\r
380 float wscale = (float) (width - labelLength - offx * 2) / tree.getMaxHeight();
\r
382 SequenceNode top = tree.getTopNode();
\r
384 if (top.count == 0)
\r
386 top.count = ( (SequenceNode) top.left()).count +
\r
387 ( (SequenceNode) top.right()).count;
\r
389 float chunk = (float) (height - offy * 2) / top.count;
\r
391 drawNode(g, tree.getTopNode(), chunk, wscale, width, offx, offy);
\r
393 if (threshold != 0)
\r
395 if (av.getCurrentTree() == tree)
\r
397 g.setColor(Color.red);
\r
401 g.setColor(Color.gray);
\r
404 int x = (int) (threshold *
\r
405 (float) (getSize().width - labelLength - 2 * offx) + offx);
\r
407 g.drawLine(x, 0, x, getSize().height);
\r
412 public void mouseReleased(MouseEvent e)
\r
415 public void mouseEntered(MouseEvent e)
\r
418 public void mouseExited(MouseEvent e)
\r
421 public void mouseClicked(MouseEvent e)
\r
425 public void mousePressed(MouseEvent e)
\r
427 av.setCurrentTree(tree);
\r
432 Object ob = findElement(x, y);
\r
434 if (ob instanceof SequenceI)
\r
436 TreeSelectionChanged( (Sequence) ob);
\r
441 else if (ob instanceof SequenceNode)
\r
443 SequenceNode tmpnode = (SequenceNode) ob;
\r
444 tree.swapNodes(tmpnode);
\r
445 tree.reCount(tree.getTopNode());
\r
446 tree.findHeight(tree.getTopNode());
\r
452 if (tree.getMaxHeight() != 0)
\r
454 threshold = (float) (x - offx) /
\r
455 (float) (getSize().width - labelLength - 2 * offx);
\r
457 tree.getGroups().removeAllElements();
\r
458 tree.groupNodes(tree.getTopNode(), threshold);
\r
459 setColor(tree.getTopNode(), Color.black);
\r
461 av.setSelectionGroup(null);
\r
462 av.alignment.deleteAllGroups();
\r
464 for (int i = 0; i < tree.getGroups().size(); i++)
\r
467 Color col = new Color( (int) (Math.random() * 255),
\r
468 (int) (Math.random() * 255),
\r
469 (int) (Math.random() * 255));
\r
470 setColor( (SequenceNode) tree.getGroups().elementAt(i), col.brighter());
\r
472 Vector l = tree.findLeaves( (SequenceNode) tree.getGroups().elementAt(
\r
475 Vector sequences = new Vector();
\r
476 for (int j = 0; j < l.size(); j++)
\r
478 SequenceI s1 = (SequenceI) ( (SequenceNode) l.elementAt(j)).element();
\r
479 if(!sequences.contains(s1))
\r
480 sequences.addElement(s1);
\r
483 ColourSchemeI cs = ColourSchemeProperty.getColour(sequences, av.alignment.getWidth(),
\r
484 ColourSchemeProperty.getColourName(av.getGlobalColourScheme()));
\r
487 SequenceGroup sg = new SequenceGroup(sequences, "TreeGroup",
\r
489 false, 0, av.alignment.getWidth()-1);
\r
492 if ( av.getGlobalColourScheme()!=null
\r
493 && av.getGlobalColourScheme().conservationApplied())
\r
495 Conservation c = new Conservation("Group",
\r
496 ResidueProperties.propHash, 3,
\r
497 sg.sequences, sg.getStartRes(),
\r
501 c.verdict(false, av.ConsPercGaps);
\r
502 cs.setConservation(c);
\r
508 av.alignment.addGroup(sg);
\r
514 PaintRefresher.Refresh(this, av.alignment);
\r
519 public void setShowDistances(boolean state)
\r
521 this.showDistances = state;
\r
525 public void setShowBootstrap(boolean state)
\r
527 this.showBootstrap = state;
\r
531 public void setMarkPlaceholders(boolean state)
\r
533 this.markPlaceholders = state;
\r