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, ScrollPane scroller)
\r
65 scrollPane = scroller;
\r
66 addMouseListener(this);
\r
69 PaintRefresher.Register(this, av.alignment);
\r
72 public void TreeSelectionChanged(Sequence sequence)
\r
74 SequenceGroup selected = av.getSelectionGroup();
\r
75 if (selected == null)
\r
77 selected = new SequenceGroup();
\r
78 av.setSelectionGroup(selected);
\r
81 selected.setEndRes(av.alignment.getWidth()-1);
\r
82 selected.addOrRemove(sequence, true);
\r
84 PaintRefresher.Refresh(this, av.alignment);
\r
88 public void setTree(NJTree tree)
\r
91 tree.findHeight(tree.getTopNode());
\r
93 // Now have to calculate longest name based on the leaves
\r
94 Vector leaves = tree.findLeaves(tree.getTopNode(), new Vector());
\r
95 boolean has_placeholders = false;
\r
98 for (int i = 0; i < leaves.size(); i++)
\r
100 SequenceNode lf = (SequenceNode) leaves.elementAt(i);
\r
102 if (lf.isPlaceholder())
\r
104 has_placeholders = true;
\r
107 if (longestName.length() < ( (Sequence) lf.element()).getName()
\r
110 longestName = TreeCanvas.PLACEHOLDER +
\r
111 ( (Sequence) lf.element()).getName();
\r
115 setMarkPlaceholders(has_placeholders);
\r
118 public void drawNode(Graphics g, SequenceNode node, float chunk, float scale,
\r
119 int width, int offx, int offy)
\r
126 if (node.left() == null && node.right() == null)
\r
128 // Drawing leaf node
\r
130 float height = node.height;
\r
131 float dist = node.dist;
\r
133 int xstart = (int) ( (height - dist) * scale) + offx;
\r
134 int xend = (int) (height * scale) + offx;
\r
136 int ypos = (int) (node.ycount * chunk) + offy;
\r
138 if (node.element() instanceof SequenceI)
\r
140 if ( ( (SequenceI) ( (SequenceNode) node).element()).getColor() ==
\r
143 g.setColor(Color.black);
\r
147 g.setColor( ( (SequenceI) ( (SequenceNode) node).element()).getColor().
\r
154 g.setColor(Color.black);
\r
157 // Draw horizontal line
\r
158 g.drawLine(xstart, ypos, xend, ypos);
\r
160 String nodeLabel = "";
\r
161 if (showDistances && node.dist > 0)
\r
163 nodeLabel = new Format("%5.2f").form(node.dist);
\r
169 nodeLabel = nodeLabel + " : ";
\r
171 nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());
\r
173 if (!nodeLabel.equals(""))
\r
175 g.drawString(nodeLabel, xstart, ypos - 10);
\r
178 String name = (markPlaceholders && node.isPlaceholder()) ?
\r
179 (PLACEHOLDER + node.getName()) : node.getName();
\r
180 FontMetrics fm = g.getFontMetrics(font);
\r
181 int charWidth = fm.stringWidth(name) + 3;
\r
182 int charHeight = fm.getHeight();
\r
184 Rectangle rect = new Rectangle(xend + 20, ypos - charHeight,
\r
185 charWidth, charHeight);
\r
187 nameHash.put( (SequenceI) node.element(), rect);
\r
189 // Colour selected leaves differently
\r
190 SequenceGroup selected = av.getSelectionGroup();
\r
191 if (selected != null &&
\r
192 selected.sequences.contains( (SequenceI) node.element()))
\r
194 g.setColor(Color.gray);
\r
196 g.fillRect(xend + 10, ypos - charHeight + 3, charWidth, charHeight);
\r
197 g.setColor(Color.white);
\r
199 g.drawString(name, xend + 10, ypos);
\r
200 g.setColor(Color.black);
\r
204 drawNode(g, (SequenceNode) node.left(), chunk, scale, width, offx, offy);
\r
205 drawNode(g, (SequenceNode) node.right(), chunk, scale, width, offx, offy);
\r
207 float height = node.height;
\r
208 float dist = node.dist;
\r
210 int xstart = (int) ( (height - dist) * scale) + offx;
\r
211 int xend = (int) (height * scale) + offx;
\r
212 int ypos = (int) (node.ycount * chunk) + offy;
\r
214 g.setColor( ( (SequenceNode) node).color.darker());
\r
216 // Draw horizontal line
\r
217 g.drawLine(xstart, ypos, xend, ypos);
\r
218 g.fillRect(xend - 2, ypos - 2, 4, 4);
\r
220 int ystart = (int) ( ( (SequenceNode) node.left()).ycount * chunk) + offy;
\r
221 int yend = (int) ( ( (SequenceNode) node.right()).ycount * chunk) + offy;
\r
223 Rectangle pos = new Rectangle(xend - 2, ypos - 2, 5, 5);
\r
224 nodeHash.put(node, pos);
\r
226 g.drawLine( (int) (height * scale) + offx, ystart,
\r
227 (int) (height * scale) + offx, yend);
\r
229 if (showDistances && node.dist > 0)
\r
231 g.drawString(new Format("%5.2f").form(node.dist), xstart, ypos - 5);
\r
237 public Object findElement(int x, int y)
\r
239 Enumeration keys = nameHash.keys();
\r
241 while (keys.hasMoreElements())
\r
243 Object ob = keys.nextElement();
\r
244 Rectangle rect = (Rectangle) nameHash.get(ob);
\r
246 if (x >= rect.x && x <= (rect.x + rect.width) &&
\r
247 y >= rect.y && y <= (rect.y + rect.height))
\r
252 keys = nodeHash.keys();
\r
254 while (keys.hasMoreElements())
\r
256 Object ob = keys.nextElement();
\r
257 Rectangle rect = (Rectangle) nodeHash.get(ob);
\r
259 if (x >= rect.x && x <= (rect.x + rect.width) &&
\r
260 y >= rect.y && y <= (rect.y + rect.height))
\r
269 public void pickNodes(Rectangle pickBox)
\r
271 int width = getSize().width;
\r
272 int height = getSize().height;
\r
274 SequenceNode top = tree.getTopNode();
\r
276 float wscale = (float) (width * .8 - offx * 2) / tree.getMaxHeight()
\r
278 if (top.count == 0)
\r
280 top.count = ( (SequenceNode) top.left()).count +
\r
281 ( (SequenceNode) top.right()).count;
\r
283 float chunk = (float) (height - offy * 2) / top.count;
\r
285 pickNode(pickBox, top, chunk, wscale, width, offx, offy);
\r
288 public void pickNode(Rectangle pickBox, SequenceNode node, float chunk,
\r
289 float scale, int width, int offx, int offy)
\r
296 if (node.left() == null && node.right() == null)
\r
298 float height = node.height;
\r
299 float dist = node.dist;
\r
301 int xstart = (int) ( (height - dist) * scale) + offx;
\r
302 int xend = (int) (height * scale) + offx;
\r
304 int ypos = (int) (node.ycount * chunk) + offy;
\r
306 if (pickBox.contains(new Point(xend, ypos)))
\r
308 if (node.element() instanceof SequenceI)
\r
310 SequenceI seq = (SequenceI) node.element();
\r
311 SequenceGroup sg = av.getSelectionGroup();
\r
314 sg.addOrRemove(seq, true);
\r
321 pickNode(pickBox, (SequenceNode) node.left(), chunk, scale, width, offx,
\r
323 pickNode(pickBox, (SequenceNode) node.right(), chunk, scale, width, offx,
\r
328 public void setColor(SequenceNode node, Color c)
\r
335 if (node.left() == null && node.right() == null)
\r
339 if (node.element() instanceof SequenceI)
\r
341 ( (SequenceI) node.element()).setColor(c);
\r
347 setColor( (SequenceNode) node.left(), c);
\r
348 setColor( (SequenceNode) node.right(), c);
\r
352 public void paint(Graphics g)
\r
355 font = new Font("Verdana", Font.PLAIN, fontSize);
\r
358 FontMetrics fm = g.getFontMetrics(font);
\r
360 if (nameHash.size() == 0)
\r
367 scrollPane.getSize().height > fm.getHeight() * nameHash.size() + offy))
\r
369 draw(g, scrollPane.getSize().width, scrollPane.getSize().height);
\r
373 setSize(new Dimension(scrollPane.getSize().width,
\r
374 fm.getHeight() * nameHash.size()));
\r
375 draw(g, scrollPane.getSize().width, fm.getHeight() * nameHash.size());
\r
378 scrollPane.validate();
\r
381 public int getFontSize()
\r
386 public void setFontSize(int fontSize)
\r
388 this.fontSize = fontSize;
\r
392 public void draw(Graphics g, int width, int height)
\r
395 g.setColor(Color.white);
\r
396 g.fillRect(0, 0, width, height);
\r
398 labelLength = g.getFontMetrics(font).stringWidth(longestName) + 20; //20 allows for scrollbar
\r
400 float wscale = (float) (width - labelLength - offx * 2) / tree.getMaxHeight();
\r
402 SequenceNode top = tree.getTopNode();
\r
404 if (top.count == 0)
\r
406 top.count = ( (SequenceNode) top.left()).count +
\r
407 ( (SequenceNode) top.right()).count;
\r
409 float chunk = (float) (height - offy * 2) / top.count;
\r
411 drawNode(g, tree.getTopNode(), chunk, wscale, width, offx, offy);
\r
413 if (threshold != 0)
\r
415 if (av.getCurrentTree() == tree)
\r
417 g.setColor(Color.red);
\r
421 g.setColor(Color.gray);
\r
424 int x = (int) (threshold *
\r
425 (float) (getSize().width - labelLength - 2 * offx) + offx);
\r
427 g.drawLine(x, 0, x, getSize().height);
\r
432 public void mouseReleased(MouseEvent e)
\r
435 public void mouseEntered(MouseEvent e)
\r
438 public void mouseExited(MouseEvent e)
\r
441 public void mouseClicked(MouseEvent e)
\r
445 public void mousePressed(MouseEvent e)
\r
447 av.setCurrentTree(tree);
\r
452 Object ob = findElement(x, y);
\r
454 if (ob instanceof SequenceI)
\r
456 TreeSelectionChanged( (Sequence) ob);
\r
461 else if (ob instanceof SequenceNode)
\r
463 SequenceNode tmpnode = (SequenceNode) ob;
\r
464 tree.swapNodes(tmpnode);
\r
465 tree.reCount(tree.getTopNode());
\r
466 tree.findHeight(tree.getTopNode());
\r
472 if (tree.getMaxHeight() != 0)
\r
474 threshold = (float) (x - offx) /
\r
475 (float) (getSize().width - labelLength - 2 * offx);
\r
477 tree.getGroups().removeAllElements();
\r
478 tree.groupNodes(tree.getTopNode(), threshold);
\r
479 setColor(tree.getTopNode(), Color.black);
\r
481 av.setSelectionGroup(null);
\r
482 av.alignment.deleteAllGroups();
\r
484 for (int i = 0; i < tree.getGroups().size(); i++)
\r
487 Color col = new Color( (int) (Math.random() * 255),
\r
488 (int) (Math.random() * 255),
\r
489 (int) (Math.random() * 255));
\r
490 setColor( (SequenceNode) tree.getGroups().elementAt(i), col.brighter());
\r
492 Vector l = tree.findLeaves( (SequenceNode) tree.getGroups().elementAt(
\r
495 Vector sequences = new Vector();
\r
496 for (int j = 0; j < l.size(); j++)
\r
498 SequenceI s1 = (SequenceI) ( (SequenceNode) l.elementAt(j)).element();
\r
499 if(!sequences.contains(s1))
\r
500 sequences.addElement(s1);
\r
503 ColourSchemeI cs = null;
\r
505 if (av.getGlobalColourScheme() != null)
\r
507 if (av.getGlobalColourScheme() instanceof UserColourScheme)
\r
509 cs = new UserColourScheme(
\r
510 ( (UserColourScheme) av.getGlobalColourScheme()).getColours());
\r
514 cs = ColourSchemeProperty.getColour(sequences,
\r
515 av.alignment.getWidth(),
\r
516 ColourSchemeProperty.getColourName(
\r
517 av.getGlobalColourScheme()));
\r
519 cs.setThreshold(av.getGlobalColourScheme().getThreshold(),
\r
520 av.getIgnoreGapsConsensus());
\r
523 SequenceGroup sg = new SequenceGroup(sequences, "TreeGroup",
\r
525 false, 0, av.alignment.getWidth()-1);
\r
528 if ( av.getGlobalColourScheme()!=null
\r
529 && av.getGlobalColourScheme().conservationApplied())
\r
531 Conservation c = new Conservation("Group",
\r
532 ResidueProperties.propHash, 3,
\r
533 sg.sequences, sg.getStartRes(),
\r
537 c.verdict(false, av.ConsPercGaps);
\r
538 cs.setConservation(c);
\r
544 av.alignment.addGroup(sg);
\r
550 PaintRefresher.Refresh(this, av.alignment);
\r
555 public void setShowDistances(boolean state)
\r
557 this.showDistances = state;
\r
561 public void setShowBootstrap(boolean state)
\r
563 this.showBootstrap = state;
\r
567 public void setMarkPlaceholders(boolean state)
\r
569 this.markPlaceholders = state;
\r