Jalview Imported Sources
[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 javax.swing.*;\r
8 import java.awt.*;\r
9 import java.awt.event.*;\r
10 import java.util.*;\r
11 import java.io.*;\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           g.setColor(((SequenceI)((SequenceNode)node).element()).getColor().darker());\r
96       } else {\r
97           g.setColor(Color.black);\r
98       }\r
99 \r
100       // Draw horizontal line\r
101       g.drawLine(xstart,ypos,xend,ypos);\r
102 \r
103       String nodeLabel = "";\r
104       if (showDistances && node.dist > 0) {\r
105         nodeLabel = new Format("%5.2f").form(node.dist);\r
106       }\r
107       if (showBootstrap) {\r
108         if (showDistances) {\r
109           nodeLabel = nodeLabel + " : ";\r
110         }\r
111         nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());\r
112       }\r
113       if (! nodeLabel.equals("")) {\r
114       g.drawString(nodeLabel,xstart,ypos - 10);\r
115       }\r
116 \r
117       // Colour selected leaves differently\r
118       String name    = node.getName();\r
119       FontMetrics fm = g.getFontMetrics(font);\r
120       int charWidth  = fm.stringWidth(node.getName()) + 3;\r
121       int charHeight = fm.getHeight();\r
122 \r
123       Rectangle rect = new Rectangle(xend+20,ypos-charHeight,\r
124                                      charWidth,charHeight);\r
125 \r
126       nameHash.put((SequenceI)node.element(),rect);\r
127 \r
128       if (selected.contains((SequenceI)node.element())) {\r
129         g.setColor(Color.gray);\r
130 \r
131         g.fillRect(xend + 10, ypos - charHeight + 3,charWidth,charHeight);\r
132         g.setColor(Color.white);\r
133       }\r
134       g.drawString(node.getName(),xend+10,ypos);\r
135       g.setColor(Color.black);\r
136     } else {\r
137       drawNode(g,(SequenceNode)node.left(), chunk,scale,width,offx,offy);\r
138       drawNode(g,(SequenceNode)node.right(),chunk,scale,width,offx,offy);\r
139 \r
140       float height = node.height;\r
141       float dist   = node.dist;\r
142 \r
143       int xstart = (int)((height-dist)*scale) + offx;\r
144       int xend   = (int)(height       *scale) + offx;\r
145       int ypos   = (int)(node.ycount  *chunk) + offy;\r
146 \r
147       g.setColor(((SequenceNode)node).color.darker());\r
148 \r
149       // Draw horizontal line\r
150       g.drawLine(xstart,ypos,xend,ypos);\r
151       g.fillRect(xend-2, ypos-2, 4,4);\r
152 \r
153       int ystart = (int)(((SequenceNode)node.left()) .ycount * chunk) + offy;\r
154       int yend   = (int)(((SequenceNode)node.right()).ycount * chunk) + offy;\r
155 \r
156       Rectangle pos = new Rectangle(xend-2,ypos-2,5,5);\r
157       nodeHash.put(node,pos);\r
158 \r
159       g.drawLine((int)(height*scale) + offx, ystart,\r
160                  (int)(height*scale) + offx, yend);\r
161 \r
162       if (showDistances && node.dist > 0) {\r
163         g.drawString(new Format("%5.2f").form(node.dist),xstart,ypos - 5);\r
164       }\r
165 \r
166     }\r
167   }\r
168   public Object findElement(int x, int y) {\r
169        Enumeration keys = nameHash.keys();\r
170 \r
171     while (keys.hasMoreElements()) {\r
172             Object ob = keys.nextElement();\r
173             Rectangle rect = (Rectangle)nameHash.get(ob);\r
174 \r
175             if (x >= rect.x && x <= (rect.x + rect.width) &&\r
176               y >= rect.y && y <= (rect.y + rect.height)) {\r
177               return ob;\r
178             }\r
179   }\r
180   keys = nodeHash.keys();\r
181 \r
182     while (keys.hasMoreElements()) {\r
183             Object ob = keys.nextElement();\r
184             Rectangle rect = (Rectangle)nodeHash.get(ob);\r
185 \r
186             if (x >= rect.x && x <= (rect.x + rect.width) &&\r
187               y >= rect.y && y <= (rect.y + rect.height)) {\r
188               return ob;\r
189             }\r
190     }\r
191       return null;\r
192 \r
193   }\r
194 \r
195   public void pickNodes(Rectangle pickBox, Selection sel) {\r
196     int width  = getWidth();\r
197     int height = getHeight();\r
198 \r
199     SequenceNode top = tree.getTopNode();\r
200 \r
201     float wscale = (float)(width*.8-offx*2)/tree.getMaxHeight()\r
202 ;\r
203     if (top.count == 0) {\r
204       top.count = ((SequenceNode)top.left()).count + ((SequenceNode)top.right()).count ;\r
205     }\r
206     float chunk = (float)(height-offy*2)/top.count;\r
207 \r
208     pickNode(pickBox,sel,top,chunk,wscale,width,offx,offy);\r
209   }\r
210 \r
211   public void pickNode(Rectangle pickBox, Selection sel, SequenceNode node, float chunk, float scale, int width,int offx, int offy) {\r
212     if (node == null) {\r
213       return;\r
214     }\r
215 \r
216     if (node.left() == null && node.right() == null) {\r
217       float height = node.height;\r
218       float dist   = node.dist;\r
219 \r
220       int xstart = (int)((height-dist)*scale) + offx;\r
221       int xend   = (int)(height*scale) + offx;\r
222 \r
223       int ypos = (int)(node.ycount * chunk) + offy;\r
224 \r
225       if (pickBox.contains(new Point(xend,ypos))) {\r
226         if (node.element() instanceof SequenceI) {\r
227           SequenceI seq = (SequenceI)node.element();\r
228           if (sel.contains(seq)) {\r
229             sel.removeElement(seq);\r
230           } else {\r
231             sel.addElement(seq);\r
232           }\r
233         }\r
234       }\r
235     } else {\r
236       pickNode(pickBox,sel,(SequenceNode)node.left(), chunk,scale,width,offx,offy);\r
237       pickNode(pickBox,sel,(SequenceNode)node.right(),chunk,scale,width,offx,offy);\r
238     }\r
239   }\r
240 \r
241   public void setColor(SequenceNode node, Color c) {\r
242     if (node == null) {\r
243       return;\r
244     }\r
245 \r
246     if (node.left() == null && node.right() == null) {\r
247       node.color = c;\r
248 \r
249       if (node.element() instanceof SequenceI) {\r
250           ((SequenceI)node.element()).setColor(c);\r
251       }\r
252     } else {\r
253       node.color = c;\r
254       setColor((SequenceNode)node.left(),c);\r
255       setColor((SequenceNode)node.right(),c);\r
256     }\r
257   }\r
258 \r
259 \r
260   public void paintComponent(Graphics g) {\r
261 \r
262 \r
263     font = new Font("Verdana",Font.PLAIN,fontSize);\r
264     g.setFont(font);\r
265 \r
266     FontMetrics fm = g.getFontMetrics(font);\r
267 \r
268     if(nameHash.size()==0)\r
269       repaint();\r
270 \r
271 \r
272     if( scrollPane.getHeight() > fm.getHeight() * nameHash.size()+offy)\r
273      {\r
274          draw(g,scrollPane.getWidth(),scrollPane.getHeight());\r
275          setPreferredSize(new Dimension(scrollPane.getWidth(), scrollPane.getHeight()));\r
276      }\r
277     else\r
278      {\r
279          setPreferredSize(new Dimension(getWidth(), fm.getHeight() * nameHash.size()));\r
280          draw( g,getWidth(), fm.getHeight() * nameHash.size());\r
281      }\r
282 \r
283 \r
284     if (threshold != 0)\r
285     {\r
286         g.setColor(Color.red);\r
287         g.drawLine(threshold,0,threshold,getHeight());\r
288     }\r
289 \r
290     scrollPane.revalidate();\r
291   }\r
292     public int getFontSize() {\r
293         return fontSize;\r
294     }\r
295     public void setFontSize(int fontSize) {\r
296         this.fontSize = fontSize;\r
297         repaint();\r
298     }\r
299   public void draw(Graphics g, int width, int height) {\r
300       g.setColor(Color.white);\r
301       g.fillRect(0,0,width,height);\r
302 \r
303 \r
304       labelLength = g.getFontMetrics(font).stringWidth(longestName)+ 20;//20 allows for scrollbar\r
305 \r
306       float wscale =(float)(width - labelLength -offx*2)/tree.getMaxHeight();\r
307 \r
308       SequenceNode top = tree.getTopNode();\r
309 \r
310       if (top.count == 0) {\r
311           top.count = ((SequenceNode)top.left()).count + ((SequenceNode)top.right()).count ;\r
312       }\r
313       float chunk = (float)(height-offy*2)/top.count ;\r
314 \r
315       drawNode(g,tree.getTopNode(),chunk,wscale,width,offx,offy);\r
316   }\r
317 \r
318   public void mouseReleased(MouseEvent e) { }\r
319   public void mouseEntered(MouseEvent e) { }\r
320   public void mouseExited(MouseEvent e) { }\r
321   public void mouseClicked(MouseEvent e) {\r
322   }\r
323 \r
324   public void mousePressed(MouseEvent e) {\r
325       int x = e.getX();\r
326       int y = e.getY();\r
327 \r
328       Object ob = findElement(x,y);\r
329 \r
330       if (ob instanceof SequenceI)\r
331       {\r
332           TreeSelectionChanged((Sequence)ob);\r
333           repaint();\r
334           return;\r
335 \r
336       } else if (ob instanceof SequenceNode) {\r
337           SequenceNode tmpnode = (SequenceNode)ob;\r
338           tree.swapNodes(tmpnode);\r
339           tree.reCount(tree.getTopNode());\r
340           tree.findHeight(tree.getTopNode());\r
341       } else {\r
342           // Find threshold\r
343 \r
344           if (tree.getMaxHeight() != 0) {\r
345               float fthreshold = (float)(x - offx)/(float)(getWidth()-labelLength - 2*offx);\r
346               this.threshold = x;\r
347               tree.getGroups().removeAllElements();\r
348               tree.groupNodes(tree.getTopNode(),fthreshold);\r
349               setColor(tree.getTopNode(),Color.black);\r
350 \r
351               for (int i=0; i < tree.getGroups().size(); i++) {\r
352 \r
353                   int tmp = i%(7);\r
354                   Color col = new Color((int)(Math.random()*255),\r
355                                         (int)(Math.random()*255),\r
356                                         (int)(Math.random()*255));\r
357 \r
358                   setColor((SequenceNode)tree.getGroups().elementAt(i),col.brighter());\r
359 \r
360                   // l is vector of Objects\r
361                   Vector l = tree.findLeaves((SequenceNode)tree.getGroups().elementAt(i),new Vector());\r
362 \r
363               }\r
364           }\r
365       }\r
366 \r
367       repaint();\r
368 \r
369   }\r
370 \r
371     public void setShowDistances(boolean state) {\r
372         this.showDistances = state;\r
373         repaint();\r
374     }\r
375 \r
376     public void setShowBootstrap(boolean state) {\r
377       this.showBootstrap = state;\r
378       repaint();\r
379     }\r
380 \r
381 }\r
382 \r