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