Helix glyphs drawn correctly
[jalview.git] / src / jalview / appletgui / AnnotationPanel.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.datamodel.*;\r
28 \r
29 public class AnnotationPanel\r
30     extends Panel implements AdjustmentListener\r
31 {\r
32   AlignViewport av;\r
33   AlignmentPanel ap;\r
34   int activeRow = -1;\r
35 \r
36   Vector activeRes;\r
37   static String HELIX = "Helix";\r
38   static String SHEET = "Sheet";\r
39   static String LABEL = "Label";\r
40   static String REMOVE = "Remove Annotation";\r
41   static String COLOUR = "Colour";\r
42   static Color HELIX_COLOUR = Color.red.darker();\r
43   static Color SHEET_COLOUR = Color.green.darker().darker();\r
44 \r
45   Image image;\r
46   Graphics gg;\r
47   FontMetrics fm;\r
48   int imgWidth = 0;\r
49 \r
50   boolean fastPaint = false;\r
51 \r
52   public static int GRAPH_HEIGHT = 40;\r
53 \r
54   public AnnotationPanel(AlignmentPanel ap)\r
55   {\r
56     this.ap = ap;\r
57     av = ap.av;\r
58     this.setLayout(null);\r
59     adjustPanelHeight();\r
60 \r
61     addMouseMotionListener(new MouseMotionAdapter()\r
62     {\r
63       public void mouseMoved(MouseEvent evt)\r
64       {\r
65         doMouseMoved(evt);\r
66       }\r
67     });\r
68 \r
69     // ap.annotationScroller.getVAdjustable().addAdjustmentListener( this );\r
70   }\r
71 \r
72 \r
73   public AnnotationPanel(AlignViewport av)\r
74   {\r
75     this.av = av;\r
76   }\r
77 \r
78 \r
79   public void adjustmentValueChanged(AdjustmentEvent evt)\r
80   {\r
81     ap.alabels.setScrollOffset( -evt.getValue());\r
82   }\r
83 \r
84   public int adjustPanelHeight()\r
85   {\r
86     // setHeight of panels\r
87     AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();\r
88 \r
89 \r
90     int height = 0;\r
91     if (aa != null)\r
92     {\r
93       for (int i = 0; i < aa.length; i++)\r
94       {\r
95         if (!aa[i].visible)\r
96         {\r
97           continue;\r
98         }\r
99 \r
100         aa[i].height = 0;\r
101 \r
102         if (aa[i].hasText)\r
103         {\r
104           aa[i].height += av.charHeight;\r
105         }\r
106         if (aa[i].hasIcons)\r
107         {\r
108           aa[i].height += 16;\r
109         }\r
110 \r
111         if (aa[i].graph>0)\r
112         {\r
113           aa[i].height += GRAPH_HEIGHT;\r
114         }\r
115 \r
116         if (aa[i].height == 0)\r
117         {\r
118           aa[i].height = 20;\r
119         }\r
120         height += aa[i].height;\r
121       }\r
122     }\r
123     else\r
124     {\r
125       height = 20;\r
126     }\r
127 \r
128     this.setSize(getSize().width, height);\r
129     if(ap!=null)\r
130        ap.annotationScroller.setSize(getSize().width, height);\r
131 \r
132 \r
133     repaint();\r
134 \r
135     return height;\r
136 \r
137   }\r
138 \r
139   public void addEditableColumn(int i)\r
140   {\r
141     if (activeRow == -1)\r
142     {\r
143       AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();\r
144       if(aa ==null)\r
145         return;\r
146 \r
147       for (int j = 0; j < aa.length; j++)\r
148       {\r
149         if (aa[j].editable)\r
150         {\r
151           activeRow = j;\r
152           break;\r
153         }\r
154       }\r
155     }\r
156 \r
157     if (activeRes == null)\r
158     {\r
159       activeRes = new Vector();\r
160       activeRes.addElement(String.valueOf(i));\r
161       return;\r
162     }\r
163 \r
164     activeRes.addElement(String.valueOf(i));\r
165   }\r
166 \r
167   public void doMouseMoved(MouseEvent evt)\r
168   {\r
169     AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();\r
170     if (aa == null)\r
171     {\r
172       return;\r
173     }\r
174 \r
175     int row = -1;\r
176     int height = 0;\r
177     for (int i = 0; i < aa.length; i++)\r
178     {\r
179 \r
180       if (aa[i].visible)\r
181       {\r
182         height += aa[i].height;\r
183       }\r
184 \r
185       if (evt.getY() < height)\r
186       {\r
187         row = i;\r
188         break;\r
189       }\r
190     }\r
191 \r
192     int res = evt.getX() / av.getCharWidth() + av.getStartRes();\r
193     if (row > -1 && res < aa[row].annotations.length && aa[row].annotations[res] != null)\r
194     {\r
195       StringBuffer text = new StringBuffer("Sequence position " + (res + 1) +\r
196                                            "  " +\r
197                                            aa[row].annotations[res].description);\r
198       ap.alignFrame.statusBar.setText(text.toString());\r
199     }\r
200   }\r
201 \r
202   public void update(Graphics g)\r
203   {\r
204     paint(g);\r
205   }\r
206 \r
207   public void paint(Graphics g)\r
208   {\r
209     imgWidth = (av.endRes - av.startRes + 1) * av.charWidth;\r
210 \r
211     if (image == null || imgWidth != image.getWidth(this))\r
212     {\r
213       image = createImage(imgWidth, ap.annotationPanel.getSize().height);\r
214       gg = image.getGraphics();\r
215       gg.setFont(av.getFont());\r
216       fm = gg.getFontMetrics();\r
217       fastPaint = false;\r
218     }\r
219 \r
220     if (fastPaint)\r
221     {\r
222       g.drawImage(image, 0, 0, this);\r
223       fastPaint = false;\r
224       return;\r
225     }\r
226 \r
227     drawComponent(gg, av.startRes, av.endRes + 1);\r
228     g.drawImage(image, 0, 0, this);\r
229   }\r
230 \r
231   public void fastPaint(int horizontal)\r
232   {\r
233     if (horizontal == 0\r
234         || av.alignment.getAlignmentAnnotation() == null\r
235         || av.alignment.getAlignmentAnnotation().length < 1\r
236         )\r
237     {\r
238       repaint();\r
239       return;\r
240     }\r
241 \r
242     gg.copyArea(0, 0, imgWidth, getSize().height, -horizontal * av.charWidth, 0);\r
243     int sr = av.startRes, er = av.endRes + 1, transX = 0;\r
244 \r
245     if (horizontal > 0) // scrollbar pulled right, image to the left\r
246     {\r
247       transX = (er - sr - horizontal) * av.charWidth;\r
248       sr = er - horizontal;\r
249     }\r
250     else if (horizontal < 0)\r
251     {\r
252       er = sr - horizontal;\r
253     }\r
254 \r
255     gg.translate(transX, 0);\r
256 \r
257     drawComponent(gg, sr, er);\r
258 \r
259     gg.translate( -transX, 0);\r
260 \r
261     fastPaint = true;\r
262     repaint();\r
263   }\r
264 \r
265   /**\r
266    * DOCUMENT ME!\r
267    *\r
268    * @param g DOCUMENT ME!\r
269    * @param startRes DOCUMENT ME!\r
270    * @param endRes DOCUMENT ME!\r
271    */\r
272   public void drawComponent(Graphics g, int startRes, int endRes)\r
273   {\r
274     g.setFont(av.getFont());\r
275 \r
276     if (fm == null)\r
277       fm = g.getFontMetrics();\r
278 \r
279 \r
280       g.setColor(Color.white);\r
281       g.fillRect(0, 0, (endRes - startRes) * av.charWidth, getSize().height);\r
282 \r
283       if ((av.alignment.getAlignmentAnnotation() == null) ||\r
284               (av.alignment.getAlignmentAnnotation().length < 1))\r
285       {\r
286           g.setColor(Color.white);\r
287           g.fillRect(0, 0, getSize().width, getSize().height);\r
288           g.setColor(Color.black);\r
289           g.drawString("Alignment has no annotations", 20, 15);\r
290 \r
291           return;\r
292       }\r
293 \r
294       AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();\r
295 \r
296       int j;\r
297       int x = 0;\r
298       int y = 0;\r
299       char lastSS;\r
300       int lastSSX;\r
301       int iconOffset = av.charHeight / 2;\r
302       boolean validRes = false;\r
303 \r
304       boolean [] graphGroupDrawn = new boolean[aa.length];\r
305 \r
306 \r
307       //\u03B2 \u03B1\r
308       for (int i = 0; i < aa.length; i++)\r
309       {\r
310           AlignmentAnnotation row = aa[i];\r
311 \r
312           if (!row.visible)\r
313           {\r
314               continue;\r
315           }\r
316 \r
317           lastSS = ' ';\r
318           lastSSX = 0;\r
319 \r
320           if (row.graph>0)\r
321           {\r
322               if(row.graphGroup>-1 && graphGroupDrawn[ row.graphGroup ] )\r
323                 continue;\r
324 \r
325               // this is so that we draw the characters below the graph\r
326               y += row.height;\r
327 \r
328               if (row.hasText)\r
329               {\r
330                   y -= av.charHeight;\r
331               }\r
332           }\r
333 \r
334           if (row.hasText)\r
335           {\r
336               iconOffset = av.charHeight / 2;\r
337           }\r
338           else\r
339           {\r
340               iconOffset = 0;\r
341           }\r
342 \r
343           int column = startRes;\r
344           while(column < endRes)\r
345           {\r
346               if ((row.annotations.length <= column) ||\r
347                       (row.annotations[column] == null))\r
348               {\r
349                   validRes = false;\r
350               }\r
351               else\r
352               {\r
353                   validRes = true;\r
354               }\r
355 \r
356               x = (column - startRes) * av.charWidth;\r
357 \r
358 \r
359               if (validRes &&\r
360                       (row.annotations[column].displayCharacter.length() > 0))\r
361               {\r
362                 int charOffset = (av.charWidth -\r
363                     fm.charWidth(row.annotations[column].displayCharacter.charAt(\r
364                             0))) / 2;\r
365                 g.setColor(row.annotations[column].colour);\r
366 \r
367                 if (column == 0 || row.graph>0)\r
368                 {\r
369                     g.drawString(row.annotations[column].displayCharacter, x+charOffset,\r
370                         y + iconOffset + 3);\r
371                 }\r
372                 else if (\r
373                     row.annotations[column - 1] == null\r
374                     ||(!row.annotations[column].displayCharacter.equals(\r
375                         row.annotations[column - 1].displayCharacter)\r
376                     ||\r
377               (row.annotations[column].displayCharacter.length() <2 &&\r
378                row.annotations[column].secondaryStructure==' ')))\r
379                 {\r
380                     g.drawString(row.annotations[column].displayCharacter, x+charOffset,\r
381                         y + iconOffset + 3);\r
382                     }\r
383               }\r
384 \r
385               if (row.hasIcons)\r
386               {\r
387                   if (!validRes ||\r
388                           (row.annotations[column].secondaryStructure != lastSS))\r
389                   {\r
390                       switch (lastSS)\r
391                       {\r
392                       case 'H':\r
393                         g.setColor(HELIX_COLOUR);\r
394                         int sCol = (lastSSX / av.charWidth) + startRes;\r
395                         int x1 = lastSSX;\r
396                         int x2 = x;\r
397 \r
398                        if(row.annotations[sCol-1]==null ||\r
399                           row.annotations[sCol-1].secondaryStructure!='H')\r
400                        {\r
401                          g.fillArc(lastSSX, y+4+iconOffset, av.charWidth, 8, 90,180) ;\r
402                          x1 += av.charWidth/2;\r
403                        }\r
404 \r
405                         if(row.annotations[column]==null ||\r
406                            row.annotations[column].secondaryStructure!='H')\r
407                         {\r
408                           g.fillArc(x-av.charWidth, y+4+iconOffset, av.charWidth, 8, 270,180);\r
409                           x2 -= av.charWidth/2;\r
410                         }\r
411 \r
412                         g.fillRect(x1, y+4+iconOffset, x2-x1, 8);\r
413                             break;\r
414 \r
415                       case 'E':\r
416                           g.setColor(SHEET_COLOUR);\r
417                           g.fillRect(lastSSX, y + 4 + iconOffset,\r
418                               x - lastSSX - 4, 7);\r
419                           g.fillPolygon(new int[] { x - 4, x - 4, x },\r
420                               new int[]\r
421                               {\r
422                                   y + iconOffset, y + 14 + iconOffset,\r
423                                   y + 8 + iconOffset\r
424                               }, 3);\r
425 \r
426                           break;\r
427 \r
428 \r
429                       default:\r
430                           g.setColor(Color.gray);\r
431                           g.fillRect(lastSSX, y + 6 + iconOffset,\r
432                               x - lastSSX, 2);\r
433 \r
434                           break;\r
435                       }\r
436 \r
437                       if (validRes)\r
438                       {\r
439                           lastSS = row.annotations[column].secondaryStructure;\r
440                       }\r
441                       else\r
442                       {\r
443                           lastSS = ' ';\r
444                       }\r
445 \r
446                       lastSSX = x;\r
447                   }\r
448               }\r
449 \r
450 \r
451           column++;\r
452           }\r
453 \r
454           x += av.charWidth;\r
455 \r
456           if (row.hasIcons)\r
457           {\r
458               switch (lastSS)\r
459               {\r
460               case 'H':\r
461                 g.setColor(HELIX_COLOUR);\r
462 \r
463                 int sCol = (lastSSX / av.charWidth) + startRes;\r
464                 int x1 = lastSSX;\r
465                 int x2 = x;\r
466 \r
467                if(row.annotations[sCol-1]==null ||\r
468                   row.annotations[sCol-1].secondaryStructure!='H')\r
469                {\r
470                  g.fillArc(lastSSX, y+4+iconOffset, av.charWidth, 8, 90,180) ;\r
471                  x1 += av.charWidth/2;\r
472                }\r
473 \r
474                 if(row.annotations[column]==null ||\r
475                    row.annotations[column].secondaryStructure!='H')\r
476                 {\r
477                   g.fillArc(x-av.charWidth, y+4+iconOffset, av.charWidth, 8, 270,180);\r
478                   x2 -= av.charWidth/2;\r
479                 }\r
480 \r
481                 g.fillRect(x1, y+4+iconOffset, x2-x1, 8);\r
482 \r
483                     break;\r
484 \r
485               case 'E':\r
486                   g.setColor(SHEET_COLOUR);\r
487 \r
488                   if (row.annotations.length > endRes\r
489                       && row.annotations[endRes].secondaryStructure != 'E')\r
490                   {\r
491                     g.fillRect(lastSSX, y + 4 + iconOffset,\r
492                                x - lastSSX - 4, 7);\r
493                     g.fillPolygon(new int[]\r
494                                   {x - 4, x - 4, x},\r
495                                   new int[]\r
496                                   {\r
497                                   y + iconOffset, y + 14 + iconOffset,\r
498                                   y + 7 + iconOffset\r
499                     }, 3);\r
500                   }\r
501                   else\r
502                     g.fillRect(lastSSX, y + 4 + iconOffset,\r
503                                x - lastSSX, 7);\r
504 \r
505                   break;\r
506 \r
507 \r
508               default:\r
509                   g.setColor(Color.gray);\r
510                   g.fillRect(lastSSX, y + 6 + iconOffset, x - lastSSX, 2);\r
511 \r
512                   break;\r
513               }\r
514           }\r
515 \r
516           if (row.graph>0)\r
517           {\r
518               if(row.graph == AlignmentAnnotation.LINE_GRAPH )\r
519               {\r
520                 if(row.graphGroup>-1 && !graphGroupDrawn[row.graphGroup])\r
521                  {\r
522                    float groupmax=-999999, groupmin=9999999;\r
523                    for(int gg=0; gg<aa.length; gg++)\r
524                    {\r
525                      if(aa[gg].graphGroup!=row.graphGroup)\r
526                        continue;\r
527 \r
528                      if(aa[gg]!=row)\r
529                        aa[gg].visible = false;\r
530 \r
531                      if(aa[gg].graphMax>groupmax)\r
532                        groupmax = aa[gg].graphMax;\r
533                      if(aa[gg].graphMin<groupmin)\r
534                        groupmin = aa[gg].graphMin;\r
535                    }\r
536 \r
537                    for (int gg = 0; gg < aa.length; gg++)\r
538                    {\r
539                      if (aa[gg].graphGroup == row.graphGroup)\r
540                      {\r
541                        drawLineGraph(g, aa[gg], startRes, endRes, y,\r
542                                      groupmin, groupmax,\r
543                                      row.graphHeight);\r
544                      }\r
545                    }\r
546 \r
547                    graphGroupDrawn[ row.graphGroup ] = true;\r
548                  }\r
549                  else\r
550                    drawLineGraph(g, row, startRes, endRes,\r
551                                  y, row.graphMin, row.graphMax, row.graphHeight  );\r
552               }\r
553               else if(row.graph == AlignmentAnnotation.BAR_GRAPH )\r
554                  drawBarGraph(g, row, startRes, endRes,\r
555                               row.graphMin, row.graphMax, y);\r
556           }\r
557 \r
558           if (row.graph>0 && row.hasText)\r
559           {\r
560               y += av.charHeight;\r
561           }\r
562 \r
563           if (row.graph==0)\r
564           {\r
565               y += aa[i].height;\r
566           }\r
567       }\r
568   }\r
569 \r
570   public void drawLineGraph(Graphics g, AlignmentAnnotation aa,\r
571                             int sRes, int eRes,\r
572                             int y,\r
573                             float min, float max,\r
574                             int graphHeight)\r
575   {\r
576     if(sRes>aa.annotations.length)\r
577       return;\r
578 \r
579 \r
580     eRes = Math.min(eRes, aa.annotations.length);\r
581 \r
582     int x = 0;\r
583 \r
584     //Adjustment for fastpaint to left\r
585     if(eRes<av.endRes)\r
586       eRes++;\r
587 \r
588     if(sRes==0)\r
589     {\r
590       sRes++;\r
591       x+=av.charWidth;\r
592     }\r
593 \r
594     int y1=y, y2=y;\r
595     float range = max - min;\r
596 \r
597     ////Draw origin\r
598     if(min<0)\r
599       y2 = (int)(y - (0-min / range)*graphHeight);\r
600 \r
601     g.setColor(Color.gray);\r
602     g.drawLine(x-av.charWidth,y2,(eRes-sRes)*av.charWidth,y2);\r
603 \r
604 \r
605     for (int j = sRes; j < eRes; j++)\r
606     {\r
607       if(aa.annotations[j]==null || aa.annotations[j-1]==null)\r
608       {\r
609         x+=av.charWidth;\r
610         continue;\r
611       }\r
612         g.setColor(aa.annotations[j].colour);\r
613         y1 = y - (int) (((aa.annotations[j-1].value-min) / range) * graphHeight);\r
614         y2 = y - (int) (((aa.annotations[j].value-min) / range) * graphHeight);\r
615         g.drawLine(x-av.charWidth/2, y1, x+av.charWidth/2, y2);\r
616         x += av.charWidth;\r
617      }\r
618 \r
619 \r
620      if(aa.threshold!=null)\r
621      {\r
622          g.setColor(aa.threshold.colour);\r
623          y2 = (int)(y - ((aa.threshold.value-min) / range)*aa.graphHeight);\r
624          g.drawLine(0,y2,(eRes-sRes)*av.charWidth,y2);\r
625      }\r
626 \r
627   }\r
628 \r
629   public void drawBarGraph(Graphics g, AlignmentAnnotation aa,\r
630                            int sRes, int eRes,\r
631                            float min, float max,\r
632                            int y)\r
633   {\r
634     if(sRes>aa.annotations.length)\r
635       return;\r
636 \r
637 \r
638     eRes = Math.min(eRes, aa.annotations.length);\r
639 \r
640     int x=0, y1, y2;\r
641 \r
642     float range = max - min;\r
643 \r
644     y1 = y2 = y;\r
645 \r
646     if(min<0)\r
647       y2 = (int)(y - (0-min / (range))*aa.graphHeight);\r
648 \r
649     g.setColor(Color.gray);\r
650 \r
651     g.drawLine(x,y2,(eRes-sRes)*av.charWidth,y2);\r
652 \r
653     for (int j = sRes; j < eRes; j++)\r
654     {\r
655 \r
656       if (aa.annotations[j] == null)\r
657       {\r
658         x += av.charWidth;\r
659         continue;\r
660       }\r
661 \r
662         g.setColor(aa.annotations[j].colour);\r
663         y1 = y - (int) (((aa.annotations[j].value-min) / (range)) * aa.graphHeight);\r
664 \r
665         if(y1-y2>0)\r
666           g.fillRect(x, y2, av.charWidth, y1-y2 );\r
667         else\r
668           g.fillRect(x, y1, av.charWidth, y2-y1 );\r
669 \r
670         x += av.charWidth;\r
671     }\r
672 \r
673 \r
674     if(aa.threshold!=null)\r
675     {\r
676         g.setColor(aa.threshold.colour);\r
677         y2 = (int)(y - ((aa.threshold.value-min) / range)*aa.graphHeight);\r
678         g.drawLine(0,y2,(eRes-sRes)*av.charWidth,y2);\r
679     }\r
680 \r
681 \r
682   }\r
683 \r
684   // used by overview window\r
685   public void drawGraph(Graphics g, AlignmentAnnotation aa, int width, int y, int sRes, int eRes)\r
686   {\r
687       g.setColor(Color.white);\r
688       g.fillRect(0, 0, width, y);\r
689       g.setColor(new Color(0, 0, 180));\r
690 \r
691       int x = 0, height;\r
692 \r
693       for (int j = sRes; j < eRes; j++)\r
694       {\r
695           g.setColor(aa.annotations[j].colour);\r
696 \r
697           height = (int) ((aa.annotations[j].value / aa.graphMax) * GRAPH_HEIGHT);\r
698           if(height>y)\r
699               height = y;\r
700           g.fillRect(x, y - height, av.charWidth, height);\r
701           x += av.charWidth;\r
702       }\r
703     }\r
704 }\r