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
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.sequences.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
357 if (!jalview.bin.JalviewLite.AWT1)
\r
359 MyGraphics.AntiAlias(g);
\r
365 FontMetrics fm = g.getFontMetrics(font);
\r
367 if (nameHash.size() == 0)
\r
374 scrollPane.getSize().height > fm.getHeight() * nameHash.size() + offy))
\r
376 draw(g, scrollPane.getSize().width, scrollPane.getSize().height);
\r
380 setSize(new Dimension(scrollPane.getSize().width,
\r
381 fm.getHeight() * nameHash.size()));
\r
382 draw(g, scrollPane.getSize().width, fm.getHeight() * nameHash.size());
\r
385 scrollPane.validate();
\r
389 public void draw(Graphics g, int width, int height)
\r
391 offy = font.getSize()+10;
\r
393 g.setColor(Color.white);
\r
394 g.fillRect(0, 0, width, height);
\r
396 labelLength = g.getFontMetrics(font).stringWidth(longestName) + 20; //20 allows for scrollbar
\r
398 float wscale = (float) (width - labelLength - offx * 2) / tree.getMaxHeight();
\r
400 SequenceNode top = tree.getTopNode();
\r
402 if (top.count == 0)
\r
404 top.count = ( (SequenceNode) top.left()).count +
\r
405 ( (SequenceNode) top.right()).count;
\r
407 float chunk = (float) (height - offy) / top.count;
\r
409 drawNode(g, tree.getTopNode(), chunk, wscale, width, offx, offy);
\r
411 if (threshold != 0)
\r
413 if (av.getCurrentTree() == tree)
\r
415 g.setColor(Color.red);
\r
419 g.setColor(Color.gray);
\r
422 int x = (int) (threshold *
\r
423 (float) (getSize().width - labelLength - 2 * offx) + offx);
\r
425 g.drawLine(x, 0, x, getSize().height);
\r
430 public void mouseReleased(MouseEvent e)
\r
433 public void mouseEntered(MouseEvent e)
\r
436 public void mouseExited(MouseEvent e)
\r
439 public void mouseClicked(MouseEvent e)
\r
443 public void mousePressed(MouseEvent e)
\r
445 av.setCurrentTree(tree);
\r
450 Object ob = findElement(x, y);
\r
452 if (ob instanceof SequenceI)
\r
454 TreeSelectionChanged( (Sequence) ob);
\r
459 else if (ob instanceof SequenceNode)
\r
461 SequenceNode tmpnode = (SequenceNode) ob;
\r
462 tree.swapNodes(tmpnode);
\r
463 tree.reCount(tree.getTopNode());
\r
464 tree.findHeight(tree.getTopNode());
\r
470 if (tree.getMaxHeight() != 0)
\r
472 threshold = (float) (x - offx) /
\r
473 (float) (getSize().width - labelLength - 2 * offx);
\r
475 tree.getGroups().removeAllElements();
\r
476 tree.groupNodes(tree.getTopNode(), threshold);
\r
477 setColor(tree.getTopNode(), Color.black);
\r
479 av.setSelectionGroup(null);
\r
480 av.alignment.deleteAllGroups();
\r
482 for (int i = 0; i < tree.getGroups().size(); i++)
\r
485 Color col = new Color( (int) (Math.random() * 255),
\r
486 (int) (Math.random() * 255),
\r
487 (int) (Math.random() * 255));
\r
488 setColor( (SequenceNode) tree.getGroups().elementAt(i), col.brighter());
\r
490 Vector l = tree.findLeaves( (SequenceNode) tree.getGroups().elementAt(
\r
493 Vector sequences = new Vector();
\r
494 for (int j = 0; j < l.size(); j++)
\r
496 SequenceI s1 = (SequenceI) ( (SequenceNode) l.elementAt(j)).element();
\r
497 if(!sequences.contains(s1))
\r
498 sequences.addElement(s1);
\r
501 ColourSchemeI cs = null;
\r
503 if (av.getGlobalColourScheme() != null)
\r
505 if (av.getGlobalColourScheme() instanceof UserColourScheme)
\r
507 cs = new UserColourScheme(
\r
508 ( (UserColourScheme) av.getGlobalColourScheme()).getColours());
\r
512 cs = ColourSchemeProperty.getColour(sequences,
\r
513 av.alignment.getWidth(),
\r
514 ColourSchemeProperty.getColourName(
\r
515 av.getGlobalColourScheme()));
\r
517 cs.setThreshold(av.getGlobalColourScheme().getThreshold(),
\r
518 av.getIgnoreGapsConsensus());
\r
521 SequenceGroup sg = new SequenceGroup(sequences, "TreeGroup",
\r
523 false, 0, av.alignment.getWidth()-1);
\r
526 if ( av.getGlobalColourScheme()!=null
\r
527 && av.getGlobalColourScheme().conservationApplied())
\r
529 Conservation c = new Conservation("Group",
\r
530 ResidueProperties.propHash, 3,
\r
531 sg.sequences, sg.getStartRes(),
\r
535 c.verdict(false, av.ConsPercGaps);
\r
536 cs.setConservation(c);
\r
542 av.alignment.addGroup(sg);
\r
548 PaintRefresher.Refresh(this, av.alignment);
\r
553 public void setShowDistances(boolean state)
\r
555 this.showDistances = state;
\r
559 public void setShowBootstrap(boolean state)
\r
561 this.showBootstrap = state;
\r
565 public void setMarkPlaceholders(boolean state)
\r
567 this.markPlaceholders = state;
\r