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