4 import jalview.analysis.*;
\r
5 import jalview.datamodel.*;
\r
6 import jalview.util.*;
\r
7 import jalview.schemes.*;
\r
8 import javax.swing.*;
\r
10 import java.awt.event.*;
\r
12 import java.awt.print.*;
\r
14 public class TreeCanvas extends JPanel implements MouseListener, Runnable, Printable
\r
17 JScrollPane scrollPane;
\r
23 boolean fitToWindow = true;
\r
24 boolean showDistances = false;
\r
25 boolean showBootstrap = false;
\r
35 //RubberbandRectangle rubberband;
\r
39 Hashtable nameHash = new Hashtable();
\r
40 Hashtable nodeHash = new Hashtable();
\r
42 public TreeCanvas(AlignViewport av, NJTree tree, JScrollPane scroller, String label)
\r
46 scrollPane = scroller;
\r
47 addMouseListener(this);
\r
48 tree.findHeight(tree.getTopNode());
\r
49 longestName = label;
\r
51 PaintRefresher.Register(this);
\r
53 public void TreeSelectionChanged(Sequence sequence)
\r
55 SequenceGroup selected = av.getSelectionGroup();
\r
56 if(selected == null)
\r
58 selected = new SequenceGroup();
\r
59 av.setSelectionGroup(selected);
\r
62 selected.setEndRes(av.alignment.getWidth());
\r
63 selected.addOrRemove(sequence);
\r
66 PaintRefresher.Refresh(this);
\r
72 public void setTree(NJTree tree) {
\r
74 tree.findHeight(tree.getTopNode());
\r
77 public void drawNode(Graphics g,SequenceNode node, float chunk, float scale, int width,int offx, int offy) {
\r
82 if (node.left() == null && node.right() == null) {
\r
83 // Drawing leaf node
\r
85 float height = node.height;
\r
86 float dist = node.dist;
\r
88 int xstart = (int)((height-dist)*scale) + offx;
\r
89 int xend = (int)(height*scale) + offx;
\r
91 int ypos = (int)(node.ycount * chunk) + offy;
\r
93 if (node.element() instanceof SequenceI)
\r
95 if ( ( (SequenceI) ( (SequenceNode) node).element()).getColor() == Color.white)
\r
97 g.setColor(Color.black);
\r
100 g.setColor( ( (SequenceI) ( (SequenceNode) node).element()).getColor().
\r
105 g.setColor(Color.black);
\r
108 // Draw horizontal line
\r
109 g.drawLine(xstart,ypos,xend,ypos);
\r
111 String nodeLabel = "";
\r
112 if (showDistances && node.dist > 0) {
\r
113 nodeLabel = new Format("%5.2f").form(node.dist);
\r
115 if (showBootstrap) {
\r
116 if (showDistances) {
\r
117 nodeLabel = nodeLabel + " : ";
\r
119 nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());
\r
121 if (! nodeLabel.equals("")) {
\r
122 g.drawString(nodeLabel,xstart,ypos - 10);
\r
125 // Colour selected leaves differently
\r
126 String name = node.getName();
\r
127 FontMetrics fm = g.getFontMetrics(font);
\r
128 int charWidth = fm.stringWidth(node.getName()) + 3;
\r
129 int charHeight = fm.getHeight();
\r
131 Rectangle rect = new Rectangle(xend+20,ypos-charHeight,
\r
132 charWidth,charHeight);
\r
134 nameHash.put((SequenceI)node.element(),rect);
\r
136 SequenceGroup selected = av.getSelectionGroup();
\r
137 if (selected!=null && selected.sequences.contains((SequenceI)node.element())) {
\r
138 g.setColor(Color.gray);
\r
140 g.fillRect(xend + 10, ypos - charHeight + 3,charWidth,charHeight);
\r
141 g.setColor(Color.white);
\r
143 g.drawString(node.getName(),xend+10,ypos);
\r
144 g.setColor(Color.black);
\r
146 drawNode(g,(SequenceNode)node.left(), chunk,scale,width,offx,offy);
\r
147 drawNode(g,(SequenceNode)node.right(),chunk,scale,width,offx,offy);
\r
149 float height = node.height;
\r
150 float dist = node.dist;
\r
152 int xstart = (int)((height-dist)*scale) + offx;
\r
153 int xend = (int)(height *scale) + offx;
\r
154 int ypos = (int)(node.ycount *chunk) + offy;
\r
156 g.setColor(((SequenceNode)node).color.darker());
\r
158 // Draw horizontal line
\r
159 g.drawLine(xstart,ypos,xend,ypos);
\r
160 g.fillRect(xend-2, ypos-2, 4,4);
\r
162 int ystart = (int)(((SequenceNode)node.left()) .ycount * chunk) + offy;
\r
163 int yend = (int)(((SequenceNode)node.right()).ycount * chunk) + offy;
\r
165 Rectangle pos = new Rectangle(xend-2,ypos-2,5,5);
\r
166 nodeHash.put(node,pos);
\r
168 g.drawLine((int)(height*scale) + offx, ystart,
\r
169 (int)(height*scale) + offx, yend);
\r
171 if (showDistances && node.dist > 0) {
\r
172 g.drawString(new Format("%5.2f").form(node.dist),xstart,ypos - 5);
\r
177 public Object findElement(int x, int y) {
\r
178 Enumeration keys = nameHash.keys();
\r
180 while (keys.hasMoreElements()) {
\r
181 Object ob = keys.nextElement();
\r
182 Rectangle rect = (Rectangle)nameHash.get(ob);
\r
184 if (x >= rect.x && x <= (rect.x + rect.width) &&
\r
185 y >= rect.y && y <= (rect.y + rect.height)) {
\r
189 keys = nodeHash.keys();
\r
191 while (keys.hasMoreElements()) {
\r
192 Object ob = keys.nextElement();
\r
193 Rectangle rect = (Rectangle)nodeHash.get(ob);
\r
195 if (x >= rect.x && x <= (rect.x + rect.width) &&
\r
196 y >= rect.y && y <= (rect.y + rect.height)) {
\r
204 public void pickNodes(Rectangle pickBox) {
\r
205 int width = getWidth();
\r
206 int height = getHeight();
\r
208 SequenceNode top = tree.getTopNode();
\r
210 float wscale = (float)(width*.8-offx*2)/tree.getMaxHeight()
\r
212 if (top.count == 0) {
\r
213 top.count = ((SequenceNode)top.left()).count + ((SequenceNode)top.right()).count ;
\r
215 float chunk = (float)(height-offy*2)/top.count;
\r
217 pickNode(pickBox,top,chunk,wscale,width,offx,offy);
\r
220 public void pickNode(Rectangle pickBox, SequenceNode node, float chunk, float scale, int width,int offx, int offy) {
\r
221 if (node == null) {
\r
225 if (node.left() == null && node.right() == null) {
\r
226 float height = node.height;
\r
227 float dist = node.dist;
\r
229 int xstart = (int)((height-dist)*scale) + offx;
\r
230 int xend = (int)(height*scale) + offx;
\r
232 int ypos = (int)(node.ycount * chunk) + offy;
\r
234 if (pickBox.contains(new Point(xend,ypos))) {
\r
235 if (node.element() instanceof SequenceI) {
\r
236 SequenceI seq = (SequenceI)node.element();
\r
237 SequenceGroup sg = av.getSelectionGroup();
\r
239 sg.addOrRemove(seq);
\r
243 pickNode(pickBox,(SequenceNode)node.left(), chunk,scale,width,offx,offy);
\r
244 pickNode(pickBox,(SequenceNode)node.right(),chunk,scale,width,offx,offy);
\r
248 public void setColor(SequenceNode node, Color c)
\r
253 if (node.left() == null && node.right() == null)
\r
257 if (node.element() instanceof SequenceI)
\r
258 ((SequenceI)node.element()).setColor(c);
\r
262 setColor((SequenceNode)node.left(),c);
\r
263 setColor((SequenceNode)node.right(),c);
\r
267 void startPrinting()
\r
269 Thread thread = new Thread(this);
\r
273 // put printing in a thread to avoid painting problems
\r
276 PrinterJob printJob = PrinterJob.getPrinterJob();
\r
277 PageFormat pf = printJob.pageDialog(printJob.defaultPage());
\r
279 printJob.setPrintable(this, pf);
\r
280 if (printJob.printDialog())
\r
286 catch (Exception PrintException)
\r
288 PrintException.printStackTrace();
\r
294 public int print(Graphics pg, PageFormat pf, int pi) throws PrinterException
\r
298 pg.translate((int)pf.getImageableX(), (int)pf.getImageableY());
\r
299 int pwidth = (int) pf.getImageableWidth();
\r
300 int pheight = (int) pf.getImageableHeight();
\r
302 int noPages = getHeight() / pheight;
\r
304 return Printable.NO_SUCH_PAGE;
\r
307 if (pwidth > getWidth())
\r
308 pwidth = getWidth();
\r
312 if (pheight > getHeight())
\r
313 pheight = getHeight();
\r
320 FontMetrics fm = pg.getFontMetrics(font);
\r
321 int height = fm.getHeight() * nameHash.size();
\r
322 pg.translate(0, -pi*pheight );
\r
323 pg.setClip(0,pi*pheight, pwidth,pi*pheight + pheight);
\r
324 // translate number of pages,
\r
325 // height is screen size as this is the
\r
326 // non overlapping text size
\r
330 draw(pg, pwidth, pheight);
\r
332 return Printable.PAGE_EXISTS;
\r
336 public void paintComponent(Graphics g)
\r
339 font = new Font("Verdana",Font.PLAIN,fontSize);
\r
342 FontMetrics fm = g.getFontMetrics(font);
\r
344 if(nameHash.size()==0)
\r
348 if( fitToWindow || (!fitToWindow && scrollPane.getHeight() > fm.getHeight() * nameHash.size()+offy ) )
\r
350 draw(g,scrollPane.getWidth(),scrollPane.getHeight());
\r
351 setPreferredSize(null);
\r
355 setPreferredSize(new Dimension(scrollPane.getWidth(), fm.getHeight() * nameHash.size()));
\r
356 draw( g,scrollPane.getWidth(), fm.getHeight() * nameHash.size());
\r
359 scrollPane.revalidate();
\r
361 public int getFontSize() {
\r
364 public void setFontSize(int fontSize) {
\r
365 this.fontSize = fontSize;
\r
368 public void draw(Graphics g1, int width, int height) {
\r
370 Graphics2D g2 = (Graphics2D)g1;
\r
371 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
\r
372 g2.setColor(Color.white);
\r
373 g2.fillRect(0,0,width,height);
\r
376 labelLength = g2.getFontMetrics(font).stringWidth(longestName)+ 20;//20 allows for scrollbar
\r
378 float wscale =(float)(width - labelLength -offx*2)/tree.getMaxHeight();
\r
380 SequenceNode top = tree.getTopNode();
\r
382 if (top.count == 0) {
\r
383 top.count = ((SequenceNode)top.left()).count + ((SequenceNode)top.right()).count ;
\r
385 float chunk = (float)(height-offy*2)/top.count ;
\r
387 drawNode(g2,tree.getTopNode(),chunk,wscale,width,offx,offy);
\r
389 if (threshold != 0)
\r
391 if(av.getCurrentTree() == tree)
\r
392 g2.setColor(Color.red);
\r
394 g2.setColor(Color.gray);
\r
396 int x = (int)( threshold * (float)(getWidth()-labelLength - 2*offx) +offx ) ;
\r
398 g2.drawLine(x,0,x,getHeight());
\r
403 public void mouseReleased(MouseEvent e) { }
\r
404 public void mouseEntered(MouseEvent e) { }
\r
405 public void mouseExited(MouseEvent e) { }
\r
406 public void mouseClicked(MouseEvent e) {
\r
409 public void mousePressed(MouseEvent e) {
\r
411 av.setCurrentTree(tree);
\r
416 Object ob = findElement(x,y);
\r
418 if (ob instanceof SequenceI)
\r
420 TreeSelectionChanged((Sequence)ob);
\r
424 } else if (ob instanceof SequenceNode) {
\r
425 SequenceNode tmpnode = (SequenceNode)ob;
\r
426 tree.swapNodes(tmpnode);
\r
427 tree.reCount(tree.getTopNode());
\r
428 tree.findHeight(tree.getTopNode());
\r
432 if (tree.getMaxHeight() != 0) {
\r
433 threshold = (float)(x - offx)/(float)(getWidth()-labelLength - 2*offx);
\r
435 tree.getGroups().removeAllElements();
\r
436 tree.groupNodes(tree.getTopNode(),threshold);
\r
437 setColor(tree.getTopNode(),Color.black);
\r
439 av.setSelectionGroup(null);
\r
440 av.alignment.deleteAllGroups();
\r
442 for (int i=0; i < tree.getGroups().size(); i++)
\r
445 Color col = new Color((int)(Math.random()*255),
\r
446 (int)(Math.random()*255),
\r
447 (int)(Math.random()*255));
\r
448 setColor((SequenceNode)tree.getGroups().elementAt(i),col.brighter());
\r
450 Vector l = tree.findLeaves((SequenceNode)tree.getGroups().elementAt(i),new Vector());
\r
451 SequenceGroup sg = null;
\r
452 for (int j = 0; j < l.size(); j++)
\r
454 SequenceNode sn = (SequenceNode) l.elementAt(j);
\r
456 sg = new SequenceGroup("TreeGroup", av.getGlobalColourScheme(), true, true,false,0,av.alignment.getWidth());
\r
458 sg.addSequence( (Sequence) sn.element());
\r
461 if (av.getGlobalColourScheme() instanceof ConservationColourScheme)
\r
463 ConservationColourScheme ccs = (ConservationColourScheme) av.getGlobalColourScheme();
\r
464 Conservation c = new Conservation("Group",
\r
465 ResidueProperties.propHash, 3,
\r
466 sg.sequences, sg.getStartRes(),
\r
470 c.verdict(false, av.ConsPercGaps);
\r
471 ccs = new ConservationColourScheme(c, ccs.cs);
\r
479 av.alignment.addGroup(sg);
\r
485 PaintRefresher.Refresh(this);
\r
490 public void setShowDistances(boolean state) {
\r
491 this.showDistances = state;
\r
495 public void setShowBootstrap(boolean state) {
\r
496 this.showBootstrap = state;
\r