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
23 import jalview.analysis.*;
\r
24 import jalview.datamodel.*;
\r
25 import jalview.util.*;
\r
26 import jalview.schemes.*;
\r
28 import java.awt.event.*;
\r
32 public class TreeCanvas extends Panel implements MouseListener
\r
35 ScrollPane scrollPane;
\r
37 public static final String PLACEHOLDER=" * ";
\r
41 boolean fitToWindow = true;
\r
42 boolean showDistances = false;
\r
43 boolean showBootstrap = false;
\r
44 boolean markPlaceholders = false;
\r
54 //RubberbandRectangle rubberband;
\r
58 Hashtable nameHash = new Hashtable();
\r
59 Hashtable nodeHash = new Hashtable();
\r
61 public TreeCanvas(AlignViewport av, NJTree tree, ScrollPane scroller, String label)
\r
65 scrollPane = scroller;
\r
66 addMouseListener(this);
\r
67 tree.findHeight(tree.getTopNode());
\r
68 longestName = label;
\r
71 PaintRefresher.Register(this);
\r
73 public void TreeSelectionChanged(Sequence sequence)
\r
75 SequenceGroup selected = av.getSelectionGroup();
\r
76 if(selected == null)
\r
78 selected = new SequenceGroup();
\r
79 av.setSelectionGroup(selected);
\r
82 selected.setEndRes(av.alignment.getWidth());
\r
83 selected.addOrRemove(sequence);
\r
86 PaintRefresher.Refresh(this);
\r
92 public void setTree(NJTree tree) {
\r
94 tree.findHeight(tree.getTopNode());
\r
97 public void drawNode(Graphics g,SequenceNode node, float chunk, float scale, int width,int offx, int offy) {
\r
102 if (node.left() == null && node.right() == null) {
\r
103 // Drawing leaf node
\r
105 float height = node.height;
\r
106 float dist = node.dist;
\r
108 int xstart = (int)((height-dist)*scale) + offx;
\r
109 int xend = (int)(height*scale) + offx;
\r
111 int ypos = (int)(node.ycount * chunk) + offy;
\r
113 if (node.element() instanceof SequenceI)
\r
115 if ( ( (SequenceI) ( (SequenceNode) node).element()).getColor() == Color.white)
\r
117 g.setColor(Color.black);
\r
120 g.setColor( ( (SequenceI) ( (SequenceNode) node).element()).getColor().
\r
125 g.setColor(Color.black);
\r
128 // Draw horizontal line
\r
129 g.drawLine(xstart,ypos,xend,ypos);
\r
131 String nodeLabel = "";
\r
132 if (showDistances && node.dist > 0) {
\r
133 nodeLabel = new Format("%5.2f").form(node.dist);
\r
135 if (showBootstrap) {
\r
136 if (showDistances) {
\r
137 nodeLabel = nodeLabel + " : ";
\r
139 nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());
\r
141 if (! nodeLabel.equals("")) {
\r
142 g.drawString(nodeLabel,xstart,ypos - 10);
\r
145 String name = (markPlaceholders && node.isPlaceholder()) ? (PLACEHOLDER+node.getName()) : node.getName();
\r
146 FontMetrics fm = g.getFontMetrics(font);
\r
147 int charWidth = fm.stringWidth(name) + 3;
\r
148 int charHeight = fm.getHeight();
\r
150 Rectangle rect = new Rectangle(xend+20,ypos-charHeight,
\r
151 charWidth,charHeight);
\r
153 nameHash.put((SequenceI)node.element(),rect);
\r
155 // Colour selected leaves differently
\r
156 SequenceGroup selected = av.getSelectionGroup();
\r
157 if (selected!=null && selected.sequences.contains((SequenceI)node.element())) {
\r
158 g.setColor(Color.gray);
\r
160 g.fillRect(xend + 10, ypos - charHeight + 3,charWidth,charHeight);
\r
161 g.setColor(Color.white);
\r
163 g.drawString(name,xend+10,ypos);
\r
164 g.setColor(Color.black);
\r
166 drawNode(g,(SequenceNode)node.left(), chunk,scale,width,offx,offy);
\r
167 drawNode(g,(SequenceNode)node.right(),chunk,scale,width,offx,offy);
\r
169 float height = node.height;
\r
170 float dist = node.dist;
\r
172 int xstart = (int)((height-dist)*scale) + offx;
\r
173 int xend = (int)(height *scale) + offx;
\r
174 int ypos = (int)(node.ycount *chunk) + offy;
\r
176 g.setColor(((SequenceNode)node).color.darker());
\r
178 // Draw horizontal line
\r
179 g.drawLine(xstart,ypos,xend,ypos);
\r
180 g.fillRect(xend-2, ypos-2, 4,4);
\r
182 int ystart = (int)(((SequenceNode)node.left()) .ycount * chunk) + offy;
\r
183 int yend = (int)(((SequenceNode)node.right()).ycount * chunk) + offy;
\r
185 Rectangle pos = new Rectangle(xend-2,ypos-2,5,5);
\r
186 nodeHash.put(node,pos);
\r
188 g.drawLine((int)(height*scale) + offx, ystart,
\r
189 (int)(height*scale) + offx, yend);
\r
191 if (showDistances && node.dist > 0) {
\r
192 g.drawString(new Format("%5.2f").form(node.dist),xstart,ypos - 5);
\r
197 public Object findElement(int x, int y) {
\r
198 Enumeration keys = nameHash.keys();
\r
200 while (keys.hasMoreElements()) {
\r
201 Object ob = keys.nextElement();
\r
202 Rectangle rect = (Rectangle)nameHash.get(ob);
\r
204 if (x >= rect.x && x <= (rect.x + rect.width) &&
\r
205 y >= rect.y && y <= (rect.y + rect.height)) {
\r
209 keys = nodeHash.keys();
\r
211 while (keys.hasMoreElements()) {
\r
212 Object ob = keys.nextElement();
\r
213 Rectangle rect = (Rectangle)nodeHash.get(ob);
\r
215 if (x >= rect.x && x <= (rect.x + rect.width) &&
\r
216 y >= rect.y && y <= (rect.y + rect.height)) {
\r
224 public void pickNodes(Rectangle pickBox) {
\r
225 int width = getSize().width;
\r
226 int height = getSize().height;
\r
228 SequenceNode top = tree.getTopNode();
\r
230 float wscale = (float)(width*.8-offx*2)/tree.getMaxHeight()
\r
232 if (top.count == 0) {
\r
233 top.count = ((SequenceNode)top.left()).count + ((SequenceNode)top.right()).count ;
\r
235 float chunk = (float)(height-offy*2)/top.count;
\r
237 pickNode(pickBox,top,chunk,wscale,width,offx,offy);
\r
240 public void pickNode(Rectangle pickBox, SequenceNode node, float chunk, float scale, int width,int offx, int offy) {
\r
241 if (node == null) {
\r
245 if (node.left() == null && node.right() == null) {
\r
246 float height = node.height;
\r
247 float dist = node.dist;
\r
249 int xstart = (int)((height-dist)*scale) + offx;
\r
250 int xend = (int)(height*scale) + offx;
\r
252 int ypos = (int)(node.ycount * chunk) + offy;
\r
254 if (pickBox.contains(new Point(xend,ypos))) {
\r
255 if (node.element() instanceof SequenceI) {
\r
256 SequenceI seq = (SequenceI)node.element();
\r
257 SequenceGroup sg = av.getSelectionGroup();
\r
259 sg.addOrRemove(seq);
\r
263 pickNode(pickBox,(SequenceNode)node.left(), chunk,scale,width,offx,offy);
\r
264 pickNode(pickBox,(SequenceNode)node.right(),chunk,scale,width,offx,offy);
\r
268 public void setColor(SequenceNode node, Color c)
\r
273 if (node.left() == null && node.right() == null)
\r
277 if (node.element() instanceof SequenceI)
\r
278 ((SequenceI)node.element()).setColor(c);
\r
282 setColor((SequenceNode)node.left(),c);
\r
283 setColor((SequenceNode)node.right(),c);
\r
288 public void paint(Graphics g)
\r
291 font = new Font("Verdana",Font.PLAIN,fontSize);
\r
294 FontMetrics fm = g.getFontMetrics(font);
\r
296 if(nameHash.size()==0)
\r
300 if( fitToWindow || (!fitToWindow && scrollPane.getSize().height > fm.getHeight() * nameHash.size()+offy ) )
\r
302 draw(g,scrollPane.getSize().width,scrollPane.getSize().height);
\r
306 setSize(new Dimension(scrollPane.getSize().width, fm.getHeight() * nameHash.size()));
\r
307 draw( g,scrollPane.getSize().width, fm.getHeight() * nameHash.size());
\r
310 scrollPane.validate();
\r
312 public int getFontSize() {
\r
315 public void setFontSize(int fontSize) {
\r
316 this.fontSize = fontSize;
\r
319 public void draw(Graphics g, int width, int height) {
\r
321 g.setColor(Color.white);
\r
322 g.fillRect(0,0,width,height);
\r
325 labelLength = g.getFontMetrics(font).stringWidth(longestName)+ 20;//20 allows for scrollbar
\r
327 float wscale =(float)(width - labelLength -offx*2)/tree.getMaxHeight();
\r
329 SequenceNode top = tree.getTopNode();
\r
331 if (top.count == 0) {
\r
332 top.count = ((SequenceNode)top.left()).count + ((SequenceNode)top.right()).count ;
\r
334 float chunk = (float)(height-offy*2)/top.count ;
\r
336 drawNode(g,tree.getTopNode(),chunk,wscale,width,offx,offy);
\r
338 if (threshold != 0)
\r
340 if(av.getCurrentTree() == tree)
\r
341 g.setColor(Color.red);
\r
343 g.setColor(Color.gray);
\r
345 int x = (int)( threshold * (float)(getSize().width-labelLength - 2*offx) +offx ) ;
\r
347 g.drawLine(x,0,x,getSize().height);
\r
352 public void mouseReleased(MouseEvent e) { }
\r
353 public void mouseEntered(MouseEvent e) { }
\r
354 public void mouseExited(MouseEvent e) { }
\r
355 public void mouseClicked(MouseEvent e) {
\r
358 public void mousePressed(MouseEvent e) {
\r
360 av.setCurrentTree(tree);
\r
365 Object ob = findElement(x,y);
\r
367 if (ob instanceof SequenceI)
\r
369 TreeSelectionChanged((Sequence)ob);
\r
373 } else if (ob instanceof SequenceNode) {
\r
374 SequenceNode tmpnode = (SequenceNode)ob;
\r
375 tree.swapNodes(tmpnode);
\r
376 tree.reCount(tree.getTopNode());
\r
377 tree.findHeight(tree.getTopNode());
\r
381 if (tree.getMaxHeight() != 0) {
\r
382 threshold = (float)(x - offx)/(float)(getSize().width-labelLength - 2*offx);
\r
384 tree.getGroups().removeAllElements();
\r
385 tree.groupNodes(tree.getTopNode(),threshold);
\r
386 setColor(tree.getTopNode(),Color.black);
\r
388 av.setSelectionGroup(null);
\r
389 av.alignment.deleteAllGroups();
\r
391 for (int i=0; i < tree.getGroups().size(); i++)
\r
394 Color col = new Color((int)(Math.random()*255),
\r
395 (int)(Math.random()*255),
\r
396 (int)(Math.random()*255));
\r
397 setColor((SequenceNode)tree.getGroups().elementAt(i),col.brighter());
\r
399 Vector l = tree.findLeaves((SequenceNode)tree.getGroups().elementAt(i),new Vector());
\r
400 SequenceGroup sg = null;
\r
401 for (int j = 0; j < l.size(); j++)
\r
403 SequenceNode sn = (SequenceNode) l.elementAt(j);
\r
405 sg = new SequenceGroup("TreeGroup", av.getGlobalColourScheme(), true, true,false,0,av.alignment.getWidth());
\r
407 sg.addSequence( (Sequence) sn.element());
\r
410 if (av.getGlobalColourScheme() instanceof ConservationColourScheme)
\r
412 ConservationColourScheme ccs = (ConservationColourScheme) av.getGlobalColourScheme();
\r
413 Conservation c = new Conservation("Group",
\r
414 ResidueProperties.propHash, 3,
\r
415 sg.sequences, sg.getStartRes(),
\r
419 c.verdict(false, av.ConsPercGaps);
\r
420 ccs = new ConservationColourScheme(c, ccs.cs);
\r
428 av.alignment.addGroup(sg);
\r
434 PaintRefresher.Refresh(this);
\r
439 public void setShowDistances(boolean state) {
\r
440 this.showDistances = state;
\r
444 public void setShowBootstrap(boolean state) {
\r
445 this.showBootstrap = state;
\r
448 public void setMarkPlaceholders(boolean state) {
\r
449 this.markPlaceholders = state;
\r