Tooltip on mouseover
[jalview.git] / src / jalview / gui / TreeCanvas.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2006 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 package jalview.gui;\r
20 \r
21 import jalview.analysis.*;\r
22 \r
23 import jalview.datamodel.*;\r
24 \r
25 import jalview.schemes.*;\r
26 \r
27 import jalview.util.*;\r
28 \r
29 import java.awt.*;\r
30 import java.awt.event.*;\r
31 import java.awt.print.*;\r
32 \r
33 import java.util.*;\r
34 \r
35 import javax.swing.*;\r
36 \r
37 \r
38 /**\r
39  * DOCUMENT ME!\r
40  *\r
41  * @author $author$\r
42  * @version $Revision$\r
43  */\r
44 public class TreeCanvas extends JPanel implements MouseListener, Runnable,\r
45     Printable, MouseMotionListener\r
46 {\r
47     /** DOCUMENT ME!! */\r
48     public static final String PLACEHOLDER = " * ";\r
49     NJTree tree;\r
50     JScrollPane scrollPane;\r
51     AlignViewport av;\r
52     AlignmentPanel ap;\r
53     Font font;\r
54     FontMetrics fm;\r
55     boolean fitToWindow = true;\r
56     boolean showDistances = false;\r
57     boolean showBootstrap = false;\r
58     boolean markPlaceholders = false;\r
59     int offx = 20;\r
60     int offy;\r
61     float threshold;\r
62     String longestName;\r
63     int labelLength = -1;\r
64 \r
65     Hashtable nameHash = new Hashtable();\r
66     Hashtable nodeHash = new Hashtable();\r
67     SequenceNode highlightNode;\r
68 \r
69     /**\r
70      * Creates a new TreeCanvas object.\r
71      *\r
72      * @param av DOCUMENT ME!\r
73      * @param tree DOCUMENT ME!\r
74      * @param scroller DOCUMENT ME!\r
75      * @param label DOCUMENT ME!\r
76      */\r
77     public TreeCanvas(AlignmentPanel ap, JScrollPane scroller)\r
78     {\r
79         this.av = ap.av;\r
80         this.ap = ap;\r
81         font = av.getFont();\r
82         scrollPane = scroller;\r
83         addMouseListener(this);\r
84         addMouseMotionListener(this);\r
85         PaintRefresher.Register(this, ap.av.getSequenceSetId());\r
86         ToolTipManager.sharedInstance().registerComponent(this);\r
87     }\r
88 \r
89 \r
90     /**\r
91      * DOCUMENT ME!\r
92      *\r
93      * @param sequence DOCUMENT ME!\r
94      */\r
95     public void treeSelectionChanged(SequenceI sequence)\r
96     {\r
97         SequenceGroup selected = av.getSelectionGroup();\r
98 \r
99         if (selected == null)\r
100         {\r
101             selected = new SequenceGroup();\r
102             av.setSelectionGroup(selected);\r
103         }\r
104 \r
105         selected.setEndRes(av.alignment.getWidth()-1);\r
106         selected.addOrRemove(sequence, true);\r
107     }\r
108 \r
109     /**\r
110      * DOCUMENT ME!\r
111      *\r
112      * @param tree DOCUMENT ME!\r
113      */\r
114     public void setTree(NJTree tree)\r
115     {\r
116         this.tree = tree;\r
117         tree.findHeight(tree.getTopNode());\r
118 \r
119         // Now have to calculate longest name based on the leaves\r
120         Vector leaves = tree.findLeaves(tree.getTopNode(), new Vector());\r
121         boolean has_placeholders = false;\r
122         longestName = "";\r
123 \r
124         for (int i = 0; i < leaves.size(); i++)\r
125         {\r
126           SequenceNode lf = (SequenceNode) leaves.elementAt(i);\r
127 \r
128           if (lf.isPlaceholder())\r
129           {\r
130             has_placeholders = true;\r
131           }\r
132 \r
133           if (longestName.length() < ( (Sequence) lf.element()).getName()\r
134               .length())\r
135           {\r
136             longestName = TreeCanvas.PLACEHOLDER +\r
137                 ( (Sequence) lf.element()).getName();\r
138           }\r
139         }\r
140 \r
141         setMarkPlaceholders(has_placeholders);\r
142     }\r
143 \r
144     /**\r
145      * DOCUMENT ME!\r
146      *\r
147      * @param g DOCUMENT ME!\r
148      * @param node DOCUMENT ME!\r
149      * @param chunk DOCUMENT ME!\r
150      * @param scale DOCUMENT ME!\r
151      * @param width DOCUMENT ME!\r
152      * @param offx DOCUMENT ME!\r
153      * @param offy DOCUMENT ME!\r
154      */\r
155     public void drawNode(Graphics g, SequenceNode node, float chunk,\r
156         float scale, int width, int offx, int offy)\r
157     {\r
158         if (node == null)\r
159         {\r
160             return;\r
161         }\r
162 \r
163         if ((node.left() == null) && (node.right() == null))\r
164         {\r
165             // Drawing leaf node\r
166             float height = node.height;\r
167             float dist = node.dist;\r
168 \r
169             int xstart = (int) ((height - dist) * scale) + offx;\r
170             int xend = (int) (height * scale) + offx;\r
171 \r
172             int ypos = (int) (node.ycount * chunk) + offy;\r
173 \r
174             if (node.element() instanceof SequenceI)\r
175             {\r
176                 if (((SequenceI) ((SequenceNode) node).element()).getColor() == Color.white)\r
177                 {\r
178                     g.setColor(Color.black);\r
179                 }\r
180                 else\r
181                 {\r
182                     g.setColor(((SequenceI) ((SequenceNode) node).element()).getColor()\r
183                                 .darker());\r
184                 }\r
185             }\r
186             else\r
187             {\r
188                 g.setColor(Color.black);\r
189             }\r
190 \r
191             // Draw horizontal line\r
192             g.drawLine(xstart, ypos, xend, ypos);\r
193 \r
194             String nodeLabel = "";\r
195 \r
196             if (showDistances && (node.dist > 0))\r
197             {\r
198                 nodeLabel = new Format("%-.2f").form(node.dist);\r
199             }\r
200 \r
201             if (showBootstrap)\r
202             {\r
203                 if (showDistances)\r
204                 {\r
205                     nodeLabel = nodeLabel + " : ";\r
206                 }\r
207 \r
208                 nodeLabel = nodeLabel + String.valueOf(node.getBootstrap());\r
209             }\r
210 \r
211             if (!nodeLabel.equals(""))\r
212             {\r
213                 g.drawString(nodeLabel, xstart+2, ypos - 2);\r
214             }\r
215 \r
216             String name = (markPlaceholders && node.isPlaceholder())\r
217                 ? (PLACEHOLDER + node.getName()) : node.getName();\r
218 \r
219             int charWidth = fm.stringWidth(name) + 3;\r
220             int charHeight = font.getSize();\r
221 \r
222             Rectangle rect = new Rectangle(xend+10, ypos-charHeight/2,\r
223                     charWidth, charHeight);\r
224 \r
225             nameHash.put((SequenceI) node.element(), rect);\r
226 \r
227             // Colour selected leaves differently\r
228             SequenceGroup selected = av.getSelectionGroup();\r
229 \r
230             if ((selected != null) &&\r
231                     selected.getSequences(false).contains((SequenceI) node.element()))\r
232             {\r
233                 g.setColor(Color.gray);\r
234 \r
235                 g.fillRect(xend + 10, ypos-charHeight/2, charWidth,\r
236                     charHeight);\r
237                 g.setColor(Color.white);\r
238             }\r
239 \r
240             g.drawString(name, xend + 10, ypos+fm.getDescent());\r
241             g.setColor(Color.black);\r
242         }\r
243         else\r
244         {\r
245             drawNode(g, (SequenceNode) node.left(), chunk, scale, width, offx,\r
246                 offy);\r
247             drawNode(g, (SequenceNode) node.right(), chunk, scale, width, offx,\r
248                 offy);\r
249 \r
250             float height = node.height;\r
251             float dist = node.dist;\r
252 \r
253             int xstart = (int) ((height - dist) * scale) + offx;\r
254             int xend = (int) (height * scale) + offx;\r
255             int ypos = (int) (node.ycount * chunk) + offy;\r
256 \r
257             g.setColor(((SequenceNode) node).color.darker());\r
258 \r
259             // Draw horizontal line\r
260             g.drawLine(xstart, ypos, xend, ypos);\r
261             if (node == highlightNode)\r
262               g.fillRect(xend - 3, ypos - 3, 6, 6);\r
263             else\r
264               g.fillRect(xend - 2, ypos - 2, 4, 4);\r
265 \r
266             int ystart = (int) (((SequenceNode) node.left()).ycount * chunk) +\r
267                 offy;\r
268             int yend = (int) (((SequenceNode) node.right()).ycount * chunk) +\r
269                 offy;\r
270 \r
271             Rectangle pos = new Rectangle(xend - 2, ypos - 2, 5, 5);\r
272             nodeHash.put(node, pos);\r
273 \r
274             g.drawLine((int) (height * scale) + offx, ystart,\r
275                 (int) (height * scale) + offx, yend);\r
276 \r
277             if (showDistances && (node.dist > 0))\r
278             {\r
279                 g.drawString(new Format("%-.2f").form(node.dist).trim(), xstart+2,\r
280                     ypos - 2);\r
281             }\r
282         }\r
283     }\r
284 \r
285     /**\r
286      * DOCUMENT ME!\r
287      *\r
288      * @param x DOCUMENT ME!\r
289      * @param y DOCUMENT ME!\r
290      *\r
291      * @return DOCUMENT ME!\r
292      */\r
293     public Object findElement(int x, int y)\r
294     {\r
295         Enumeration keys = nameHash.keys();\r
296 \r
297         while (keys.hasMoreElements())\r
298         {\r
299             Object ob = keys.nextElement();\r
300             Rectangle rect = (Rectangle) nameHash.get(ob);\r
301 \r
302             if ((x >= rect.x) && (x <= (rect.x + rect.width)) && (y >= rect.y) &&\r
303                     (y <= (rect.y + rect.height)))\r
304             {\r
305                 return ob;\r
306             }\r
307         }\r
308 \r
309         keys = nodeHash.keys();\r
310 \r
311         while (keys.hasMoreElements())\r
312         {\r
313             Object ob = keys.nextElement();\r
314             Rectangle rect = (Rectangle) nodeHash.get(ob);\r
315 \r
316             if ((x >= rect.x) && (x <= (rect.x + rect.width)) && (y >= rect.y) &&\r
317                     (y <= (rect.y + rect.height)))\r
318             {\r
319                 return ob;\r
320             }\r
321         }\r
322 \r
323         return null;\r
324     }\r
325 \r
326     /**\r
327      * DOCUMENT ME!\r
328      *\r
329      * @param pickBox DOCUMENT ME!\r
330      */\r
331     public void pickNodes(Rectangle pickBox)\r
332     {\r
333         int width = getWidth();\r
334         int height = getHeight();\r
335 \r
336         SequenceNode top = tree.getTopNode();\r
337 \r
338         float wscale = (float) ((width * .8) - (offx * 2)) / tree.getMaxHeight();\r
339 \r
340         if (top.count == 0)\r
341         {\r
342             top.count = ((SequenceNode) top.left()).count +\r
343                 ((SequenceNode) top.right()).count;\r
344         }\r
345 \r
346         float chunk = (float) (height - (offy)) / top.count;\r
347 \r
348         pickNode(pickBox, top, chunk, wscale, width, offx, offy);\r
349     }\r
350 \r
351     /**\r
352      * DOCUMENT ME!\r
353      *\r
354      * @param pickBox DOCUMENT ME!\r
355      * @param node DOCUMENT ME!\r
356      * @param chunk DOCUMENT ME!\r
357      * @param scale DOCUMENT ME!\r
358      * @param width DOCUMENT ME!\r
359      * @param offx DOCUMENT ME!\r
360      * @param offy DOCUMENT ME!\r
361      */\r
362     public void pickNode(Rectangle pickBox, SequenceNode node, float chunk,\r
363         float scale, int width, int offx, int offy)\r
364     {\r
365         if (node == null)\r
366         {\r
367             return;\r
368         }\r
369 \r
370         if ((node.left() == null) && (node.right() == null))\r
371         {\r
372             float height = node.height;\r
373             float dist = node.dist;\r
374 \r
375             int xstart = (int) ((height - dist) * scale) + offx;\r
376             int xend = (int) (height * scale) + offx;\r
377 \r
378             int ypos = (int) (node.ycount * chunk) + offy;\r
379 \r
380             if (pickBox.contains(new Point(xend, ypos)))\r
381             {\r
382                 if (node.element() instanceof SequenceI)\r
383                 {\r
384                     SequenceI seq = (SequenceI) node.element();\r
385                     SequenceGroup sg = av.getSelectionGroup();\r
386 \r
387                     if (sg != null)\r
388                     {\r
389                         sg.addOrRemove(seq, true);\r
390                     }\r
391                 }\r
392             }\r
393         }\r
394         else\r
395         {\r
396             pickNode(pickBox, (SequenceNode) node.left(), chunk, scale, width,\r
397                 offx, offy);\r
398             pickNode(pickBox, (SequenceNode) node.right(), chunk, scale, width,\r
399                 offx, offy);\r
400         }\r
401     }\r
402 \r
403     /**\r
404      * DOCUMENT ME!\r
405      *\r
406      * @param node DOCUMENT ME!\r
407      * @param c DOCUMENT ME!\r
408      */\r
409     public void setColor(SequenceNode node, Color c)\r
410     {\r
411         if (node == null)\r
412         {\r
413             return;\r
414         }\r
415 \r
416         if ((node.left() == null) && (node.right() == null))\r
417         {\r
418             node.color = c;\r
419 \r
420             if (node.element() instanceof SequenceI)\r
421             {\r
422                 ((SequenceI) node.element()).setColor(c);\r
423             }\r
424         }\r
425         else\r
426         {\r
427             node.color = c;\r
428             setColor((SequenceNode) node.left(), c);\r
429             setColor((SequenceNode) node.right(), c);\r
430         }\r
431     }\r
432 \r
433     /**\r
434      * DOCUMENT ME!\r
435      */\r
436     void startPrinting()\r
437     {\r
438         Thread thread = new Thread(this);\r
439         thread.start();\r
440     }\r
441 \r
442     // put printing in a thread to avoid painting problems\r
443     public void run()\r
444     {\r
445         PrinterJob printJob = PrinterJob.getPrinterJob();\r
446         PageFormat pf = printJob.pageDialog(printJob.defaultPage());\r
447 \r
448         printJob.setPrintable(this, pf);\r
449 \r
450         if (printJob.printDialog())\r
451         {\r
452             try\r
453             {\r
454                 printJob.print();\r
455             }\r
456             catch (Exception PrintException)\r
457             {\r
458                 PrintException.printStackTrace();\r
459             }\r
460         }\r
461     }\r
462 \r
463     /**\r
464      * DOCUMENT ME!\r
465      *\r
466      * @param pg DOCUMENT ME!\r
467      * @param pf DOCUMENT ME!\r
468      * @param pi DOCUMENT ME!\r
469      *\r
470      * @return DOCUMENT ME!\r
471      *\r
472      * @throws PrinterException DOCUMENT ME!\r
473      */\r
474     public int print(Graphics pg, PageFormat pf, int pi)\r
475         throws PrinterException\r
476     {\r
477         pg.setFont(font);\r
478         pg.translate((int) pf.getImageableX(), (int) pf.getImageableY());\r
479 \r
480         int pwidth = (int) pf.getImageableWidth();\r
481         int pheight = (int) pf.getImageableHeight();\r
482 \r
483         int noPages = getHeight() / pheight;\r
484 \r
485         if (pi > noPages)\r
486         {\r
487             return Printable.NO_SUCH_PAGE;\r
488         }\r
489 \r
490         if (pwidth > getWidth())\r
491         {\r
492             pwidth = getWidth();\r
493         }\r
494 \r
495         if (fitToWindow)\r
496         {\r
497             if (pheight > getHeight())\r
498             {\r
499                 pheight = getHeight();\r
500             }\r
501 \r
502             noPages = 0;\r
503         }\r
504         else\r
505         {\r
506             FontMetrics fm = pg.getFontMetrics(font);\r
507             int height = fm.getHeight() * nameHash.size();\r
508             pg.translate(0, -pi * pheight);\r
509             pg.setClip(0, pi * pheight, pwidth, (pi * pheight) + pheight);\r
510 \r
511             // translate number of pages,\r
512             // height is screen size as this is the\r
513             // non overlapping text size\r
514             pheight = height;\r
515         }\r
516 \r
517         draw(pg, pwidth, pheight);\r
518 \r
519         return Printable.PAGE_EXISTS;\r
520     }\r
521 \r
522     /**\r
523      * DOCUMENT ME!\r
524      *\r
525      * @param g DOCUMENT ME!\r
526      */\r
527     public void paintComponent(Graphics g)\r
528     {\r
529         super.paintComponent(g);\r
530         g.setFont(font);\r
531 \r
532         if(tree==null)\r
533         {\r
534           g.drawString("Calculating tree....", 20, getHeight()/2);\r
535         }\r
536         else\r
537         {\r
538           fm = g.getFontMetrics(font);\r
539 \r
540           if (nameHash.size() == 0)\r
541           {\r
542             repaint();\r
543           }\r
544 \r
545           if (fitToWindow ||\r
546               (!fitToWindow &&\r
547                (scrollPane.getHeight() > ( (fm.getHeight() * nameHash.size()) +\r
548                                           offy))))\r
549           {\r
550             draw(g, scrollPane.getWidth(), scrollPane.getHeight());\r
551             setPreferredSize(null);\r
552           }\r
553           else\r
554           {\r
555             setPreferredSize(new Dimension(scrollPane.getWidth(),\r
556                                            fm.getHeight() * nameHash.size()));\r
557             draw(g, scrollPane.getWidth(), fm.getHeight() * nameHash.size());\r
558           }\r
559 \r
560           scrollPane.revalidate();\r
561         }\r
562     }\r
563 \r
564 \r
565     /**\r
566      * DOCUMENT ME!\r
567      *\r
568      * @param fontSize DOCUMENT ME!\r
569      */\r
570     public void setFont(Font font)\r
571     {\r
572         this.font = font;\r
573         repaint();\r
574     }\r
575 \r
576     /**\r
577      * DOCUMENT ME!\r
578      *\r
579      * @param g1 DOCUMENT ME!\r
580      * @param width DOCUMENT ME!\r
581      * @param height DOCUMENT ME!\r
582      */\r
583     public void draw(Graphics g1, int width, int height)\r
584     {\r
585         Graphics2D g2 = (Graphics2D) g1;\r
586         g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,\r
587             RenderingHints.VALUE_ANTIALIAS_ON);\r
588         g2.setColor(Color.white);\r
589         g2.fillRect(0, 0, width, height);\r
590 \r
591         g2.setFont(font);\r
592 \r
593         offy = font.getSize()+10;\r
594 \r
595         fm = g2.getFontMetrics(font);\r
596 \r
597         labelLength = fm.stringWidth(longestName) + 20; //20 allows for scrollbar\r
598 \r
599         float wscale = (float) (width - labelLength - (offx * 2)) / tree.getMaxHeight();\r
600 \r
601         SequenceNode top = tree.getTopNode();\r
602 \r
603         if (top.count == 0)\r
604         {\r
605             top.count = ((SequenceNode) top.left()).count +\r
606                 ((SequenceNode) top.right()).count;\r
607         }\r
608 \r
609         float chunk = (float) (height - (offy)) / top.count;\r
610 \r
611         drawNode(g2, tree.getTopNode(), chunk, wscale, width, offx, offy);\r
612 \r
613         if (threshold != 0)\r
614         {\r
615             if (av.getCurrentTree() == tree)\r
616             {\r
617                 g2.setColor(Color.red);\r
618             }\r
619             else\r
620             {\r
621                 g2.setColor(Color.gray);\r
622             }\r
623 \r
624             int x = (int) ((threshold * (float) (getWidth() - labelLength -\r
625                 (2 * offx))) + offx);\r
626 \r
627             g2.drawLine(x, 0, x, getHeight());\r
628         }\r
629     }\r
630 \r
631     /**\r
632      * DOCUMENT ME!\r
633      *\r
634      * @param e DOCUMENT ME!\r
635      */\r
636     public void mouseReleased(MouseEvent e)\r
637     {\r
638     }\r
639 \r
640     /**\r
641      * DOCUMENT ME!\r
642      *\r
643      * @param e DOCUMENT ME!\r
644      */\r
645     public void mouseEntered(MouseEvent e)\r
646     {\r
647     }\r
648 \r
649     /**\r
650      * DOCUMENT ME!\r
651      *\r
652      * @param e DOCUMENT ME!\r
653      */\r
654     public void mouseExited(MouseEvent e)\r
655     {\r
656     }\r
657 \r
658     /**\r
659      * DOCUMENT ME!\r
660      *\r
661      * @param e DOCUMENT ME!\r
662      */\r
663     public void mouseClicked(MouseEvent evt)\r
664     {\r
665       if(highlightNode!=null)\r
666       {\r
667         if (SwingUtilities.isRightMouseButton(evt))\r
668         {\r
669           Color col = JColorChooser.showDialog(this, "Select Background Colour",\r
670                                                highlightNode.color);\r
671 \r
672           setColor(highlightNode, col);\r
673         }\r
674         else\r
675         if(evt.getClickCount()>1)\r
676         {\r
677           tree.swapNodes(highlightNode);\r
678           tree.reCount(tree.getTopNode());\r
679           tree.findHeight(tree.getTopNode());\r
680         }\r
681         else\r
682         {\r
683           Vector leaves = new Vector();\r
684           tree.findLeaves(highlightNode, leaves);\r
685 \r
686           for (int i = 0; i < leaves.size(); i++)\r
687           {\r
688             SequenceI seq =\r
689                 (SequenceI) ( (SequenceNode) leaves.elementAt(i)).element();\r
690             treeSelectionChanged(seq);\r
691           }\r
692         }\r
693 \r
694         PaintRefresher.Refresh(this, av.getSequenceSetId());\r
695         repaint();\r
696       }\r
697     }\r
698 \r
699 \r
700 \r
701     public void mouseMoved(MouseEvent evt)\r
702     {\r
703       av.setCurrentTree(tree);\r
704 \r
705       Object ob = findElement(evt.getX(), evt.getY());\r
706 \r
707       if (ob instanceof SequenceNode)\r
708       {\r
709         highlightNode = (SequenceNode) ob;\r
710         this.setToolTipText(\r
711             "<html>Left click to select leaves"\r
712             + "<br>Double-click to invert leaves"\r
713             + "<br>Right click to change colour");\r
714         repaint();\r
715 \r
716       }\r
717       else\r
718       {\r
719         if (highlightNode != null)\r
720         {\r
721           highlightNode = null;\r
722           setToolTipText("");\r
723           repaint();\r
724         }\r
725       }\r
726   }\r
727 \r
728     public void mouseDragged(MouseEvent ect)\r
729     {}\r
730 \r
731     /**\r
732      * DOCUMENT ME!\r
733      *\r
734      * @param e DOCUMENT ME!\r
735      */\r
736     public void mousePressed(MouseEvent e)\r
737     {\r
738         av.setCurrentTree(tree);\r
739 \r
740         int x = e.getX();\r
741         int y = e.getY();\r
742 \r
743         Object ob = findElement(x, y);\r
744 \r
745         if (ob instanceof SequenceI)\r
746         {\r
747           treeSelectionChanged( (Sequence) ob);\r
748           PaintRefresher.Refresh(this, ap.av.getSequenceSetId());\r
749           repaint();\r
750           return;\r
751         }\r
752         else if( !(ob instanceof SequenceNode) )\r
753         {\r
754             // Find threshold\r
755             if (tree.getMaxHeight() != 0)\r
756             {\r
757                 threshold = (float) (x - offx) / (float) (getWidth() -\r
758                     labelLength - (2 * offx));\r
759 \r
760                 tree.getGroups().removeAllElements();\r
761                 tree.groupNodes(tree.getTopNode(), threshold);\r
762                 setColor(tree.getTopNode(), Color.black);\r
763 \r
764                 av.setSelectionGroup(null);\r
765                 av.alignment.deleteAllGroups();\r
766 \r
767                 colourGroups();\r
768               }\r
769 \r
770               PaintRefresher.Refresh(this, ap.av.getSequenceSetId());\r
771               repaint();\r
772         }\r
773 \r
774 \r
775     }\r
776 \r
777     void colourGroups()\r
778     {\r
779       for (int i = 0; i < tree.getGroups().size(); i++)\r
780       {\r
781         Color col = new Color( (int) (Math.random() * 255),\r
782                               (int) (Math.random() * 255),\r
783                               (int) (Math.random() * 255));\r
784         setColor( (SequenceNode) tree.getGroups().elementAt(i),\r
785                  col.brighter());\r
786 \r
787         Vector l = tree.findLeaves( (SequenceNode) tree.getGroups()\r
788                                    .elementAt(i),\r
789                                    new Vector());\r
790 \r
791         Vector sequences = new Vector();\r
792 \r
793         for (int j = 0; j < l.size(); j++)\r
794         {\r
795           SequenceI s1 = (SequenceI) ( (SequenceNode) l.elementAt(j)).element();\r
796 \r
797           if (!sequences.contains(s1))\r
798           {\r
799             sequences.addElement(s1);\r
800           }\r
801         }\r
802 \r
803         ColourSchemeI cs = null;\r
804 \r
805         if (av.getGlobalColourScheme() != null)\r
806         {\r
807           if (av.getGlobalColourScheme() instanceof UserColourScheme)\r
808           {\r
809             cs = new UserColourScheme(\r
810                 ( (UserColourScheme) av.getGlobalColourScheme()).getColours());\r
811 \r
812           }\r
813           else\r
814             cs = ColourSchemeProperty.getColour(sequences,\r
815                                                 av.alignment.getWidth(),\r
816                                                 ColourSchemeProperty.\r
817                                                 getColourName(\r
818                                                     av.getGlobalColourScheme()));\r
819 \r
820           cs.setThreshold(av.getGlobalColourScheme().getThreshold(),\r
821                           av.getIgnoreGapsConsensus());\r
822         }\r
823 \r
824         SequenceGroup sg = new SequenceGroup(sequences,\r
825                                              "TreeGroup", cs, true, true, false,\r
826                                              0,\r
827                                              av.alignment.getWidth() - 1);\r
828 \r
829         if (av.getGlobalColourScheme() != null\r
830             && av.getGlobalColourScheme().conservationApplied())\r
831         {\r
832           Conservation c = new Conservation("Group",\r
833                                             ResidueProperties.propHash, 3,\r
834                                             sg.getSequences(false),\r
835                                             sg.getStartRes(), sg.getEndRes());\r
836 \r
837           c.calculate();\r
838           c.verdict(false, av.ConsPercGaps);\r
839           sg.cs.setConservation(c);\r
840         }\r
841 \r
842         av.alignment.addGroup(sg);\r
843       }\r
844 \r
845     }\r
846 \r
847     /**\r
848      * DOCUMENT ME!\r
849      *\r
850      * @param state DOCUMENT ME!\r
851      */\r
852     public void setShowDistances(boolean state)\r
853     {\r
854         this.showDistances = state;\r
855         repaint();\r
856     }\r
857 \r
858     /**\r
859      * DOCUMENT ME!\r
860      *\r
861      * @param state DOCUMENT ME!\r
862      */\r
863     public void setShowBootstrap(boolean state)\r
864     {\r
865         this.showBootstrap = state;\r
866         repaint();\r
867     }\r
868 \r
869     /**\r
870      * DOCUMENT ME!\r
871      *\r
872      * @param state DOCUMENT ME!\r
873      */\r
874     public void setMarkPlaceholders(boolean state)\r
875     {\r
876         this.markPlaceholders = state;\r
877         repaint();\r
878     }\r
879 }\r