sequence place holder reveal menu item.
[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 import java.awt.print.*;\r
13 \r
14 public class TreeCanvas extends JPanel implements MouseListener, Runnable, Printable\r
15 {\r
16   NJTree tree;\r
17   JScrollPane scrollPane;\r
18   AlignViewport av;\r
19   public static final String PLACEHOLDER=" * ";\r
20   Font font;\r
21   int  fontSize = 12;\r
22 \r
23   boolean fitToWindow = true;\r
24   boolean showDistances = false;\r
25   boolean showBootstrap = false;\r
26   boolean markPlaceholders = false;\r
27 \r
28   int offx = 20;\r
29   int offy = 20;\r
30 \r
31   float threshold;\r
32 \r
33   String longestName;\r
34   int labelLength=-1;\r
35 \r
36   //RubberbandRectangle rubberband;\r
37 \r
38   Vector    listeners;\r
39 \r
40   Hashtable nameHash = new Hashtable();\r
41   Hashtable nodeHash = new Hashtable();\r
42 \r
43   public TreeCanvas(AlignViewport av, NJTree tree, JScrollPane scroller, String label)\r
44   {\r
45     this.av = av;\r
46     this.tree     = tree;\r
47     scrollPane = scroller;\r
48     addMouseListener(this);\r
49     tree.findHeight(tree.getTopNode());\r
50     longestName = label;\r
51 \r
52     PaintRefresher.Register(this);\r
53   }\r
54   public void TreeSelectionChanged(Sequence sequence)\r
55  {\r
56     SequenceGroup selected = av.getSelectionGroup();\r
57     if(selected == null)\r
58     {\r
59       selected = new SequenceGroup();\r
60       av.setSelectionGroup(selected);\r
61     }\r
62 \r
63     selected.setEndRes(av.alignment.getWidth());\r
64     selected.addOrRemove(sequence);\r
65 \r
66 \r
67     PaintRefresher.Refresh(this);\r
68     repaint();\r
69  }\r
70 \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       String name    = (markPlaceholders && node.isPlaceholder()) ? (PLACEHOLDER+node.getName()) : node.getName();\r
127       FontMetrics fm = g.getFontMetrics(font);\r
128       int charWidth  = fm.stringWidth(name) + 3;\r
129       int charHeight = fm.getHeight();\r
130 \r
131       Rectangle rect = new Rectangle(xend+20,ypos-charHeight,\r
132                                      charWidth,charHeight);\r
133 \r
134       nameHash.put((SequenceI)node.element(),rect);\r
135 \r
136       // Colour selected leaves differently\r
137       SequenceGroup selected = av.getSelectionGroup();\r
138       if (selected!=null && selected.sequences.contains((SequenceI)node.element())) {\r
139         g.setColor(Color.gray);\r
140 \r
141         g.fillRect(xend + 10, ypos - charHeight + 3,charWidth,charHeight);\r
142         g.setColor(Color.white);\r
143       }\r
144       g.drawString(name,xend+10,ypos);\r
145       g.setColor(Color.black);\r
146     } else {\r
147       drawNode(g,(SequenceNode)node.left(), chunk,scale,width,offx,offy);\r
148       drawNode(g,(SequenceNode)node.right(),chunk,scale,width,offx,offy);\r
149 \r
150       float height = node.height;\r
151       float dist   = node.dist;\r
152 \r
153       int xstart = (int)((height-dist)*scale) + offx;\r
154       int xend   = (int)(height       *scale) + offx;\r
155       int ypos   = (int)(node.ycount  *chunk) + offy;\r
156 \r
157       g.setColor(((SequenceNode)node).color.darker());\r
158 \r
159       // Draw horizontal line\r
160       g.drawLine(xstart,ypos,xend,ypos);\r
161       g.fillRect(xend-2, ypos-2, 4,4);\r
162 \r
163       int ystart = (int)(((SequenceNode)node.left()) .ycount * chunk) + offy;\r
164       int yend   = (int)(((SequenceNode)node.right()).ycount * chunk) + offy;\r
165 \r
166       Rectangle pos = new Rectangle(xend-2,ypos-2,5,5);\r
167       nodeHash.put(node,pos);\r
168 \r
169       g.drawLine((int)(height*scale) + offx, ystart,\r
170                  (int)(height*scale) + offx, yend);\r
171 \r
172       if (showDistances && node.dist > 0) {\r
173         g.drawString(new Format("%5.2f").form(node.dist),xstart,ypos - 5);\r
174       }\r
175 \r
176     }\r
177   }\r
178   public Object findElement(int x, int y) {\r
179        Enumeration keys = nameHash.keys();\r
180 \r
181     while (keys.hasMoreElements()) {\r
182             Object ob = keys.nextElement();\r
183             Rectangle rect = (Rectangle)nameHash.get(ob);\r
184 \r
185             if (x >= rect.x && x <= (rect.x + rect.width) &&\r
186               y >= rect.y && y <= (rect.y + rect.height)) {\r
187               return ob;\r
188             }\r
189   }\r
190   keys = nodeHash.keys();\r
191 \r
192     while (keys.hasMoreElements()) {\r
193             Object ob = keys.nextElement();\r
194             Rectangle rect = (Rectangle)nodeHash.get(ob);\r
195 \r
196             if (x >= rect.x && x <= (rect.x + rect.width) &&\r
197               y >= rect.y && y <= (rect.y + rect.height)) {\r
198               return ob;\r
199             }\r
200     }\r
201       return null;\r
202 \r
203   }\r
204 \r
205   public void pickNodes(Rectangle pickBox) {\r
206     int width  = getWidth();\r
207     int height = getHeight();\r
208 \r
209     SequenceNode top = tree.getTopNode();\r
210 \r
211     float wscale = (float)(width*.8-offx*2)/tree.getMaxHeight()\r
212 ;\r
213     if (top.count == 0) {\r
214       top.count = ((SequenceNode)top.left()).count + ((SequenceNode)top.right()).count ;\r
215     }\r
216     float chunk = (float)(height-offy*2)/top.count;\r
217 \r
218     pickNode(pickBox,top,chunk,wscale,width,offx,offy);\r
219   }\r
220 \r
221   public void pickNode(Rectangle pickBox, SequenceNode node, float chunk, float scale, int width,int offx, int offy) {\r
222     if (node == null) {\r
223       return;\r
224     }\r
225 \r
226     if (node.left() == null && node.right() == null) {\r
227       float height = node.height;\r
228       float dist   = node.dist;\r
229 \r
230       int xstart = (int)((height-dist)*scale) + offx;\r
231       int xend   = (int)(height*scale) + offx;\r
232 \r
233       int ypos = (int)(node.ycount * chunk) + offy;\r
234 \r
235       if (pickBox.contains(new Point(xend,ypos))) {\r
236         if (node.element() instanceof SequenceI) {\r
237           SequenceI seq = (SequenceI)node.element();\r
238           SequenceGroup sg = av.getSelectionGroup();\r
239           if(sg!=null)\r
240             sg.addOrRemove(seq);\r
241         }\r
242       }\r
243     } else {\r
244       pickNode(pickBox,(SequenceNode)node.left(), chunk,scale,width,offx,offy);\r
245       pickNode(pickBox,(SequenceNode)node.right(),chunk,scale,width,offx,offy);\r
246     }\r
247   }\r
248 \r
249   public void setColor(SequenceNode node, Color c)\r
250   {\r
251     if (node == null)\r
252       return;\r
253 \r
254     if (node.left() == null && node.right() == null)\r
255     {\r
256       node.color = c;\r
257 \r
258       if (node.element() instanceof SequenceI)\r
259           ((SequenceI)node.element()).setColor(c);\r
260     } else\r
261     {\r
262       node.color = c;\r
263       setColor((SequenceNode)node.left(),c);\r
264       setColor((SequenceNode)node.right(),c);\r
265     }\r
266   }\r
267 \r
268   void startPrinting()\r
269   {\r
270     Thread thread = new Thread(this);\r
271     thread.start();\r
272   }\r
273 \r
274   // put printing in a thread to avoid painting problems\r
275   public void run()\r
276   {\r
277     PrinterJob printJob = PrinterJob.getPrinterJob();\r
278     PageFormat pf = printJob.pageDialog(printJob.defaultPage());\r
279 \r
280     printJob.setPrintable(this, pf);\r
281     if (printJob.printDialog())\r
282     {\r
283       try\r
284       {\r
285         printJob.print();\r
286       }\r
287       catch (Exception PrintException)\r
288       {\r
289         PrintException.printStackTrace();\r
290       }\r
291     }\r
292   }\r
293 \r
294 \r
295   public int print(Graphics pg, PageFormat pf, int pi) throws PrinterException\r
296   {\r
297 \r
298     pg.setFont(font);\r
299     pg.translate((int)pf.getImageableX(), (int)pf.getImageableY());\r
300     int pwidth = (int) pf.getImageableWidth();\r
301     int pheight = (int) pf.getImageableHeight();\r
302 \r
303     int noPages = getHeight() / pheight;\r
304     if(pi>noPages)\r
305       return Printable.NO_SUCH_PAGE;\r
306 \r
307 \r
308     if (pwidth > getWidth())\r
309         pwidth = getWidth();\r
310 \r
311     if(fitToWindow)\r
312     {\r
313       if (pheight > getHeight())\r
314         pheight = getHeight();\r
315 \r
316       noPages = 0;\r
317     }\r
318     else\r
319     {\r
320 \r
321         FontMetrics fm = pg.getFontMetrics(font);\r
322         int height = fm.getHeight() * nameHash.size();\r
323         pg.translate(0, -pi*pheight  );\r
324         pg.setClip(0,pi*pheight, pwidth,pi*pheight + pheight);\r
325        // translate number of pages,\r
326        // height is screen size as this is the\r
327        // non overlapping text size\r
328         pheight = height;\r
329     }\r
330 \r
331     draw(pg, pwidth, pheight);\r
332 \r
333     return Printable.PAGE_EXISTS;\r
334 \r
335   }\r
336 \r
337   public void paintComponent(Graphics g)\r
338   {\r
339 \r
340     font = new Font("Verdana",Font.PLAIN,fontSize);\r
341     g.setFont(font);\r
342 \r
343     FontMetrics fm = g.getFontMetrics(font);\r
344 \r
345     if(nameHash.size()==0)\r
346       repaint();\r
347 \r
348 \r
349     if( fitToWindow || (!fitToWindow && scrollPane.getHeight() > fm.getHeight() * nameHash.size()+offy ) )\r
350      {\r
351          draw(g,scrollPane.getWidth(),scrollPane.getHeight());\r
352          setPreferredSize(null);\r
353      }\r
354     else\r
355      {\r
356          setPreferredSize(new Dimension(scrollPane.getWidth(), fm.getHeight() * nameHash.size()));\r
357          draw( g,scrollPane.getWidth(), fm.getHeight() * nameHash.size());\r
358      }\r
359 \r
360     scrollPane.revalidate();\r
361   }\r
362     public int getFontSize() {\r
363         return fontSize;\r
364     }\r
365     public void setFontSize(int fontSize) {\r
366         this.fontSize = fontSize;\r
367         repaint();\r
368     }\r
369   public void draw(Graphics g1, int width, int height) {\r
370 \r
371       Graphics2D g2 = (Graphics2D)g1;\r
372       g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);\r
373       g2.setColor(Color.white);\r
374       g2.fillRect(0,0,width,height);\r
375 \r
376 \r
377       labelLength = g2.getFontMetrics(font).stringWidth(longestName)+ 20;//20 allows for scrollbar\r
378 \r
379       float wscale =(float)(width - labelLength -offx*2)/tree.getMaxHeight();\r
380 \r
381       SequenceNode top = tree.getTopNode();\r
382 \r
383       if (top.count == 0) {\r
384           top.count = ((SequenceNode)top.left()).count + ((SequenceNode)top.right()).count ;\r
385       }\r
386       float chunk = (float)(height-offy*2)/top.count ;\r
387 \r
388       drawNode(g2,tree.getTopNode(),chunk,wscale,width,offx,offy);\r
389 \r
390       if (threshold != 0)\r
391       {\r
392         if(av.getCurrentTree() == tree)\r
393           g2.setColor(Color.red);\r
394         else\r
395           g2.setColor(Color.gray);\r
396 \r
397           int x = (int)(    threshold * (float)(getWidth()-labelLength - 2*offx) +offx   ) ;\r
398 \r
399           g2.drawLine(x,0,x,getHeight());\r
400       }\r
401 \r
402   }\r
403 \r
404   public void mouseReleased(MouseEvent e) { }\r
405   public void mouseEntered(MouseEvent e) { }\r
406   public void mouseExited(MouseEvent e) { }\r
407   public void mouseClicked(MouseEvent e) {\r
408   }\r
409 \r
410   public void mousePressed(MouseEvent e) {\r
411 \r
412       av.setCurrentTree(tree);\r
413 \r
414       int x = e.getX();\r
415       int y = e.getY();\r
416 \r
417       Object ob = findElement(x,y);\r
418 \r
419       if (ob instanceof SequenceI)\r
420       {\r
421           TreeSelectionChanged((Sequence)ob);\r
422           repaint();\r
423           return;\r
424 \r
425       } else if (ob instanceof SequenceNode) {\r
426           SequenceNode tmpnode = (SequenceNode)ob;\r
427           tree.swapNodes(tmpnode);\r
428           tree.reCount(tree.getTopNode());\r
429           tree.findHeight(tree.getTopNode());\r
430       } else {\r
431           // Find threshold\r
432 \r
433           if (tree.getMaxHeight() != 0) {\r
434               threshold = (float)(x - offx)/(float)(getWidth()-labelLength - 2*offx);\r
435 \r
436               tree.getGroups().removeAllElements();\r
437               tree.groupNodes(tree.getTopNode(),threshold);\r
438               setColor(tree.getTopNode(),Color.black);\r
439 \r
440               av.setSelectionGroup(null);\r
441               av.alignment.deleteAllGroups();\r
442 \r
443               for (int i=0; i < tree.getGroups().size(); i++)\r
444               {\r
445 \r
446                   Color col = new Color((int)(Math.random()*255),\r
447                                         (int)(Math.random()*255),\r
448                                         (int)(Math.random()*255));\r
449                   setColor((SequenceNode)tree.getGroups().elementAt(i),col.brighter());\r
450 \r
451                   Vector l = tree.findLeaves((SequenceNode)tree.getGroups().elementAt(i),new Vector());\r
452                   SequenceGroup sg = null;\r
453                   for (int j = 0; j < l.size(); j++)\r
454                   {\r
455                     SequenceNode sn = (SequenceNode) l.elementAt(j);\r
456                     if(sg==null)\r
457                        sg = new SequenceGroup("TreeGroup", av.getGlobalColourScheme(), true, true,false,0,av.alignment.getWidth());\r
458 \r
459                     sg.addSequence( (Sequence) sn.element());\r
460                   }\r
461 \r
462                   if (av.getGlobalColourScheme() instanceof ConservationColourScheme)\r
463                   {\r
464                     ConservationColourScheme ccs = (ConservationColourScheme) av.getGlobalColourScheme();\r
465                     Conservation c = new Conservation("Group",\r
466                                                       ResidueProperties.propHash, 3,\r
467                                                       sg.sequences, sg.getStartRes(),\r
468                                                       sg.getEndRes());\r
469 \r
470                     c.calculate();\r
471                     c.verdict(false, av.ConsPercGaps);\r
472                     ccs = new ConservationColourScheme(c, ccs.cs);\r
473 \r
474                     sg.cs = ccs;\r
475 \r
476                   }\r
477 \r
478 \r
479 \r
480                   av.alignment.addGroup(sg);\r
481 \r
482               }\r
483           }\r
484       }\r
485 \r
486       PaintRefresher.Refresh(this);\r
487       repaint();\r
488 \r
489   }\r
490 \r
491     public void setShowDistances(boolean state) {\r
492         this.showDistances = state;\r
493         repaint();\r
494     }\r
495 \r
496     public void setShowBootstrap(boolean state) {\r
497       this.showBootstrap = state;\r
498       repaint();\r
499     }\r
500     public void setMarkPlaceholders(boolean state) {\r
501             this.markPlaceholders = state;\r
502             repaint();\r
503     }\r
504 \r
505 }\r
506 \r