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 font = new Font("Verdana", Font.PLAIN, fontSize);
\r
420 FontMetrics fm = g.getFontMetrics(font);
\r
422 if (nameHash.size() == 0)
\r
429 (scrollPane.getHeight() > ( (fm.getHeight() * nameHash.size()) +
\r
432 draw(g, scrollPane.getWidth(), scrollPane.getHeight());
\r
433 setPreferredSize(null);
\r
437 setPreferredSize(new Dimension(scrollPane.getWidth(),
\r
438 fm.getHeight() * nameHash.size()));
\r
439 draw(g, scrollPane.getWidth(), fm.getHeight() * nameHash.size());
\r
442 scrollPane.revalidate();
\r
445 public int getFontSize()
\r
450 public void setFontSize(int fontSize)
\r
452 this.fontSize = fontSize;
\r
456 public void draw(Graphics g1, int width, int height)
\r
458 Graphics2D g2 = (Graphics2D) g1;
\r
459 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
\r
460 RenderingHints.VALUE_ANTIALIAS_ON);
\r
461 g2.setColor(Color.white);
\r
462 g2.fillRect(0, 0, width, height);
\r
464 labelLength = g2.getFontMetrics(font).stringWidth(longestName) + 20; //20 allows for scrollbar
\r
466 float wscale = (float) (width - labelLength - (offx * 2)) /
\r
467 tree.getMaxHeight();
\r
469 SequenceNode top = tree.getTopNode();
\r
471 if (top.count == 0)
\r
473 top.count = ( (SequenceNode) top.left()).count +
\r
474 ( (SequenceNode) top.right()).count;
\r
477 float chunk = (float) (height - (offy * 2)) / top.count;
\r
479 drawNode(g2, tree.getTopNode(), chunk, wscale, width, offx, offy);
\r
481 if (threshold != 0)
\r
483 if (av.getCurrentTree() == tree)
\r
485 g2.setColor(Color.red);
\r
489 g2.setColor(Color.gray);
\r
492 int x = (int) ( (threshold * (float) (getWidth() - labelLength -
\r
493 (2 * offx))) + offx);
\r
495 g2.drawLine(x, 0, x, getHeight());
\r
499 public void mouseReleased(MouseEvent e)
\r
503 public void mouseEntered(MouseEvent e)
\r
507 public void mouseExited(MouseEvent e)
\r
511 public void mouseClicked(MouseEvent e)
\r
515 public void mousePressed(MouseEvent e)
\r
517 av.setCurrentTree(tree);
\r
522 Object ob = findElement(x, y);
\r
524 if (ob instanceof SequenceI)
\r
526 TreeSelectionChanged( (Sequence) ob);
\r
531 else if (ob instanceof SequenceNode)
\r
533 SequenceNode tmpnode = (SequenceNode) ob;
\r
534 tree.swapNodes(tmpnode);
\r
535 tree.reCount(tree.getTopNode());
\r
536 tree.findHeight(tree.getTopNode());
\r
541 if (tree.getMaxHeight() != 0)
\r
543 threshold = (float) (x - offx) / (float) (getWidth() -
\r
544 labelLength - (2 * offx));
\r
546 tree.getGroups().removeAllElements();
\r
547 tree.groupNodes(tree.getTopNode(), threshold);
\r
548 setColor(tree.getTopNode(), Color.black);
\r
550 av.setSelectionGroup(null);
\r
551 av.alignment.deleteAllGroups();
\r
553 for (int i = 0; i < tree.getGroups().size(); i++)
\r
555 Color col = new Color( (int) (Math.random() * 255),
\r
556 (int) (Math.random() * 255),
\r
557 (int) (Math.random() * 255));
\r
558 setColor( (SequenceNode) tree.getGroups().elementAt(i),
\r
561 Vector l = tree.findLeaves( (SequenceNode) tree.getGroups()
\r
565 Vector sequences = new Vector();
\r
566 for (int j = 0; j < l.size(); j++)
\r
568 sequences.add( (Sequence)((SequenceNode) l.elementAt(j)).element());
\r
571 ColourSchemeI cs = ColourSchemeProperty.getColour(sequences, av.alignment.getWidth(),
\r
572 ColourSchemeProperty.getColourName(av.getGlobalColourScheme()));
\r
574 SequenceGroup sg = new SequenceGroup(sequences, "TreeGroup",
\r
576 false, 0, av.alignment.getWidth());
\r
579 ( (ResidueColourScheme) sg.cs).setThreshold(25);
\r
584 if (av.getGlobalColourScheme() instanceof ConservationColourScheme)
\r
586 ConservationColourScheme ccs = (ConservationColourScheme) av.
\r
587 getGlobalColourScheme();
\r
588 Conservation c = new Conservation("Group",
\r
589 ResidueProperties.propHash, 3,
\r
591 sg.getStartRes(), sg.getEndRes());
\r
594 c.verdict(false, av.ConsPercGaps);
\r
595 ccs = new ConservationColourScheme(c, ccs.cs);
\r
600 av.alignment.addGroup(sg);
\r
605 PaintRefresher.Refresh(this, av.alignment);
\r
609 public void setShowDistances(boolean state)
\r
611 this.showDistances = state;
\r
615 public void setShowBootstrap(boolean state)
\r
617 this.showBootstrap = state;
\r
621 public void setMarkPlaceholders(boolean state)
\r
623 this.markPlaceholders = state;
\r