End res of selection group corrected
[jalview.git] / src / jalview / appletgui / TreeCanvas.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2005 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
4  *\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
9  *\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
14  *\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
18  */\r
19 \r
20 package jalview.appletgui;\r
21 \r
22 import java.util.*;\r
23 \r
24 import java.awt.*;\r
25 import java.awt.event.*;\r
26 \r
27 import jalview.analysis.*;\r
28 import jalview.datamodel.*;\r
29 import jalview.schemes.*;\r
30 import jalview.util.*;\r
31 \r
32 public class TreeCanvas\r
33     extends Panel implements MouseListener\r
34 {\r
35   NJTree tree;\r
36   ScrollPane scrollPane;\r
37   AlignViewport av;\r
38   public static final String PLACEHOLDER = " * ";\r
39   Font font;\r
40   int fontSize = 12;\r
41 \r
42   boolean fitToWindow = true;\r
43   boolean showDistances = false;\r
44   boolean showBootstrap = false;\r
45   boolean markPlaceholders = false;\r
46 \r
47   int offx = 20;\r
48   int offy = 20;\r
49 \r
50   float threshold;\r
51 \r
52   String longestName;\r
53   int labelLength = -1;\r
54 \r
55   //RubberbandRectangle rubberband;\r
56 \r
57   Vector listeners;\r
58 \r
59   Hashtable nameHash = new Hashtable();\r
60   Hashtable nodeHash = new Hashtable();\r
61 \r
62   public TreeCanvas(AlignViewport av, NJTree tree, ScrollPane scroller,\r
63                     String label)\r
64   {\r
65     this.av = av;\r
66     this.tree = tree;\r
67     scrollPane = scroller;\r
68     addMouseListener(this);\r
69     tree.findHeight(tree.getTopNode());\r
70     longestName = label;\r
71     setLayout(null);\r
72 \r
73     PaintRefresher.Register(this, av.alignment);\r
74   }\r
75 \r
76   public void TreeSelectionChanged(Sequence sequence)\r
77   {\r
78     SequenceGroup selected = av.getSelectionGroup();\r
79     if (selected == null)\r
80     {\r
81       selected = new SequenceGroup();\r
82       av.setSelectionGroup(selected);\r
83     }\r
84 \r
85     selected.setEndRes(av.alignment.getWidth());\r
86     selected.addOrRemove(sequence, true);\r
87 \r
88 System.out.println("called here");\r
89     PaintRefresher.Refresh(this, av.alignment);\r
90     repaint();\r
91   }\r
92 \r
93   public void setTree(NJTree tree)\r
94   {\r
95     this.tree = tree;\r
96     tree.findHeight(tree.getTopNode());\r
97   }\r
98 \r
99   public void drawNode(Graphics g, SequenceNode node, float chunk, float scale,\r
100                        int width, int offx, int offy)\r
101   {\r
102     if (node == null)\r
103     {\r
104       return;\r
105     }\r
106 \r
107     if (node.left() == null && node.right() == null)\r
108     {\r
109       // Drawing leaf node\r
110 \r
111       float height = node.height;\r
112       float dist = node.dist;\r
113 \r
114       int xstart = (int) ( (height - dist) * scale) + offx;\r
115       int xend = (int) (height * scale) + offx;\r
116 \r
117       int ypos = (int) (node.ycount * chunk) + offy;\r
118 \r
119       if (node.element() instanceof SequenceI)\r
120       {\r
121         if ( ( (SequenceI) ( (SequenceNode) node).element()).getColor() ==\r
122             Color.white)\r
123         {\r
124           g.setColor(Color.black);\r
125         }\r
126         else\r
127         {\r
128           g.setColor( ( (SequenceI) ( (SequenceNode) node).element()).getColor().\r
129                      darker());\r
130         }\r
131 \r
132       }\r
133       else\r
134       {\r
135         g.setColor(Color.black);\r
136       }\r
137 \r
138       // Draw horizontal line\r
139       g.drawLine(xstart, ypos, xend, ypos);\r
140 \r
141       String nodeLabel = "";\r
142       if (showDistances && node.dist > 0)\r
143       {\r
144         nodeLabel = new Format("%5.2f").form(node.dist);\r
145       }\r
146       if (showBootstrap)\r
147       {\r
148         if (showDistances)\r
149         {\r
150           nodeLabel = nodeLabel + " : ";\r
151         }\r
152         nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());\r
153       }\r
154       if (!nodeLabel.equals(""))\r
155       {\r
156         g.drawString(nodeLabel, xstart, ypos - 10);\r
157       }\r
158 \r
159       String name = (markPlaceholders && node.isPlaceholder()) ?\r
160           (PLACEHOLDER + node.getName()) : node.getName();\r
161       FontMetrics fm = g.getFontMetrics(font);\r
162       int charWidth = fm.stringWidth(name) + 3;\r
163       int charHeight = fm.getHeight();\r
164 \r
165       Rectangle rect = new Rectangle(xend + 20, ypos - charHeight,\r
166                                      charWidth, charHeight);\r
167 \r
168       nameHash.put( (SequenceI) node.element(), rect);\r
169 \r
170       // Colour selected leaves differently\r
171       SequenceGroup selected = av.getSelectionGroup();\r
172       if (selected != null &&\r
173           selected.sequences.contains( (SequenceI) node.element()))\r
174       {\r
175         g.setColor(Color.gray);\r
176 \r
177         g.fillRect(xend + 10, ypos - charHeight + 3, charWidth, charHeight);\r
178         g.setColor(Color.white);\r
179       }\r
180       g.drawString(name, xend + 10, ypos);\r
181       g.setColor(Color.black);\r
182     }\r
183     else\r
184     {\r
185       drawNode(g, (SequenceNode) node.left(), chunk, scale, width, offx, offy);\r
186       drawNode(g, (SequenceNode) node.right(), chunk, scale, width, offx, offy);\r
187 \r
188       float height = node.height;\r
189       float dist = node.dist;\r
190 \r
191       int xstart = (int) ( (height - dist) * scale) + offx;\r
192       int xend = (int) (height * scale) + offx;\r
193       int ypos = (int) (node.ycount * chunk) + offy;\r
194 \r
195       g.setColor( ( (SequenceNode) node).color.darker());\r
196 \r
197       // Draw horizontal line\r
198       g.drawLine(xstart, ypos, xend, ypos);\r
199       g.fillRect(xend - 2, ypos - 2, 4, 4);\r
200 \r
201       int ystart = (int) ( ( (SequenceNode) node.left()).ycount * chunk) + offy;\r
202       int yend = (int) ( ( (SequenceNode) node.right()).ycount * chunk) + offy;\r
203 \r
204       Rectangle pos = new Rectangle(xend - 2, ypos - 2, 5, 5);\r
205       nodeHash.put(node, pos);\r
206 \r
207       g.drawLine( (int) (height * scale) + offx, ystart,\r
208                  (int) (height * scale) + offx, yend);\r
209 \r
210       if (showDistances && node.dist > 0)\r
211       {\r
212         g.drawString(new Format("%5.2f").form(node.dist), xstart, ypos - 5);\r
213       }\r
214 \r
215     }\r
216   }\r
217 \r
218   public Object findElement(int x, int y)\r
219   {\r
220     Enumeration keys = nameHash.keys();\r
221 \r
222     while (keys.hasMoreElements())\r
223     {\r
224       Object ob = keys.nextElement();\r
225       Rectangle rect = (Rectangle) nameHash.get(ob);\r
226 \r
227       if (x >= rect.x && x <= (rect.x + rect.width) &&\r
228           y >= rect.y && y <= (rect.y + rect.height))\r
229       {\r
230         return ob;\r
231       }\r
232     }\r
233     keys = nodeHash.keys();\r
234 \r
235     while (keys.hasMoreElements())\r
236     {\r
237       Object ob = keys.nextElement();\r
238       Rectangle rect = (Rectangle) nodeHash.get(ob);\r
239 \r
240       if (x >= rect.x && x <= (rect.x + rect.width) &&\r
241           y >= rect.y && y <= (rect.y + rect.height))\r
242       {\r
243         return ob;\r
244       }\r
245     }\r
246     return null;\r
247 \r
248   }\r
249 \r
250   public void pickNodes(Rectangle pickBox)\r
251   {\r
252     int width = getSize().width;\r
253     int height = getSize().height;\r
254 \r
255     SequenceNode top = tree.getTopNode();\r
256 \r
257     float wscale = (float) (width * .8 - offx * 2) / tree.getMaxHeight()\r
258         ;\r
259     if (top.count == 0)\r
260     {\r
261       top.count = ( (SequenceNode) top.left()).count +\r
262           ( (SequenceNode) top.right()).count;\r
263     }\r
264     float chunk = (float) (height - offy * 2) / top.count;\r
265 \r
266     pickNode(pickBox, top, chunk, wscale, width, offx, offy);\r
267   }\r
268 \r
269   public void pickNode(Rectangle pickBox, SequenceNode node, float chunk,\r
270                        float scale, int width, int offx, int offy)\r
271   {\r
272     if (node == null)\r
273     {\r
274       return;\r
275     }\r
276 \r
277     if (node.left() == null && node.right() == null)\r
278     {\r
279       float height = node.height;\r
280       float dist = node.dist;\r
281 \r
282       int xstart = (int) ( (height - dist) * scale) + offx;\r
283       int xend = (int) (height * scale) + offx;\r
284 \r
285       int ypos = (int) (node.ycount * chunk) + offy;\r
286 \r
287       if (pickBox.contains(new Point(xend, ypos)))\r
288       {\r
289         if (node.element() instanceof SequenceI)\r
290         {\r
291           SequenceI seq = (SequenceI) node.element();\r
292           SequenceGroup sg = av.getSelectionGroup();\r
293           if (sg != null)\r
294           {\r
295             sg.addOrRemove(seq, true);\r
296           }\r
297         }\r
298       }\r
299     }\r
300     else\r
301     {\r
302       pickNode(pickBox, (SequenceNode) node.left(), chunk, scale, width, offx,\r
303                offy);\r
304       pickNode(pickBox, (SequenceNode) node.right(), chunk, scale, width, offx,\r
305                offy);\r
306     }\r
307   }\r
308 \r
309   public void setColor(SequenceNode node, Color c)\r
310   {\r
311     if (node == null)\r
312     {\r
313       return;\r
314     }\r
315 \r
316     if (node.left() == null && node.right() == null)\r
317     {\r
318       node.color = c;\r
319 \r
320       if (node.element() instanceof SequenceI)\r
321       {\r
322         ( (SequenceI) node.element()).setColor(c);\r
323       }\r
324     }\r
325     else\r
326     {\r
327       node.color = c;\r
328       setColor( (SequenceNode) node.left(), c);\r
329       setColor( (SequenceNode) node.right(), c);\r
330     }\r
331   }\r
332 \r
333   public void paint(Graphics g)\r
334   {\r
335 \r
336     font = new Font("Verdana", Font.PLAIN, fontSize);\r
337     g.setFont(font);\r
338 \r
339     FontMetrics fm = g.getFontMetrics(font);\r
340 \r
341     if (nameHash.size() == 0)\r
342     {\r
343       repaint();\r
344     }\r
345 \r
346     if (fitToWindow ||\r
347         (!fitToWindow &&\r
348          scrollPane.getSize().height > fm.getHeight() * nameHash.size() + offy))\r
349     {\r
350       draw(g, scrollPane.getSize().width, scrollPane.getSize().height);\r
351     }\r
352     else\r
353     {\r
354       setSize(new Dimension(scrollPane.getSize().width,\r
355                             fm.getHeight() * nameHash.size()));\r
356       draw(g, scrollPane.getSize().width, fm.getHeight() * nameHash.size());\r
357     }\r
358 \r
359     scrollPane.validate();\r
360   }\r
361 \r
362   public int getFontSize()\r
363   {\r
364     return fontSize;\r
365   }\r
366 \r
367   public void setFontSize(int fontSize)\r
368   {\r
369     this.fontSize = fontSize;\r
370     repaint();\r
371   }\r
372 \r
373   public void draw(Graphics g, int width, int height)\r
374   {\r
375 \r
376     g.setColor(Color.white);\r
377     g.fillRect(0, 0, width, height);\r
378 \r
379     labelLength = g.getFontMetrics(font).stringWidth(longestName) + 20; //20 allows for scrollbar\r
380 \r
381     float wscale = (float) (width - labelLength - offx * 2) / tree.getMaxHeight();\r
382 \r
383     SequenceNode top = tree.getTopNode();\r
384 \r
385     if (top.count == 0)\r
386     {\r
387       top.count = ( (SequenceNode) top.left()).count +\r
388           ( (SequenceNode) top.right()).count;\r
389     }\r
390     float chunk = (float) (height - offy * 2) / top.count;\r
391 \r
392     drawNode(g, tree.getTopNode(), chunk, wscale, width, offx, offy);\r
393 \r
394     if (threshold != 0)\r
395     {\r
396       if (av.getCurrentTree() == tree)\r
397       {\r
398         g.setColor(Color.red);\r
399       }\r
400       else\r
401       {\r
402         g.setColor(Color.gray);\r
403       }\r
404 \r
405       int x = (int) (threshold *\r
406                      (float) (getSize().width - labelLength - 2 * offx) + offx);\r
407 \r
408       g.drawLine(x, 0, x, getSize().height);\r
409     }\r
410 \r
411   }\r
412 \r
413   public void mouseReleased(MouseEvent e)\r
414   {}\r
415 \r
416   public void mouseEntered(MouseEvent e)\r
417   {}\r
418 \r
419   public void mouseExited(MouseEvent e)\r
420   {}\r
421 \r
422   public void mouseClicked(MouseEvent e)\r
423   {\r
424   }\r
425 \r
426   public void mousePressed(MouseEvent e)\r
427   {\r
428     av.setCurrentTree(tree);\r
429 \r
430     int x = e.getX();\r
431     int y = e.getY();\r
432 \r
433     Object ob = findElement(x, y);\r
434 \r
435     if (ob instanceof SequenceI)\r
436     {\r
437       TreeSelectionChanged( (Sequence) ob);\r
438       repaint();\r
439       return;\r
440 \r
441     }\r
442     else if (ob instanceof SequenceNode)\r
443     {\r
444       SequenceNode tmpnode = (SequenceNode) ob;\r
445       tree.swapNodes(tmpnode);\r
446       tree.reCount(tree.getTopNode());\r
447       tree.findHeight(tree.getTopNode());\r
448     }\r
449     else\r
450     {\r
451       // Find threshold\r
452 \r
453       if (tree.getMaxHeight() != 0)\r
454       {\r
455         threshold = (float) (x - offx) /\r
456             (float) (getSize().width - labelLength - 2 * offx);\r
457 \r
458         tree.getGroups().removeAllElements();\r
459         tree.groupNodes(tree.getTopNode(), threshold);\r
460         setColor(tree.getTopNode(), Color.black);\r
461 \r
462         av.setSelectionGroup(null);\r
463         av.alignment.deleteAllGroups();\r
464 \r
465         for (int i = 0; i < tree.getGroups().size(); i++)\r
466         {\r
467 \r
468           Color col = new Color( (int) (Math.random() * 255),\r
469                                 (int) (Math.random() * 255),\r
470                                 (int) (Math.random() * 255));\r
471           setColor( (SequenceNode) tree.getGroups().elementAt(i), col.brighter());\r
472 \r
473           Vector l = tree.findLeaves( (SequenceNode) tree.getGroups().elementAt(\r
474               i), new Vector());\r
475 \r
476           Vector sequences = new Vector();\r
477           for (int j = 0; j < l.size(); j++)\r
478           {\r
479             SequenceI s1 = (SequenceI) ( (SequenceNode) l.elementAt(j)).element();\r
480             if(!sequences.contains(s1))\r
481               sequences.addElement(s1);\r
482           }\r
483 \r
484           ColourSchemeI cs = ColourSchemeProperty.getColour(sequences, av.alignment.getWidth(),\r
485               ColourSchemeProperty.getColourName(av.getGlobalColourScheme()));\r
486 \r
487 \r
488           SequenceGroup sg = new SequenceGroup(sequences, "TreeGroup",\r
489                                                cs, true, true,\r
490                                                false, 0, av.alignment.getWidth()-1);\r
491 \r
492 \r
493           if (  av.getGlobalColourScheme()!=null\r
494              && av.getGlobalColourScheme().conservationApplied())\r
495             {\r
496             Conservation c = new Conservation("Group",\r
497                                               ResidueProperties.propHash, 3,\r
498                                               sg.sequences, sg.getStartRes(),\r
499                                               sg.getEndRes());\r
500 \r
501             c.calculate();\r
502             c.verdict(false, av.ConsPercGaps);\r
503             cs.setConservation(c);\r
504 \r
505             sg.cs = cs;\r
506 \r
507           }\r
508 \r
509           av.alignment.addGroup(sg);\r
510 \r
511         }\r
512       }\r
513     }\r
514 \r
515     PaintRefresher.Refresh(this, av.alignment);\r
516     repaint();\r
517 \r
518   }\r
519 \r
520   public void setShowDistances(boolean state)\r
521   {\r
522     this.showDistances = state;\r
523     repaint();\r
524   }\r
525 \r
526   public void setShowBootstrap(boolean state)\r
527   {\r
528     this.showBootstrap = state;\r
529     repaint();\r
530   }\r
531 \r
532   public void setMarkPlaceholders(boolean state)\r
533   {\r
534     this.markPlaceholders = state;\r
535     repaint();\r
536   }\r
537 \r
538 }\r