2 * Jalview - A Sequence Alignment Editor and Viewer
\r
3 * Copyright (C) 2006 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
40 boolean fitToWindow = true;
\r
41 boolean showDistances = false;
\r
42 boolean showBootstrap = false;
\r
43 boolean markPlaceholders = false;
\r
51 int labelLength = -1;
\r
53 //RubberbandRectangle rubberband;
\r
57 Hashtable nameHash = new Hashtable();
\r
58 Hashtable nodeHash = new Hashtable();
\r
60 public TreeCanvas(AlignViewport av, ScrollPane scroller)
\r
63 font = av.getFont();
\r
64 scrollPane = scroller;
\r
65 addMouseListener(this);
\r
68 PaintRefresher.Register(this, av.alignment);
\r
71 public void TreeSelectionChanged(Sequence sequence)
\r
73 SequenceGroup selected = av.getSelectionGroup();
\r
74 if (selected == null)
\r
76 selected = new SequenceGroup();
\r
77 av.setSelectionGroup(selected);
\r
80 selected.setEndRes(av.alignment.getWidth()-1);
\r
81 selected.addOrRemove(sequence, true);
\r
83 PaintRefresher.Refresh(this, av.alignment);
\r
87 public void setTree(NJTree tree)
\r
90 tree.findHeight(tree.getTopNode());
\r
92 // Now have to calculate longest name based on the leaves
\r
93 Vector leaves = tree.findLeaves(tree.getTopNode(), new Vector());
\r
94 boolean has_placeholders = false;
\r
97 for (int i = 0; i < leaves.size(); i++)
\r
99 SequenceNode lf = (SequenceNode) leaves.elementAt(i);
\r
101 if (lf.isPlaceholder())
\r
103 has_placeholders = true;
\r
106 if (longestName.length() < ( (Sequence) lf.element()).getName()
\r
109 longestName = TreeCanvas.PLACEHOLDER +
\r
110 ( (Sequence) lf.element()).getName();
\r
114 setMarkPlaceholders(has_placeholders);
\r
117 public void drawNode(Graphics g, SequenceNode node, float chunk, float scale,
\r
118 int width, int offx, int offy)
\r
125 if (node.left() == null && node.right() == null)
\r
127 // Drawing leaf node
\r
129 float height = node.height;
\r
130 float dist = node.dist;
\r
132 int xstart = (int) ( (height - dist) * scale) + offx;
\r
133 int xend = (int) (height * scale) + offx;
\r
135 int ypos = (int) (node.ycount * chunk) + offy;
\r
137 if (node.element() instanceof SequenceI)
\r
139 if ( ( (SequenceI) ( (SequenceNode) node).element()).getColor() ==
\r
142 g.setColor(Color.black);
\r
146 g.setColor( ( (SequenceI) ( (SequenceNode) node).element()).getColor().
\r
153 g.setColor(Color.black);
\r
156 // Draw horizontal line
\r
157 g.drawLine(xstart, ypos, xend, ypos);
\r
159 String nodeLabel = "";
\r
160 if (showDistances && node.dist > 0)
\r
162 nodeLabel = new Format("%-.2f").form(node.dist);
\r
168 nodeLabel = nodeLabel + " : ";
\r
170 nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());
\r
172 if (!nodeLabel.equals(""))
\r
174 g.drawString(nodeLabel, xstart+2, ypos - 2);
\r
177 String name = (markPlaceholders && node.isPlaceholder()) ?
\r
178 (PLACEHOLDER + node.getName()) : node.getName();
\r
179 FontMetrics fm = g.getFontMetrics(font);
\r
180 int charWidth = fm.stringWidth(name) + 3;
\r
181 int charHeight = fm.getHeight();
\r
183 Rectangle rect = new Rectangle(xend + 10, ypos - charHeight,
\r
184 charWidth, charHeight);
\r
186 nameHash.put( (SequenceI) node.element(), rect);
\r
188 // Colour selected leaves differently
\r
189 SequenceGroup selected = av.getSelectionGroup();
\r
190 if (selected != null &&
\r
191 selected.getSequences(false).contains( (SequenceI) node.element()))
\r
193 g.setColor(Color.gray);
\r
195 g.fillRect(xend + 10, ypos - charHeight + 3, charWidth, charHeight);
\r
196 g.setColor(Color.white);
\r
198 g.drawString(name, xend + 10, ypos);
\r
199 g.setColor(Color.black);
\r
203 drawNode(g, (SequenceNode) node.left(), chunk, scale, width, offx, offy);
\r
204 drawNode(g, (SequenceNode) node.right(), chunk, scale, width, offx, offy);
\r
206 float height = node.height;
\r
207 float dist = node.dist;
\r
209 int xstart = (int) ( (height - dist) * scale) + offx;
\r
210 int xend = (int) (height * scale) + offx;
\r
211 int ypos = (int) (node.ycount * chunk) + offy;
\r
213 g.setColor( ( (SequenceNode) node).color.darker());
\r
215 // Draw horizontal line
\r
216 g.drawLine(xstart, ypos, xend, ypos);
\r
217 g.fillRect(xend - 2, ypos - 2, 4, 4);
\r
219 int ystart = (int) ( ( (SequenceNode) node.left()).ycount * chunk) + offy;
\r
220 int yend = (int) ( ( (SequenceNode) node.right()).ycount * chunk) + offy;
\r
222 Rectangle pos = new Rectangle(xend - 2, ypos - 2, 5, 5);
\r
223 nodeHash.put(node, pos);
\r
225 g.drawLine( (int) (height * scale) + offx, ystart,
\r
226 (int) (height * scale) + offx, yend);
\r
228 if (showDistances && node.dist > 0)
\r
230 g.drawString(new Format("%-.2f").form(node.dist), xstart+2, ypos - 2);
\r
236 public Object findElement(int x, int y)
\r
238 Enumeration keys = nameHash.keys();
\r
240 while (keys.hasMoreElements())
\r
242 Object ob = keys.nextElement();
\r
243 Rectangle rect = (Rectangle) nameHash.get(ob);
\r
245 if (x >= rect.x && x <= (rect.x + rect.width) &&
\r
246 y >= rect.y && y <= (rect.y + rect.height))
\r
251 keys = nodeHash.keys();
\r
253 while (keys.hasMoreElements())
\r
255 Object ob = keys.nextElement();
\r
256 Rectangle rect = (Rectangle) nodeHash.get(ob);
\r
258 if (x >= rect.x && x <= (rect.x + rect.width) &&
\r
259 y >= rect.y && y <= (rect.y + rect.height))
\r
268 public void pickNodes(Rectangle pickBox)
\r
270 int width = getSize().width;
\r
271 int height = getSize().height;
\r
273 SequenceNode top = tree.getTopNode();
\r
275 float wscale = (float) (width * .8 - offx * 2) / tree.getMaxHeight()
\r
277 if (top.count == 0)
\r
279 top.count = ( (SequenceNode) top.left()).count +
\r
280 ( (SequenceNode) top.right()).count;
\r
282 float chunk = (float) (height - offy) / top.count;
\r
284 pickNode(pickBox, top, chunk, wscale, width, offx, offy);
\r
287 public void pickNode(Rectangle pickBox, SequenceNode node, float chunk,
\r
288 float scale, int width, int offx, int offy)
\r
295 if (node.left() == null && node.right() == null)
\r
297 float height = node.height;
\r
298 //float dist = node.dist;
\r
300 //int xstart = (int) ( (height - dist) * scale) + offx;
\r
301 int xend = (int) (height * scale) + offx;
\r
303 int ypos = (int) (node.ycount * chunk) + offy;
\r
305 if (pickBox.contains(new Point(xend, ypos)))
\r
307 if (node.element() instanceof SequenceI)
\r
309 SequenceI seq = (SequenceI) node.element();
\r
310 SequenceGroup sg = av.getSelectionGroup();
\r
313 sg.addOrRemove(seq, true);
\r
320 pickNode(pickBox, (SequenceNode) node.left(), chunk, scale, width, offx,
\r
322 pickNode(pickBox, (SequenceNode) node.right(), chunk, scale, width, offx,
\r
327 public void setColor(SequenceNode node, Color c)
\r
334 if (node.left() == null && node.right() == null)
\r
338 if (node.element() instanceof SequenceI)
\r
340 ( (SequenceI) node.element()).setColor(c);
\r
346 setColor( (SequenceNode) node.left(), c);
\r
347 setColor( (SequenceNode) node.right(), c);
\r
351 public void paint(Graphics g)
\r
360 FontMetrics fm = g.getFontMetrics(font);
\r
362 if (nameHash.size() == 0)
\r
369 scrollPane.getSize().height > fm.getHeight() * nameHash.size() + offy))
\r
371 draw(g, scrollPane.getSize().width, scrollPane.getSize().height);
\r
375 setSize(new Dimension(scrollPane.getSize().width,
\r
376 fm.getHeight() * nameHash.size()));
\r
377 draw(g, scrollPane.getSize().width, fm.getHeight() * nameHash.size());
\r
380 scrollPane.validate();
\r
384 public void draw(Graphics g, int width, int height)
\r
386 offy = font.getSize()+10;
\r
388 g.setColor(Color.white);
\r
389 g.fillRect(0, 0, width, height);
\r
391 labelLength = g.getFontMetrics(font).stringWidth(longestName) + 20; //20 allows for scrollbar
\r
393 float wscale = (float) (width - labelLength - offx * 2) / tree.getMaxHeight();
\r
395 SequenceNode top = tree.getTopNode();
\r
397 if (top.count == 0)
\r
399 top.count = ( (SequenceNode) top.left()).count +
\r
400 ( (SequenceNode) top.right()).count;
\r
402 float chunk = (float) (height - offy) / top.count;
\r
404 drawNode(g, tree.getTopNode(), chunk, wscale, width, offx, offy);
\r
406 if (threshold != 0)
\r
408 if (av.getCurrentTree() == tree)
\r
410 g.setColor(Color.red);
\r
414 g.setColor(Color.gray);
\r
417 int x = (int) (threshold *
\r
418 (float) (getSize().width - labelLength - 2 * offx) + offx);
\r
420 g.drawLine(x, 0, x, getSize().height);
\r
425 public void mouseReleased(MouseEvent e)
\r
428 public void mouseEntered(MouseEvent e)
\r
431 public void mouseExited(MouseEvent e)
\r
434 public void mouseClicked(MouseEvent e)
\r
438 public void mousePressed(MouseEvent e)
\r
440 av.setCurrentTree(tree);
\r
445 Object ob = findElement(x, y);
\r
447 if (ob instanceof SequenceI)
\r
449 TreeSelectionChanged( (Sequence) ob);
\r
454 else if (ob instanceof SequenceNode)
\r
456 SequenceNode tmpnode = (SequenceNode) ob;
\r
457 tree.swapNodes(tmpnode);
\r
458 tree.reCount(tree.getTopNode());
\r
459 tree.findHeight(tree.getTopNode());
\r
465 if (tree.getMaxHeight() != 0)
\r
467 threshold = (float) (x - offx) /
\r
468 (float) (getSize().width - labelLength - 2 * offx);
\r
470 tree.getGroups().removeAllElements();
\r
471 tree.groupNodes(tree.getTopNode(), threshold);
\r
472 setColor(tree.getTopNode(), Color.black);
\r
474 av.setSelectionGroup(null);
\r
475 av.alignment.deleteAllGroups();
\r
477 for (int i = 0; i < tree.getGroups().size(); i++)
\r
480 Color col = new Color( (int) (Math.random() * 255),
\r
481 (int) (Math.random() * 255),
\r
482 (int) (Math.random() * 255));
\r
483 setColor( (SequenceNode) tree.getGroups().elementAt(i), col.brighter());
\r
485 Vector l = tree.findLeaves( (SequenceNode) tree.getGroups().elementAt(
\r
488 Vector sequences = new Vector();
\r
489 for (int j = 0; j < l.size(); j++)
\r
491 SequenceI s1 = (SequenceI) ( (SequenceNode) l.elementAt(j)).element();
\r
492 if(!sequences.contains(s1))
\r
493 sequences.addElement(s1);
\r
496 ColourSchemeI cs = null;
\r
498 if (av.getGlobalColourScheme() != null)
\r
500 if (av.getGlobalColourScheme() instanceof UserColourScheme)
\r
502 cs = new UserColourScheme(
\r
503 ( (UserColourScheme) av.getGlobalColourScheme()).getColours());
\r
507 cs = ColourSchemeProperty.getColour(sequences,
\r
508 av.alignment.getWidth(),
\r
509 ColourSchemeProperty.getColourName(
\r
510 av.getGlobalColourScheme()));
\r
512 cs.setThreshold(av.getGlobalColourScheme().getThreshold(),
\r
513 av.getIgnoreGapsConsensus());
\r
516 SequenceGroup sg = new SequenceGroup(sequences, "TreeGroup",
\r
518 false, 0, av.alignment.getWidth()-1);
\r
521 if ( av.getGlobalColourScheme()!=null
\r
522 && av.getGlobalColourScheme().conservationApplied())
\r
524 Conservation c = new Conservation("Group",
\r
525 ResidueProperties.propHash, 3,
\r
526 sg.getSequences(false),
\r
531 c.verdict(false, av.ConsPercGaps);
\r
532 cs.setConservation(c);
\r
538 av.alignment.addGroup(sg);
\r
544 PaintRefresher.Refresh(this, av.alignment);
\r
549 public void setShowDistances(boolean state)
\r
551 this.showDistances = state;
\r
555 public void setShowBootstrap(boolean state)
\r
557 this.showBootstrap = state;
\r
561 public void setMarkPlaceholders(boolean state)
\r
563 this.markPlaceholders = state;
\r