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