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