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
19 package jalview.gui;
\r
24 import java.awt.event.*;
\r
25 import java.awt.print.*;
\r
26 import javax.swing.*;
\r
28 import jalview.analysis.*;
\r
29 import jalview.datamodel.*;
\r
30 import jalview.schemes.*;
\r
31 import jalview.util.*;
\r
33 public class TreeCanvas
\r
34 extends JPanel implements MouseListener, Runnable,
\r
37 public static final String PLACEHOLDER = " * ";
\r
39 JScrollPane scrollPane;
\r
43 boolean fitToWindow = true;
\r
44 boolean showDistances = false;
\r
45 boolean showBootstrap = false;
\r
46 boolean markPlaceholders = false;
\r
51 int labelLength = -1;
\r
53 //RubberbandRectangle rubberband;
\r
55 Hashtable nameHash = new Hashtable();
\r
56 Hashtable nodeHash = new Hashtable();
\r
58 public TreeCanvas(AlignViewport av, NJTree tree, JScrollPane scroller,
\r
63 scrollPane = scroller;
\r
64 addMouseListener(this);
\r
65 tree.findHeight(tree.getTopNode());
\r
66 longestName = label;
\r
68 PaintRefresher.Register(this, av.alignment);
\r
71 public void TreeSelectionChanged(Sequence sequence)
\r
73 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());
\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
94 public void drawNode(Graphics g, SequenceNode node, float chunk,
\r
95 float scale, int width, int offx, int offy)
\r
102 if ( (node.left() == null) && (node.right() == null))
\r
104 // 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() ==
\r
118 g.setColor(Color.black);
\r
122 g.setColor( ( (SequenceI) ( (SequenceNode) node).element()).getColor()
\r
128 g.setColor(Color.black);
\r
131 // Draw horizontal line
\r
132 g.drawLine(xstart, ypos, xend, ypos);
\r
134 String nodeLabel = "";
\r
136 if (showDistances && (node.dist > 0))
\r
138 nodeLabel = new Format("%5.2f").form(node.dist);
\r
145 nodeLabel = nodeLabel + " : ";
\r
148 nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());
\r
151 if (!nodeLabel.equals(""))
\r
153 g.drawString(nodeLabel, xstart, ypos - 10);
\r
156 String name = (markPlaceholders && node.isPlaceholder())
\r
157 ? (PLACEHOLDER + node.getName()) : node.getName();
\r
158 FontMetrics fm = g.getFontMetrics(font);
\r
159 int charWidth = fm.stringWidth(name) + 3;
\r
160 int charHeight = fm.getHeight();
\r
162 Rectangle rect = new Rectangle(xend + 20, ypos - charHeight,
\r
163 charWidth, charHeight);
\r
165 nameHash.put( (SequenceI) node.element(), rect);
\r
167 // Colour selected leaves differently
\r
168 SequenceGroup selected = av.getSelectionGroup();
\r
170 if ( (selected != null) &&
\r
171 selected.sequences.contains( (SequenceI) node.element()))
\r
173 g.setColor(Color.gray);
\r
175 g.fillRect(xend + 10, ypos - charHeight + 3, charWidth,
\r
177 g.setColor(Color.white);
\r
180 g.drawString(name, xend + 10, ypos);
\r
181 g.setColor(Color.black);
\r
185 drawNode(g, (SequenceNode) node.left(), chunk, scale, width, offx,
\r
187 drawNode(g, (SequenceNode) node.right(), chunk, scale, width, offx,
\r
190 float height = node.height;
\r
191 float dist = node.dist;
\r
193 int xstart = (int) ( (height - dist) * scale) + offx;
\r
194 int xend = (int) (height * scale) + offx;
\r
195 int ypos = (int) (node.ycount * chunk) + offy;
\r
197 g.setColor( ( (SequenceNode) node).color.darker());
\r
199 // Draw horizontal line
\r
200 g.drawLine(xstart, ypos, xend, ypos);
\r
201 g.fillRect(xend - 2, ypos - 2, 4, 4);
\r
203 int ystart = (int) ( ( (SequenceNode) node.left()).ycount * chunk) +
\r
205 int yend = (int) ( ( (SequenceNode) node.right()).ycount * chunk) +
\r
208 Rectangle pos = new Rectangle(xend - 2, ypos - 2, 5, 5);
\r
209 nodeHash.put(node, pos);
\r
211 g.drawLine( (int) (height * scale) + offx, ystart,
\r
212 (int) (height * scale) + offx, yend);
\r
214 if (showDistances && (node.dist > 0))
\r
216 g.drawString(new Format("%5.2f").form(node.dist), xstart,
\r
222 public Object findElement(int x, int y)
\r
224 Enumeration keys = nameHash.keys();
\r
226 while (keys.hasMoreElements())
\r
228 Object ob = keys.nextElement();
\r
229 Rectangle rect = (Rectangle) nameHash.get(ob);
\r
231 if ( (x >= rect.x) && (x <= (rect.x + rect.width)) && (y >= rect.y) &&
\r
232 (y <= (rect.y + rect.height)))
\r
238 keys = nodeHash.keys();
\r
240 while (keys.hasMoreElements())
\r
242 Object ob = keys.nextElement();
\r
243 Rectangle rect = (Rectangle) nodeHash.get(ob);
\r
245 if ( (x >= rect.x) && (x <= (rect.x + rect.width)) && (y >= rect.y) &&
\r
246 (y <= (rect.y + rect.height)))
\r
255 public void pickNodes(Rectangle pickBox)
\r
257 int width = getWidth();
\r
258 int height = getHeight();
\r
260 SequenceNode top = tree.getTopNode();
\r
262 float wscale = (float) ( (width * .8) - (offx * 2)) / tree.getMaxHeight();
\r
264 if (top.count == 0)
\r
266 top.count = ( (SequenceNode) top.left()).count +
\r
267 ( (SequenceNode) top.right()).count;
\r
270 float chunk = (float) (height - (offy * 2)) / top.count;
\r
272 pickNode(pickBox, top, chunk, wscale, width, offx, offy);
\r
275 public void pickNode(Rectangle pickBox, SequenceNode node, float chunk,
\r
276 float scale, int width, int offx, int offy)
\r
283 if ( (node.left() == null) && (node.right() == null))
\r
285 float height = node.height;
\r
286 float dist = node.dist;
\r
288 int xstart = (int) ( (height - dist) * scale) + offx;
\r
289 int xend = (int) (height * scale) + offx;
\r
291 int ypos = (int) (node.ycount * chunk) + offy;
\r
293 if (pickBox.contains(new Point(xend, ypos)))
\r
295 if (node.element() instanceof SequenceI)
\r
297 SequenceI seq = (SequenceI) node.element();
\r
298 SequenceGroup sg = av.getSelectionGroup();
\r
302 sg.addOrRemove(seq, true);
\r
309 pickNode(pickBox, (SequenceNode) node.left(), chunk, scale, width,
\r
311 pickNode(pickBox, (SequenceNode) node.right(), chunk, scale, width,
\r
316 public void setColor(SequenceNode node, Color c)
\r
323 if ( (node.left() == null) && (node.right() == null))
\r
327 if (node.element() instanceof SequenceI)
\r
329 ( (SequenceI) node.element()).setColor(c);
\r
335 setColor( (SequenceNode) node.left(), c);
\r
336 setColor( (SequenceNode) node.right(), c);
\r
340 void startPrinting()
\r
342 Thread thread = new Thread(this);
\r
346 // put printing in a thread to avoid painting problems
\r
349 PrinterJob printJob = PrinterJob.getPrinterJob();
\r
350 PageFormat pf = printJob.pageDialog(printJob.defaultPage());
\r
352 printJob.setPrintable(this, pf);
\r
354 if (printJob.printDialog())
\r
360 catch (Exception PrintException)
\r
362 PrintException.printStackTrace();
\r
367 public int print(Graphics pg, PageFormat pf, int pi)
\r
368 throws PrinterException
\r
371 pg.translate( (int) pf.getImageableX(), (int) pf.getImageableY());
\r
373 int pwidth = (int) pf.getImageableWidth();
\r
374 int pheight = (int) pf.getImageableHeight();
\r
376 int noPages = getHeight() / pheight;
\r
380 return Printable.NO_SUCH_PAGE;
\r
383 if (pwidth > getWidth())
\r
385 pwidth = getWidth();
\r
390 if (pheight > getHeight())
\r
392 pheight = getHeight();
\r
399 FontMetrics fm = pg.getFontMetrics(font);
\r
400 int height = fm.getHeight() * nameHash.size();
\r
401 pg.translate(0, -pi * pheight);
\r
402 pg.setClip(0, pi * pheight, pwidth, (pi * pheight) + pheight);
\r
404 // translate number of pages,
\r
405 // height is screen size as this is the
\r
406 // non overlapping text size
\r
410 draw(pg, pwidth, pheight);
\r
412 return Printable.PAGE_EXISTS;
\r
415 public void paintComponent(Graphics g)
\r
417 super.paintComponent(g);
\r
418 font = new Font("Verdana", Font.PLAIN, fontSize);
\r
421 FontMetrics fm = g.getFontMetrics(font);
\r
423 if (nameHash.size() == 0)
\r
430 (scrollPane.getHeight() > ( (fm.getHeight() * nameHash.size()) +
\r
433 draw(g, scrollPane.getWidth(), scrollPane.getHeight());
\r
434 setPreferredSize(null);
\r
438 setPreferredSize(new Dimension(scrollPane.getWidth(),
\r
439 fm.getHeight() * nameHash.size()));
\r
440 draw(g, scrollPane.getWidth(), fm.getHeight() * nameHash.size());
\r
443 scrollPane.revalidate();
\r
446 public int getFontSize()
\r
451 public void setFontSize(int fontSize)
\r
453 this.fontSize = fontSize;
\r
457 public void draw(Graphics g1, int width, int height)
\r
459 Graphics2D g2 = (Graphics2D) g1;
\r
460 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
\r
461 RenderingHints.VALUE_ANTIALIAS_ON);
\r
462 g2.setColor(Color.white);
\r
463 g2.fillRect(0, 0, width, height);
\r
465 labelLength = g2.getFontMetrics(font).stringWidth(longestName) + 20; //20 allows for scrollbar
\r
467 float wscale = (float) (width - labelLength - (offx * 2)) /
\r
468 tree.getMaxHeight();
\r
470 SequenceNode top = tree.getTopNode();
\r
472 if (top.count == 0)
\r
474 top.count = ( (SequenceNode) top.left()).count +
\r
475 ( (SequenceNode) top.right()).count;
\r
478 float chunk = (float) (height - (offy * 2)) / top.count;
\r
480 drawNode(g2, tree.getTopNode(), chunk, wscale, width, offx, offy);
\r
482 if (threshold != 0)
\r
484 if (av.getCurrentTree() == tree)
\r
486 g2.setColor(Color.red);
\r
490 g2.setColor(Color.gray);
\r
493 int x = (int) ( (threshold * (float) (getWidth() - labelLength -
\r
494 (2 * offx))) + offx);
\r
496 g2.drawLine(x, 0, x, getHeight());
\r
500 public void mouseReleased(MouseEvent e)
\r
504 public void mouseEntered(MouseEvent e)
\r
508 public void mouseExited(MouseEvent e)
\r
512 public void mouseClicked(MouseEvent e)
\r
516 public void mousePressed(MouseEvent e)
\r
518 av.setCurrentTree(tree);
\r
523 Object ob = findElement(x, y);
\r
525 if (ob instanceof SequenceI)
\r
527 TreeSelectionChanged( (Sequence) ob);
\r
532 else if (ob instanceof SequenceNode)
\r
534 SequenceNode tmpnode = (SequenceNode) ob;
\r
535 tree.swapNodes(tmpnode);
\r
536 tree.reCount(tree.getTopNode());
\r
537 tree.findHeight(tree.getTopNode());
\r
542 if (tree.getMaxHeight() != 0)
\r
544 threshold = (float) (x - offx) / (float) (getWidth() -
\r
545 labelLength - (2 * offx));
\r
547 tree.getGroups().removeAllElements();
\r
548 tree.groupNodes(tree.getTopNode(), threshold);
\r
549 setColor(tree.getTopNode(), Color.black);
\r
551 av.setSelectionGroup(null);
\r
552 av.alignment.deleteAllGroups();
\r
554 for (int i = 0; i < tree.getGroups().size(); i++)
\r
556 Color col = new Color( (int) (Math.random() * 255),
\r
557 (int) (Math.random() * 255),
\r
558 (int) (Math.random() * 255));
\r
559 setColor( (SequenceNode) tree.getGroups().elementAt(i),
\r
562 Vector l = tree.findLeaves( (SequenceNode) tree.getGroups()
\r
566 Vector sequences = new Vector();
\r
567 for (int j = 0; j < l.size(); j++)
\r
569 sequences.add( (Sequence)((SequenceNode) l.elementAt(j)).element());
\r
572 ColourSchemeI cs = ColourSchemeProperty.getColour(sequences, av.alignment.getWidth(),
\r
573 ColourSchemeProperty.getColourName(av.getGlobalColourScheme()));
\r
575 SequenceGroup sg = new SequenceGroup(sequences, "TreeGroup",
\r
577 false, 0, av.alignment.getWidth());
\r
580 ( (ResidueColourScheme) sg.cs).setThreshold(25);
\r
585 if (av.getGlobalColourScheme() instanceof ConservationColourScheme)
\r
587 ConservationColourScheme ccs = (ConservationColourScheme) av.
\r
588 getGlobalColourScheme();
\r
589 Conservation c = new Conservation("Group",
\r
590 ResidueProperties.propHash, 3,
\r
592 sg.getStartRes(), sg.getEndRes());
\r
595 c.verdict(false, av.ConsPercGaps);
\r
596 ccs = new ConservationColourScheme(c, ccs.cs);
\r
601 av.alignment.addGroup(sg);
\r
606 PaintRefresher.Refresh(this, av.alignment);
\r
610 public void setShowDistances(boolean state)
\r
612 this.showDistances = state;
\r
616 public void setShowBootstrap(boolean state)
\r
618 this.showBootstrap = state;
\r
622 public void setMarkPlaceholders(boolean state)
\r
624 this.markPlaceholders = state;
\r