tree is printable
[jalview.git] / src / jalview / gui / TreeCanvas.java
1 package jalview.gui;\r
2 \r
3 \r
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
9 import java.awt.*;\r
10 import java.awt.event.*;\r
11 import java.util.*;\r
12 import java.awt.print.*;\r
13 \r
14 public class TreeCanvas extends JPanel implements MouseListener, Runnable, Printable\r
15 {\r
16   NJTree tree;\r
17   JScrollPane scrollPane;\r
18   AlignViewport av;\r
19 \r
20   Font font;\r
21   int  fontSize = 12;\r
22 \r
23   boolean showDistances = false;\r
24   boolean showBootstrap = false;\r
25 \r
26   int offx = 20;\r
27   int offy = 20;\r
28 \r
29   int threshold;\r
30 \r
31   String longestName;\r
32   int labelLength=-1;\r
33 \r
34   //RubberbandRectangle rubberband;\r
35 \r
36   Selection selected;\r
37   Vector    listeners;\r
38 \r
39   Hashtable nameHash = new Hashtable();\r
40   Hashtable nodeHash = new Hashtable();\r
41 \r
42   public TreeCanvas(AlignViewport av, NJTree tree, JScrollPane scroller, String label)\r
43   {\r
44     this.av = av;\r
45     this.tree     = tree;\r
46     selected = av.getSelection();\r
47     scrollPane = scroller;\r
48     addMouseListener(this);\r
49     tree.findHeight(tree.getTopNode());\r
50     longestName = label;\r
51 \r
52     PaintRefresher.Register(this);\r
53   }\r
54   public void TreeSelectionChanged(Sequence sequence)\r
55  {\r
56     selected = av.getSelection();\r
57 \r
58     if (selected.contains(sequence))\r
59         selected.removeElement(sequence);\r
60     else\r
61         selected.addElement(sequence);\r
62 \r
63     setSelected(selected);\r
64     PaintRefresher.Refresh(this);\r
65     repaint();\r
66  }\r
67 \r
68 \r
69   public void setSelected(Selection selected)\r
70   {\r
71         this.selected = selected;\r
72   }\r
73 \r
74   public void setTree(NJTree tree) {\r
75     this.tree = tree;\r
76     tree.findHeight(tree.getTopNode());\r
77   }\r
78 \r
79     public void drawNode(Graphics g,SequenceNode node, float chunk, float scale, int width,int offx, int offy) {\r
80     if (node == null) {\r
81       return;\r
82     }\r
83 \r
84     if (node.left() == null && node.right() == null) {\r
85       // Drawing leaf node\r
86 \r
87       float height = node.height;\r
88       float dist   = node.dist;\r
89 \r
90       int xstart = (int)((height-dist)*scale) + offx;\r
91       int xend =   (int)(height*scale)        + offx;\r
92 \r
93       int ypos = (int)(node.ycount * chunk) + offy;\r
94 \r
95       if (node.element() instanceof SequenceI)\r
96       {\r
97        if ( ( (SequenceI) ( (SequenceNode) node).element()).getColor() == Color.white)\r
98        {\r
99          g.setColor(Color.black);\r
100        }\r
101        else\r
102         g.setColor( ( (SequenceI) ( (SequenceNode) node).element()).getColor().\r
103                    darker());\r
104 \r
105       }\r
106       else\r
107           g.setColor(Color.black);\r
108 \r
109 \r
110       // Draw horizontal line\r
111       g.drawLine(xstart,ypos,xend,ypos);\r
112 \r
113       String nodeLabel = "";\r
114       if (showDistances && node.dist > 0) {\r
115         nodeLabel = new Format("%5.2f").form(node.dist);\r
116       }\r
117       if (showBootstrap) {\r
118         if (showDistances) {\r
119           nodeLabel = nodeLabel + " : ";\r
120         }\r
121         nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());\r
122       }\r
123       if (! nodeLabel.equals("")) {\r
124       g.drawString(nodeLabel,xstart,ypos - 10);\r
125       }\r
126 \r
127       // Colour selected leaves differently\r
128       String name    = node.getName();\r
129       FontMetrics fm = g.getFontMetrics(font);\r
130       int charWidth  = fm.stringWidth(node.getName()) + 3;\r
131       int charHeight = fm.getHeight();\r
132 \r
133       Rectangle rect = new Rectangle(xend+20,ypos-charHeight,\r
134                                      charWidth,charHeight);\r
135 \r
136       nameHash.put((SequenceI)node.element(),rect);\r
137 \r
138       if (selected.contains((SequenceI)node.element())) {\r
139         g.setColor(Color.gray);\r
140 \r
141         g.fillRect(xend + 10, ypos - charHeight + 3,charWidth,charHeight);\r
142         g.setColor(Color.white);\r
143       }\r
144       g.drawString(node.getName(),xend+10,ypos);\r
145       g.setColor(Color.black);\r
146     } else {\r
147       drawNode(g,(SequenceNode)node.left(), chunk,scale,width,offx,offy);\r
148       drawNode(g,(SequenceNode)node.right(),chunk,scale,width,offx,offy);\r
149 \r
150       float height = node.height;\r
151       float dist   = node.dist;\r
152 \r
153       int xstart = (int)((height-dist)*scale) + offx;\r
154       int xend   = (int)(height       *scale) + offx;\r
155       int ypos   = (int)(node.ycount  *chunk) + offy;\r
156 \r
157       g.setColor(((SequenceNode)node).color.darker());\r
158 \r
159       // Draw horizontal line\r
160       g.drawLine(xstart,ypos,xend,ypos);\r
161       g.fillRect(xend-2, ypos-2, 4,4);\r
162 \r
163       int ystart = (int)(((SequenceNode)node.left()) .ycount * chunk) + offy;\r
164       int yend   = (int)(((SequenceNode)node.right()).ycount * chunk) + offy;\r
165 \r
166       Rectangle pos = new Rectangle(xend-2,ypos-2,5,5);\r
167       nodeHash.put(node,pos);\r
168 \r
169       g.drawLine((int)(height*scale) + offx, ystart,\r
170                  (int)(height*scale) + offx, yend);\r
171 \r
172       if (showDistances && node.dist > 0) {\r
173         g.drawString(new Format("%5.2f").form(node.dist),xstart,ypos - 5);\r
174       }\r
175 \r
176     }\r
177   }\r
178   public Object findElement(int x, int y) {\r
179        Enumeration keys = nameHash.keys();\r
180 \r
181     while (keys.hasMoreElements()) {\r
182             Object ob = keys.nextElement();\r
183             Rectangle rect = (Rectangle)nameHash.get(ob);\r
184 \r
185             if (x >= rect.x && x <= (rect.x + rect.width) &&\r
186               y >= rect.y && y <= (rect.y + rect.height)) {\r
187               return ob;\r
188             }\r
189   }\r
190   keys = nodeHash.keys();\r
191 \r
192     while (keys.hasMoreElements()) {\r
193             Object ob = keys.nextElement();\r
194             Rectangle rect = (Rectangle)nodeHash.get(ob);\r
195 \r
196             if (x >= rect.x && x <= (rect.x + rect.width) &&\r
197               y >= rect.y && y <= (rect.y + rect.height)) {\r
198               return ob;\r
199             }\r
200     }\r
201       return null;\r
202 \r
203   }\r
204 \r
205   public void pickNodes(Rectangle pickBox, Selection sel) {\r
206     int width  = getWidth();\r
207     int height = getHeight();\r
208 \r
209     SequenceNode top = tree.getTopNode();\r
210 \r
211     float wscale = (float)(width*.8-offx*2)/tree.getMaxHeight()\r
212 ;\r
213     if (top.count == 0) {\r
214       top.count = ((SequenceNode)top.left()).count + ((SequenceNode)top.right()).count ;\r
215     }\r
216     float chunk = (float)(height-offy*2)/top.count;\r
217 \r
218     pickNode(pickBox,sel,top,chunk,wscale,width,offx,offy);\r
219   }\r
220 \r
221   public void pickNode(Rectangle pickBox, Selection sel, SequenceNode node, float chunk, float scale, int width,int offx, int offy) {\r
222     if (node == null) {\r
223       return;\r
224     }\r
225 \r
226     if (node.left() == null && node.right() == null) {\r
227       float height = node.height;\r
228       float dist   = node.dist;\r
229 \r
230       int xstart = (int)((height-dist)*scale) + offx;\r
231       int xend   = (int)(height*scale) + offx;\r
232 \r
233       int ypos = (int)(node.ycount * chunk) + offy;\r
234 \r
235       if (pickBox.contains(new Point(xend,ypos))) {\r
236         if (node.element() instanceof SequenceI) {\r
237           SequenceI seq = (SequenceI)node.element();\r
238           if (sel.contains(seq)) {\r
239             sel.removeElement(seq);\r
240           } else {\r
241             sel.addElement(seq);\r
242           }\r
243         }\r
244       }\r
245     } else {\r
246       pickNode(pickBox,sel,(SequenceNode)node.left(), chunk,scale,width,offx,offy);\r
247       pickNode(pickBox,sel,(SequenceNode)node.right(),chunk,scale,width,offx,offy);\r
248     }\r
249   }\r
250 \r
251   public void setColor(SequenceNode node, Color c)\r
252   {\r
253     if (node == null)\r
254       return;\r
255 \r
256     if (node.left() == null && node.right() == null)\r
257     {\r
258       node.color = c;\r
259 \r
260       if (node.element() instanceof SequenceI)\r
261           ((SequenceI)node.element()).setColor(c);\r
262     } else\r
263     {\r
264       node.color = c;\r
265       setColor((SequenceNode)node.left(),c);\r
266       setColor((SequenceNode)node.right(),c);\r
267     }\r
268   }\r
269 \r
270   void startPrinting()\r
271   {\r
272     Thread thread = new Thread(this);\r
273     thread.start();\r
274   }\r
275 \r
276   // put printing in a thread to avoid painting problems\r
277   public void run()\r
278   {\r
279     PrinterJob printJob = PrinterJob.getPrinterJob();\r
280     PageFormat pf = printJob.pageDialog(printJob.defaultPage());\r
281     printJob.setPrintable(this, pf);\r
282     if (printJob.printDialog())\r
283     {\r
284       try\r
285       {\r
286         printJob.print();\r
287       }\r
288       catch (Exception PrintException)\r
289       {\r
290         PrintException.printStackTrace();\r
291       }\r
292     }\r
293   }\r
294 \r
295 \r
296   public int print(Graphics pg, PageFormat pf, int pi) throws PrinterException\r
297   {\r
298     font = new Font("Verdana",Font.PLAIN,fontSize);\r
299     pg.setFont(font);\r
300     FontMetrics fm = pg.getFontMetrics(font);\r
301 \r
302     pg.translate((int)pf.getImageableX(), (int)pf.getImageableY());\r
303 \r
304     int pwidth = (int) pf.getImageableWidth();\r
305     int pheight = (int) pf.getImageableHeight();\r
306 \r
307     // adjust pheight to row height\r
308     pheight -= (pheight%fm.getHeight());\r
309     pg.setClip(0,0, pwidth, pheight);\r
310 \r
311     int noPages = (fm.getHeight() * nameHash.size()) / pheight;\r
312 \r
313     if(pi>noPages)\r
314       return Printable.NO_SUCH_PAGE;\r
315 \r
316     pg.translate(0, -pheight*pi );\r
317     draw(pg, pwidth, fm.getHeight() * nameHash.size());\r
318 \r
319     return Printable.PAGE_EXISTS;\r
320 \r
321   }\r
322 \r
323   public void paintComponent(Graphics g)\r
324   {\r
325     font = new Font("Verdana",Font.PLAIN,fontSize);\r
326     g.setFont(font);\r
327 \r
328     FontMetrics fm = g.getFontMetrics(font);\r
329 \r
330     if(nameHash.size()==0)\r
331       repaint();\r
332 \r
333 \r
334     if( scrollPane.getHeight() > fm.getHeight() * nameHash.size()+offy)\r
335      {\r
336          draw(g,scrollPane.getWidth(),scrollPane.getHeight());\r
337          setPreferredSize(new Dimension(scrollPane.getWidth(), scrollPane.getHeight()));\r
338      }\r
339     else\r
340      {\r
341          setPreferredSize(new Dimension(getWidth(), fm.getHeight() * nameHash.size()));\r
342          draw( g,getWidth(), fm.getHeight() * nameHash.size());\r
343      }\r
344 \r
345 \r
346     if (threshold != 0)\r
347     {\r
348         g.setColor(Color.red);\r
349         g.drawLine(threshold,0,threshold,getHeight());\r
350     }\r
351 \r
352     scrollPane.revalidate();\r
353   }\r
354     public int getFontSize() {\r
355         return fontSize;\r
356     }\r
357     public void setFontSize(int fontSize) {\r
358         this.fontSize = fontSize;\r
359         repaint();\r
360     }\r
361   public void draw(Graphics g, int width, int height) {\r
362       g.setColor(Color.white);\r
363       g.fillRect(0,0,width,height);\r
364 \r
365 \r
366       labelLength = g.getFontMetrics(font).stringWidth(longestName)+ 20;//20 allows for scrollbar\r
367 \r
368       float wscale =(float)(width - labelLength -offx*2)/tree.getMaxHeight();\r
369 \r
370       SequenceNode top = tree.getTopNode();\r
371 \r
372       if (top.count == 0) {\r
373           top.count = ((SequenceNode)top.left()).count + ((SequenceNode)top.right()).count ;\r
374       }\r
375       float chunk = (float)(height-offy*2)/top.count ;\r
376 \r
377       drawNode(g,tree.getTopNode(),chunk,wscale,width,offx,offy);\r
378   }\r
379 \r
380   public void mouseReleased(MouseEvent e) { }\r
381   public void mouseEntered(MouseEvent e) { }\r
382   public void mouseExited(MouseEvent e) { }\r
383   public void mouseClicked(MouseEvent e) {\r
384   }\r
385 \r
386   public void mousePressed(MouseEvent e) {\r
387       int x = e.getX();\r
388       int y = e.getY();\r
389 \r
390       Object ob = findElement(x,y);\r
391 \r
392       if (ob instanceof SequenceI)\r
393       {\r
394           TreeSelectionChanged((Sequence)ob);\r
395           repaint();\r
396           return;\r
397 \r
398       } else if (ob instanceof SequenceNode) {\r
399           SequenceNode tmpnode = (SequenceNode)ob;\r
400           tree.swapNodes(tmpnode);\r
401           tree.reCount(tree.getTopNode());\r
402           tree.findHeight(tree.getTopNode());\r
403       } else {\r
404           // Find threshold\r
405 \r
406           if (tree.getMaxHeight() != 0) {\r
407               float fthreshold = (float)(x - offx)/(float)(getWidth()-labelLength - 2*offx);\r
408               this.threshold = x;\r
409               tree.getGroups().removeAllElements();\r
410               tree.groupNodes(tree.getTopNode(),fthreshold);\r
411               setColor(tree.getTopNode(),Color.black);\r
412 \r
413               av.sel.clear();\r
414               av.alignment.deleteAllGroups();\r
415 \r
416               for (int i=0; i < tree.getGroups().size(); i++)\r
417               {\r
418 \r
419                   Color col = new Color((int)(Math.random()*255),\r
420                                         (int)(Math.random()*255),\r
421                                         (int)(Math.random()*255));\r
422                   setColor((SequenceNode)tree.getGroups().elementAt(i),col.brighter());\r
423 \r
424                   Vector l = tree.findLeaves((SequenceNode)tree.getGroups().elementAt(i),new Vector());\r
425                   SequenceGroup sg = null;\r
426                   for (int j = 0; j < l.size(); j++)\r
427                   {\r
428                     SequenceNode sn = (SequenceNode) l.elementAt(j);\r
429                     if(sg==null)\r
430                        sg  = new SequenceGroup("TreeGroup", av.getGlobalColourScheme(), true, true,false,0,av.alignment.getWidth());\r
431 \r
432                     sg.addSequence( (Sequence) sn.element());\r
433                   }\r
434 \r
435                   if (av.getGlobalColourScheme() instanceof ConservationColourScheme)\r
436                   {\r
437                     ConservationColourScheme ccs = (ConservationColourScheme) av.getGlobalColourScheme();\r
438                     Conservation c = new Conservation("Group",\r
439                                                       ResidueProperties.propHash, 3,\r
440                                                       sg.sequences, sg.getStartRes(),\r
441                                                       sg.getEndRes());\r
442 \r
443                     c.calculate();\r
444                     c.verdict(false, 100);\r
445                     ccs = new ConservationColourScheme(c, ccs.cs);\r
446 \r
447                     sg.cs = ccs;\r
448 \r
449                   }\r
450 \r
451 \r
452 \r
453                   av.alignment.addGroup(sg);\r
454 \r
455               }\r
456           }\r
457       }\r
458 \r
459       PaintRefresher.Refresh(this);\r
460       repaint();\r
461 \r
462   }\r
463 \r
464     public void setShowDistances(boolean state) {\r
465         this.showDistances = state;\r
466         repaint();\r
467     }\r
468 \r
469     public void setShowBootstrap(boolean state) {\r
470       this.showBootstrap = state;\r
471       repaint();\r
472     }\r
473 \r
474 }\r
475 \r