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
359 FontMetrics fm = g.getFontMetrics(font);
\r
361 if (nameHash.size() == 0)
\r
368 scrollPane.getSize().height > fm.getHeight() * nameHash.size() + offy))
\r
370 draw(g, scrollPane.getSize().width, scrollPane.getSize().height);
\r
374 setSize(new Dimension(scrollPane.getSize().width,
\r
375 fm.getHeight() * nameHash.size()));
\r
376 draw(g, scrollPane.getSize().width, fm.getHeight() * nameHash.size());
\r
379 scrollPane.validate();
\r
383 public void draw(Graphics g, int width, int height)
\r
385 offy = font.getSize()+10;
\r
387 g.setColor(Color.white);
\r
388 g.fillRect(0, 0, width, height);
\r
390 labelLength = g.getFontMetrics(font).stringWidth(longestName) + 20; //20 allows for scrollbar
\r
392 float wscale = (float) (width - labelLength - offx * 2) / tree.getMaxHeight();
\r
394 SequenceNode top = tree.getTopNode();
\r
396 if (top.count == 0)
\r
398 top.count = ( (SequenceNode) top.left()).count +
\r
399 ( (SequenceNode) top.right()).count;
\r
401 float chunk = (float) (height - offy) / top.count;
\r
403 drawNode(g, tree.getTopNode(), chunk, wscale, width, offx, offy);
\r
405 if (threshold != 0)
\r
407 if (av.getCurrentTree() == tree)
\r
409 g.setColor(Color.red);
\r
413 g.setColor(Color.gray);
\r
416 int x = (int) (threshold *
\r
417 (float) (getSize().width - labelLength - 2 * offx) + offx);
\r
419 g.drawLine(x, 0, x, getSize().height);
\r
424 public void mouseReleased(MouseEvent e)
\r
427 public void mouseEntered(MouseEvent e)
\r
430 public void mouseExited(MouseEvent e)
\r
433 public void mouseClicked(MouseEvent e)
\r
437 public void mousePressed(MouseEvent e)
\r
439 av.setCurrentTree(tree);
\r
444 Object ob = findElement(x, y);
\r
446 if (ob instanceof SequenceI)
\r
448 TreeSelectionChanged( (Sequence) ob);
\r
453 else if (ob instanceof SequenceNode)
\r
455 SequenceNode tmpnode = (SequenceNode) ob;
\r
456 tree.swapNodes(tmpnode);
\r
457 tree.reCount(tree.getTopNode());
\r
458 tree.findHeight(tree.getTopNode());
\r
464 if (tree.getMaxHeight() != 0)
\r
466 threshold = (float) (x - offx) /
\r
467 (float) (getSize().width - labelLength - 2 * offx);
\r
469 tree.getGroups().removeAllElements();
\r
470 tree.groupNodes(tree.getTopNode(), threshold);
\r
471 setColor(tree.getTopNode(), Color.black);
\r
473 av.setSelectionGroup(null);
\r
474 av.alignment.deleteAllGroups();
\r
476 for (int i = 0; i < tree.getGroups().size(); i++)
\r
479 Color col = new Color( (int) (Math.random() * 255),
\r
480 (int) (Math.random() * 255),
\r
481 (int) (Math.random() * 255));
\r
482 setColor( (SequenceNode) tree.getGroups().elementAt(i), col.brighter());
\r
484 Vector l = tree.findLeaves( (SequenceNode) tree.getGroups().elementAt(
\r
487 Vector sequences = new Vector();
\r
488 for (int j = 0; j < l.size(); j++)
\r
490 SequenceI s1 = (SequenceI) ( (SequenceNode) l.elementAt(j)).element();
\r
491 if(!sequences.contains(s1))
\r
492 sequences.addElement(s1);
\r
495 ColourSchemeI cs = null;
\r
497 if (av.getGlobalColourScheme() != null)
\r
499 if (av.getGlobalColourScheme() instanceof UserColourScheme)
\r
501 cs = new UserColourScheme(
\r
502 ( (UserColourScheme) av.getGlobalColourScheme()).getColours());
\r
506 cs = ColourSchemeProperty.getColour(sequences,
\r
507 av.alignment.getWidth(),
\r
508 ColourSchemeProperty.getColourName(
\r
509 av.getGlobalColourScheme()));
\r
511 cs.setThreshold(av.getGlobalColourScheme().getThreshold(),
\r
512 av.getIgnoreGapsConsensus());
\r
515 SequenceGroup sg = new SequenceGroup(sequences, "TreeGroup",
\r
517 false, 0, av.alignment.getWidth()-1);
\r
520 if ( av.getGlobalColourScheme()!=null
\r
521 && av.getGlobalColourScheme().conservationApplied())
\r
523 Conservation c = new Conservation("Group",
\r
524 ResidueProperties.propHash, 3,
\r
525 sg.sequences, sg.getStartRes(),
\r
529 c.verdict(false, av.ConsPercGaps);
\r
530 cs.setConservation(c);
\r
536 av.alignment.addGroup(sg);
\r
542 PaintRefresher.Refresh(this, av.alignment);
\r
547 public void setShowDistances(boolean state)
\r
549 this.showDistances = state;
\r
553 public void setShowBootstrap(boolean state)
\r
555 this.showBootstrap = state;
\r
559 public void setMarkPlaceholders(boolean state)
\r
561 this.markPlaceholders = state;
\r