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