last version stay many bugs ..
[jalview.git] / src / jalview / renderer / AnnotationRenderer.java
1
2 package jalview.renderer;
3
4 import jalview.analysis.AAFrequency;
5 import jalview.analysis.StructureFrequency;
6 import jalview.api.AlignViewportI;
7 import jalview.datamodel.AlignmentAnnotation;
8 import jalview.datamodel.Annotation;
9 import jalview.datamodel.ColumnSelection;
10 import jalview.schemes.ColourSchemeI;
11
12 import java.awt.BasicStroke;
13 import java.awt.Color;
14 import java.awt.Font;
15 import java.awt.FontMetrics;
16 import java.awt.Graphics;
17 import java.awt.Graphics2D;
18 import java.awt.Image;
19 import java.awt.font.LineMetrics;
20 import java.awt.geom.AffineTransform;
21 import java.awt.image.ImageObserver;
22 import java.util.Hashtable;
23
24 import com.stevesoft.pat.Regex;
25
26 public class AnnotationRenderer
27 {
28
29   public AnnotationRenderer()
30   {
31     // TODO Auto-generated constructor stub
32   }
33
34   public void drawStemAnnot(Graphics g, Annotation[] row_annotations,
35           int lastSSX, int x, int y, int iconOffset, int startRes,
36           int column, boolean validRes, boolean validEnd)
37   {
38         
39     g.setColor(STEM_COLOUR);
40     int sCol = (lastSSX / charWidth) + startRes;
41     int x1 = lastSSX;
42     int x2 = (x * charWidth);
43     Regex closeparen = new Regex("(\\))");
44
45     String dc = (column == 0 || row_annotations[column - 1] == null) ? ""
46             : row_annotations[column - 1].displayCharacter;
47
48     boolean diffupstream = sCol == 0 || row_annotations[sCol - 1] == null
49             || !dc.equals(row_annotations[sCol - 1].displayCharacter);
50     boolean diffdownstream = !validRes || !validEnd
51             || row_annotations[column] == null
52             || !dc.equals(row_annotations[column].displayCharacter);
53     // System.out.println("Column "+column+" diff up: "+diffupstream+" down:"+diffdownstream);
54     // If a closing base pair half of the stem, display a backward arrow
55     if (column > 0 && closeparen.search(dc))
56     {
57         
58       if (diffupstream)
59       // if (validRes && column>1 && row_annotations[column-2]!=null &&
60       // dc.equals(row_annotations[column-2].displayCharacter))
61       {
62         g.fillPolygon(new int[]
63         { lastSSX + 5, lastSSX + 5, lastSSX }, new int[]
64         { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3);
65         x1 += 5;
66       }
67       if (diffdownstream)
68       {
69         x2 -= 1;
70       }
71     }
72     else
73     {
74         
75       // display a forward arrow
76       if (diffdownstream)
77       {
78         g.fillPolygon(new int[]
79         { x2 - 5, x2 - 5, x2 }, new int[]
80         { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3);
81         x2 -= 5;
82       }
83       if (diffupstream)
84       {
85         x1 += 1;
86       }
87     }
88     // draw arrow body
89     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 7);
90   }
91
92   private int charWidth, endRes, charHeight;
93
94   private boolean validCharWidth, hasHiddenColumns;
95
96   private FontMetrics fm;
97
98   private final boolean MAC = new jalview.util.Platform().isAMac();
99
100   boolean av_renderHistogram = true, av_renderProfile = true,
101           av_normaliseProfile = false;
102
103   ColourSchemeI profcolour = null;
104
105   private ColumnSelection columnSelection;
106
107   private Hashtable[] hconsensus;
108
109   private Hashtable[] hStrucConsensus;
110
111   private boolean av_ignoreGapsConsensus;
112
113   /**
114    * attributes set from AwtRenderPanelI
115    */
116   /**
117    * old image used when data is currently being calculated and cannot be
118    * rendered
119    */
120   private Image fadedImage;
121
122   /**
123    * panel being rendered into
124    */
125   private ImageObserver annotationPanel;
126
127   /**
128    * width of image to render in panel
129    */
130   private int imgWidth;
131
132   
133   public void drawNotCanonicalAnnot(Graphics g, Color nonCanColor, Annotation[] row_annotations,
134           int lastSSX, int x, int y, int iconOffset, int startRes,
135           int column, boolean validRes, boolean validEnd)
136   {
137         //System.out.println(nonCanColor);
138         
139     g.setColor(nonCanColor);
140     int sCol = (lastSSX / charWidth) + startRes;
141     int x1 = lastSSX;
142     int x2 = (x * charWidth);
143     Regex closeparen = new Regex("}|]|<|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z");
144     
145     String dc = (column == 0 || row_annotations[column - 1] == null) ? ""
146             : row_annotations[column - 1].displayCharacter;
147
148     boolean diffupstream = sCol == 0 || row_annotations[sCol - 1] == null
149             || !dc.equals(row_annotations[sCol - 1].displayCharacter);
150     boolean diffdownstream = !validRes || !validEnd
151             || row_annotations[column] == null
152             || !dc.equals(row_annotations[column].displayCharacter);
153     // System.out.println("Column "+column+" diff up: "+diffupstream+" down:"+diffdownstream);
154     // If a closing base pair half of the stem, display a backward arrow
155     if (column > 0 && closeparen.search(dc))//  closeletter_b.search(dc)||closeletter_c.search(dc)||closeletter_d.search(dc)||closecrochet.search(dc)) )
156     {
157         
158       if (diffupstream)
159       // if (validRes && column>1 && row_annotations[column-2]!=null &&
160       // dc.equals(row_annotations[column-2].displayCharacter))
161       {
162         g.fillPolygon(new int[]
163         { lastSSX + 5, lastSSX + 5, lastSSX }, new int[]
164         { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3);
165         x1 += 5;
166       }
167       if (diffdownstream)
168       {
169         x2 -= 1;
170       }
171     }
172     else
173     {
174         
175       // display a forward arrow
176       if (diffdownstream)
177       {
178         g.fillPolygon(new int[]
179         { x2 - 5, x2 - 5, x2 }, new int[]
180         { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3);
181         x2 -= 5;
182       }
183       if (diffupstream)
184       {
185         x1 += 1;
186       }
187     }
188     // draw arrow body
189     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 7);
190   }
191   // public void updateFromAnnotationPanel(FontMetrics annotFM, AlignViewportI
192   // av)
193   public void updateFromAwtRenderPanel(AwtRenderPanelI annotPanel,
194           AlignViewportI av)
195   {
196     fm = annotPanel.getFontMetrics();
197     annotationPanel = annotPanel;
198     fadedImage = annotPanel.getFadedImage();
199     imgWidth = annotPanel.getFadedImageWidth();
200     updateFromAlignViewport(av);
201   }
202
203   public void updateFromAlignViewport(AlignViewportI av)
204   {
205     charWidth = av.getCharWidth();
206     endRes = av.getEndRes();
207     charHeight = av.getCharHeight();
208     hasHiddenColumns = av.hasHiddenColumns();
209     validCharWidth = av.isValidCharWidth();
210     av_renderHistogram = av.isShowConsensusHistogram();
211     av_renderProfile = av.isShowSequenceLogo();
212     av_normaliseProfile = av.isNormaliseSequenceLogo();
213     profcolour = av.getGlobalColourScheme();
214     if (profcolour == null)
215     {
216       // Set the default colour for sequence logo if the alignnent has no
217       // colourscheme set
218       profcolour = av.getAlignment().isNucleotide() ? new jalview.schemes.NucleotideColourScheme()
219               : new jalview.schemes.ZappoColourScheme();
220     }
221     columnSelection = av.getColumnSelection();
222     hconsensus = av.getSequenceConsensusHash();// hconsensus;
223     hStrucConsensus = av.getRnaStructureConsensusHash(); // hStrucConsensus;
224     av_ignoreGapsConsensus = av.getIgnoreGapsConsensus();
225   }
226
227   public int[] getProfileFor(AlignmentAnnotation aa, int column)
228   {
229     // TODO : consider refactoring the global alignment calculation
230     // properties/rendering attributes as a global 'alignment group' which holds
231     // all vis settings for the alignment as a whole rather than a subset
232     //
233     if (aa.autoCalculated && aa.label.startsWith("Consensus"))
234     {
235       if (aa.groupRef != null && aa.groupRef.consensusData != null
236               && aa.groupRef.isShowSequenceLogo())
237       {
238         return AAFrequency.extractProfile(
239                 aa.groupRef.consensusData[column],
240                 aa.groupRef.getIgnoreGapsConsensus());
241       }
242       // TODO extend annotation row to enable dynamic and static profile data to
243       // be stored
244       if (aa.groupRef == null && aa.sequenceRef == null && av_renderProfile)
245       {
246         return AAFrequency.extractProfile(hconsensus[column],
247                 av_ignoreGapsConsensus);
248       }
249     }
250     else
251     {
252       if (aa.autoCalculated && aa.label.startsWith("StrucConsensus"))
253       {
254         // TODO implement group structure consensus
255         /*
256          * if (aa.groupRef != null && aa.groupRef.consensusData != null &&
257          * aa.groupRef.isShowSequenceLogo()) { //TODO check what happens for
258          * group selections return StructureFrequency.extractProfile(
259          * aa.groupRef.consensusData[column], aa.groupRef
260          * .getIgnoreGapsConsensus()); }
261          */
262         // TODO extend annotation row to enable dynamic and static profile data
263         // to
264         // be stored
265         if (aa.groupRef == null && aa.sequenceRef == null
266                 && av_renderProfile && hStrucConsensus != null
267                 && hStrucConsensus.length > column)
268         {
269           return StructureFrequency.extractProfile(hStrucConsensus[column],
270                   av_ignoreGapsConsensus);
271         }
272       }
273     }
274     return null;
275   }
276
277   /**
278    * Render the annotation rows associated with an alignment.
279    * @param annotPanel
280    *          container frame
281    * @param av
282    *          data and view settings to render
283    * @param g
284    *          destination for graphics
285    * @param activeRow
286    *          row where a mouse event occured (or -1)
287    * @param startRes
288    *          first column that will be drawn
289    * @param endRes
290    *          last column that will be drawn
291    * @return true if the fadedImage was used for any alignment annotation rows currently being calculated
292    */
293   public boolean drawComponent(AwtRenderPanelI annotPanel, AlignViewportI av,
294           Graphics g, int activeRow, int startRes, int endRes)
295   {
296     boolean usedFaded=false;
297     // NOTES:
298     // AnnotationPanel needs to implement: ImageObserver, access to
299     // AlignViewport
300     updateFromAwtRenderPanel(annotPanel, av);
301     fm = g.getFontMetrics();
302     AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
303
304     int x = 0, y = 0;
305     int column = 0;
306     char lastSS;
307     int lastSSX;
308     int iconOffset = 0;
309     boolean validRes = false;
310     boolean validEnd = false;
311     boolean labelAllCols = false;
312     boolean centreColLabels, centreColLabelsDef = av
313             .getCentreColumnLabels();
314     boolean scaleColLabel = false;
315     boolean[] graphGroupDrawn = new boolean[aa.length];
316     int charOffset = 0; // offset for a label
317     float fmWidth, fmScaling = 1f; // scaling for a label to fit it into a
318     // column.
319     Font ofont = g.getFont();
320     // \u03B2 \u03B1
321     for (int i = 0; i < aa.length; i++)
322     {
323       AlignmentAnnotation row = aa[i];
324       Annotation[] row_annotations=row.annotations;
325       if (!row.visible)
326       {
327         continue;
328       }
329       centreColLabels = row.centreColLabels || centreColLabelsDef;
330       labelAllCols = row.showAllColLabels;
331       scaleColLabel = row.scaleColLabel;
332       lastSS = ' ';
333       lastSSX = 0;
334       if (row.graph > 0)
335       {
336         if (row.graphGroup > -1 && graphGroupDrawn[row.graphGroup])
337         {
338           continue;
339         }
340
341         // this is so that we draw the characters below the graph
342         y += row.height;
343
344         if (row.hasText)
345         {
346           iconOffset = charHeight - fm.getDescent();
347           y -= charHeight;
348         }
349       }
350       else if (row.hasText)
351       {
352         iconOffset = charHeight - fm.getDescent();
353
354       }
355       else
356       {
357         iconOffset = 0;
358       }
359
360       if (row.autoCalculated && av.isCalculationInProgress(row))
361       {
362         y += charHeight;
363         usedFaded=true;
364         g.drawImage(fadedImage, 0, y - row.height, imgWidth, y, 0, y
365                 - row.height, imgWidth, y, annotationPanel);
366         g.setColor(Color.black);
367         // g.drawString("Calculating "+aa[i].label+"....",20, y-row.height/2);
368
369         continue;
370       }
371
372       /*
373        * else if (annotationPanel.av.updatingConservation &&
374        * aa[i].label.equals("Conservation")) {
375        *
376        * y += charHeight; g.drawImage(annotationPanel.fadedImage, 0, y -
377        * row.height, annotationPanel.imgWidth, y, 0, y - row.height,
378        * annotationPanel.imgWidth, y, annotationPanel);
379        *
380        * g.setColor(Color.black); //
381        * g.drawString("Calculating Conservation.....",20, y-row.height/2);
382        *
383        * continue; } else if (annotationPanel.av.updatingConservation &&
384        * aa[i].label.equals("Quality")) {
385        *
386        * y += charHeight; g.drawImage(annotationPanel.fadedImage, 0, y -
387        * row.height, annotationPanel.imgWidth, y, 0, y - row.height,
388        * annotationPanel.imgWidth, y, annotationPanel); g.setColor(Color.black);
389        * // / g.drawString("Calculating Quality....",20, y-row.height/2);
390        *
391        * continue; }
392        */
393       // first pass sets up state for drawing continuation from left-hand column
394       // of startRes
395       x = (startRes == 0) ? 0 : -1;
396       while (x < endRes - startRes)
397       {
398         if (hasHiddenColumns)
399         {
400           column = columnSelection.adjustForHiddenColumns(startRes + x);
401           if (column > row_annotations.length - 1)
402           {
403             break;
404           }
405         }
406         else
407         {
408           column = startRes + x;
409         }
410
411         if ((row_annotations == null) || (row_annotations.length <= column)
412                 || (row_annotations[column] == null))
413         {
414           validRes = false;
415         }
416         else
417         {
418           validRes = true;
419         }
420         if (x > -1)
421         {
422           if (activeRow == i)
423           {
424             g.setColor(Color.red);
425
426             if (columnSelection != null)
427             {
428               for (int n = 0; n < columnSelection.size(); n++)
429               {
430                 int v = columnSelection.columnAt(n);
431
432                 if (v == column)
433                 {
434                   g.fillRect(x * charWidth, y, charWidth, charHeight);
435                 }
436               }
437             }
438           }
439           if (!row.isValidStruc())
440           {
441             g.setColor(Color.orange);
442             g.fillRect((int) row.getInvalidStrucPos() * charWidth, y,
443                     charWidth, charHeight);
444           }
445           if (validCharWidth
446                   && validRes
447                   && row_annotations[column].displayCharacter != null
448                   && (row_annotations[column].displayCharacter.length() > 0))
449           {
450
451             if (centreColLabels || scaleColLabel)
452             {
453               fmWidth = fm.charsWidth(
454                       row_annotations[column].displayCharacter
455                               .toCharArray(), 0,
456                       row_annotations[column].displayCharacter.length());
457
458               if (scaleColLabel)
459               {
460                 // justify the label and scale to fit in column
461                 if (fmWidth > charWidth)
462                 {
463                   // scale only if the current font isn't already small enough
464                   fmScaling = charWidth;
465                   fmScaling /= fmWidth;
466                   g.setFont(ofont.deriveFont(AffineTransform
467                           .getScaleInstance(fmScaling, 1.0)));
468                   // and update the label's width to reflect the scaling.
469                   fmWidth = charWidth;
470                 }
471               }
472             }
473             else
474             {
475               fmWidth = fm
476                       .charWidth(row_annotations[column].displayCharacter
477                               .charAt(0));
478             }
479             charOffset = (int) ((charWidth - fmWidth) / 2f);
480
481             if (row_annotations[column].colour == null)
482               g.setColor(Color.black);
483             else
484               g.setColor(row_annotations[column].colour);
485
486             if (column == 0 || row.graph > 0)
487             {
488               g.drawString(row_annotations[column].displayCharacter,
489                       (x * charWidth) + charOffset, y + iconOffset);
490             }
491             else if (row_annotations[column - 1] == null
492                     || (labelAllCols
493                             || !row_annotations[column].displayCharacter
494                                     .equals(row_annotations[column - 1].displayCharacter) || (row_annotations[column].displayCharacter
495                             .length() < 2 && row_annotations[column].secondaryStructure == ' ')))
496             {
497                 g.drawString(row_annotations[column].displayCharacter
498                           , x
499                       * charWidth + charOffset, y + iconOffset);
500             }
501             g.setFont(ofont);
502           }
503         }
504         if (row.hasIcons)
505         {
506           char ss = validRes ? row_annotations[column].secondaryStructure
507                   : '-';
508           
509           if (ss == '(')
510           {
511             // distinguish between forward/backward base-pairing
512             if (row_annotations[column].displayCharacter.indexOf(')') > -1)
513             {
514             
515               ss = ')';
516               
517             }
518           }
519            if (ss == '[')
520           {
521             if ((row_annotations[column].displayCharacter.indexOf(']') > -1))
522             {
523                 ss = ']';
524                 
525                 
526             }
527           }
528            if (ss == '{')
529            {
530              // distinguish between forward/backward base-pairing
531              if (row_annotations[column].displayCharacter.indexOf('}') > -1)
532              {
533                ss = '}';
534                
535                
536              }
537            }
538            if (ss == '<')
539            {
540              // distinguish between forward/backward base-pairing
541              if (row_annotations[column].displayCharacter.indexOf('<') > -1)
542              {
543                ss = '>';
544                
545                
546              }
547            }
548            if (ss == 'A')
549            {
550              // distinguish between forward/backward base-pairing
551              if (row_annotations[column].displayCharacter.indexOf('a') > -1)
552              {
553                ss = 'a';
554               
555                
556              }
557            }
558            
559            if (ss == 'B')
560            {
561              // distinguish between forward/backward base-pairing
562              if (row_annotations[column].displayCharacter.indexOf('b') > -1)
563              {
564                ss = 'b';
565                
566              }
567            }
568            
569            if (ss == 'C')
570            {
571              // distinguish between forward/backward base-pairing
572              if (row_annotations[column].displayCharacter.indexOf('c') > -1)
573              {
574                ss = 'c';
575                
576              }
577            }
578            if (ss == 'D')
579            {
580              // distinguish between forward/backward base-pairing
581              if (row_annotations[column].displayCharacter.indexOf('d') > -1)
582              {
583                ss = 'd';
584                
585              }
586            }
587            if (ss == '1')
588            {
589              // distinguish between forward/backward base-pairing
590              if (row_annotations[column].displayCharacter.indexOf('e') > -1)
591              {
592                ss = 'e';
593                
594              }
595            }
596            if (ss == 'F')
597            {
598              // distinguish between forward/backward base-pairing
599              if (row_annotations[column].displayCharacter.indexOf('f') > -1)
600              {
601                ss = 'f';
602                
603              }
604            }
605            if (ss == 'G')
606            {
607              // distinguish between forward/backward base-pairing
608              if (row_annotations[column].displayCharacter.indexOf('g') > -1)
609              {
610                ss = 'g';
611                
612              }
613            }
614            if (ss == '2')
615            {
616              // distinguish between forward/backward base-pairing
617              if (row_annotations[column].displayCharacter.indexOf('h') > -1)
618              {
619                ss = 'h';
620                
621              }
622            }
623            if (ss == 'I')
624            {
625              // distinguish between forward/backward base-pairing
626              if (row_annotations[column].displayCharacter.indexOf('i') > -1)
627              {
628                ss = 'i';
629                
630              }
631            }
632            if (ss == 'J')
633            {
634              // distinguish between forward/backward base-pairing
635              if (row_annotations[column].displayCharacter.indexOf('j') > -1)
636              {
637                ss = 'j';
638                
639              }
640            }
641            if (ss == 'K')
642            {
643              // distinguish between forward/backward base-pairing
644              if (row_annotations[column].displayCharacter.indexOf('k') > -1)
645              {
646                ss = 'k';
647                
648              }
649            }
650            if (ss == 'L')
651            {
652              // distinguish between forward/backward base-pairing
653              if (row_annotations[column].displayCharacter.indexOf('l') > -1)
654              {
655                ss = 'l';
656                
657              }
658            }
659            if (ss == 'M')
660            {
661              // distinguish between forward/backward base-pairing
662              if (row_annotations[column].displayCharacter.indexOf('m') > -1)
663              {
664                ss = 'm';
665                
666              }
667            }
668            if (ss == 'N')
669            {
670              // distinguish between forward/backward base-pairing
671              if (row_annotations[column].displayCharacter.indexOf('n') > -1)
672              {
673                ss = 'n';
674                
675              }
676            }
677            if (ss == 'O')
678            {
679              // distinguish between forward/backward base-pairing
680              if (row_annotations[column].displayCharacter.indexOf('o') > -1)
681              {
682                ss = 'o';
683                
684              }
685            }
686            if (ss == 'P')
687            {
688              // distinguish between forward/backward base-pairing
689              if (row_annotations[column].displayCharacter.indexOf('p') > -1)
690              {
691                ss = 'p';
692                
693              }
694            }
695            if (ss == 'Q')
696            {
697              // distinguish between forward/backward base-pairing
698              if (row_annotations[column].displayCharacter.indexOf('q') > -1)
699              {
700                ss = 'q';
701                
702              }
703            }
704            if (ss == 'R')
705            {
706              // distinguish between forward/backward base-pairing
707              if (row_annotations[column].displayCharacter.indexOf('r') > -1)
708              {
709                ss = 'r';
710                
711              }
712            }
713            if (ss == 'S')
714            {
715              // distinguish between forward/backward base-pairing
716              if (row_annotations[column].displayCharacter.indexOf('s') > -1)
717              {
718                ss = 's';
719                
720              }
721            }
722            if (ss == 'T')
723            {
724              // distinguish between forward/backward base-pairing
725              if (row_annotations[column].displayCharacter.indexOf('t') > -1)
726              {
727                ss = 't';
728                
729              }
730            }
731            if (ss == 'U')
732            {
733              // distinguish between forward/backward base-pairing
734              if (row_annotations[column].displayCharacter.indexOf('u') > -1)
735              {
736                ss = 'u';
737                
738              }
739            }
740            if (ss == 'V')
741            {
742              // distinguish between forward/backward base-pairing
743              if (row_annotations[column].displayCharacter.indexOf('v') > -1)
744              {
745                ss = 'v';
746                
747              }
748            }
749            if (ss == 'W')
750            {
751              // distinguish between forward/backward base-pairing
752              if (row_annotations[column].displayCharacter.indexOf('w') > -1)
753              {
754                ss = 'w';
755                
756              }
757            }
758            if (ss == 'X')
759            {
760              // distinguish between forward/backward base-pairing
761              if (row_annotations[column].displayCharacter.indexOf('x') > -1)
762              {
763                ss = 'x';
764                
765              }
766            }
767            if (ss == 'Y')
768            {
769              // distinguish between forward/backward base-pairing
770              if (row_annotations[column].displayCharacter.indexOf('y') > -1)
771              {
772                ss = 'y';
773                
774              }
775            }
776            if (ss == 'Z')
777            {
778              // distinguish between forward/backward base-pairing
779              if (row_annotations[column].displayCharacter.indexOf('z') > -1)
780              {
781                ss = 'z';
782                
783              }
784            }
785           if (!validRes || (ss != lastSS))
786           {
787             if (x > -1)
788             {
789               switch (lastSS)
790               {
791               case 'H':
792                 drawHelixAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
793                         column, validRes, validEnd);
794                 break;
795
796               case 'E':
797                 drawSheetAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
798                         column, validRes, validEnd);
799                 break;
800
801               case '(': // Stem case for RNA secondary structure
802               case ')': // and opposite direction
803                 drawStemAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
804                         column, validRes, validEnd);
805                 break;
806               case '{':
807               case '}':
808               case '[':
809               case ']':
810               case '>':
811               case '<':
812               case 'A':
813               case 'a':
814               case 'B':
815               case 'b':
816               case 'C':
817               case 'c':
818               case 'D':
819               case 'd':
820               case '1':
821               case 'e':
822               case 'F':
823               case 'f':
824               case 'G':
825               case 'g':
826               case '2':
827               case 'h':
828               case 'I':
829               case 'i':
830               case 'J':
831               case 'j':
832               case 'K':
833               case 'k':
834               case 'L':
835               case 'l':
836               case 'M':
837               case 'm':
838               case 'N':
839               case 'n':
840               case 'O':
841               case 'o':
842               case 'P':
843               case 'p':
844               case 'Q':
845               case 'q':
846               case 'R':
847               case 'r':
848               case 'S':
849               case 's':
850               case 'T':
851               case 't':
852               case 'U':
853               case 'u':
854               case 'V':
855               case 'v':
856               case 'W':
857               case 'w':
858               case 'X':
859               case 'x':
860               case 'Y':
861               case 'y':
862               case 'Z':
863               case 'z':
864                   //System.out.println(lastSS);
865                   Color nonCanColor= getNotCanonicalColor(lastSS);
866                   drawNotCanonicalAnnot(g, nonCanColor, row_annotations, lastSSX, x, y, iconOffset, startRes,
867                           column, validRes, validEnd);
868                   break;
869               default:
870                 g.setColor(Color.gray);
871                 g.fillRect(lastSSX, y + 6 + iconOffset, (x * charWidth)
872                         - lastSSX, 2);
873
874                 break;
875               }
876             }
877             if (validRes)
878             {
879               lastSS = ss;
880             }
881             else
882             {
883               lastSS = ' ';
884             }
885             if (x > -1)
886             {
887               lastSSX = (x * charWidth);
888             }
889           }
890         }
891         column++;
892         x++;
893       }
894       if (column >= row_annotations.length)
895       {
896         column = row_annotations.length - 1;
897         validEnd = false;
898       }
899       else
900       {
901         validEnd = true;
902       }
903       if ((row_annotations == null) || (row_annotations.length <= column)
904               || (row_annotations[column] == null))
905       {
906         validRes = false;
907       }
908       else
909       {
910         validRes = true;
911       }
912
913       // x ++;
914
915       if (row.hasIcons)
916       {
917         switch (lastSS)
918         {
919         case 'H':
920           drawHelixAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
921                   column, validRes, validEnd);
922           break;
923
924         case 'E':
925           drawSheetAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
926                   column, validRes, validEnd);
927           break;
928         case 's':
929         case 'S': // Stem case for RNA secondary structure
930           drawStemAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
931                   column, validRes, validEnd);
932           break;
933         case '{':
934         case '}':
935         case '[':
936         case ']':
937         case '>':
938         case '<':
939         case 'A':
940         case 'a':
941         case 'B':
942         case 'b':
943         case 'C':
944         case 'c':
945         case 'D':
946         case 'd':
947         case '1':
948         case 'e':
949         case 'F':
950         case 'f':
951         case 'G':
952         case 'g':
953         case '2':
954         case 'h':
955         case 'I':
956         case 'i':
957         case 'J':
958         case 'j':
959         case 'K':
960         case 'k':
961         case 'L':
962         case 'l':
963         case 'M':
964         case 'm':
965         case 'N':
966         case 'n':
967         case 'O':
968         case 'o':
969         case 'P':
970         case 'p':
971         case 'Q':
972         case 'q':
973         case 'R':
974         case 'r':
975         case 'T':
976         case 't':
977         case 'U':
978         case 'u':
979         case 'V':
980         case 'v':
981         case 'W':
982         case 'w':
983         case 'X':
984         case 'x':
985         case 'Y':
986         case 'y':
987         case 'Z':
988         case 'z':
989                 //System.out.println(lastSS);
990           Color nonCanColor = getNotCanonicalColor(lastSS);
991           drawNotCanonicalAnnot(g,nonCanColor, row_annotations, lastSSX, x, y, iconOffset, startRes,
992                     column, validRes, validEnd);
993           break;
994         default:
995           drawGlyphLine(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
996                   column, validRes, validEnd);
997           break;
998         }
999       }
1000
1001       if (row.graph > 0 && row.graphHeight > 0)
1002       {
1003         if (row.graph == AlignmentAnnotation.LINE_GRAPH)
1004         {
1005           if (row.graphGroup > -1 && !graphGroupDrawn[row.graphGroup])
1006           {
1007             float groupmax = -999999, groupmin = 9999999;
1008             for (int gg = 0; gg < aa.length; gg++)
1009             {
1010               if (aa[gg].graphGroup != row.graphGroup)
1011               {
1012                 continue;
1013               }
1014
1015               if (aa[gg] != row)
1016               {
1017                 aa[gg].visible = false;
1018               }
1019
1020               if (aa[gg].graphMax > groupmax)
1021               {
1022                 groupmax = aa[gg].graphMax;
1023               }
1024               if (aa[gg].graphMin < groupmin)
1025               {
1026                 groupmin = aa[gg].graphMin;
1027               }
1028             }
1029
1030             for (int gg = 0; gg < aa.length; gg++)
1031             {
1032               if (aa[gg].graphGroup == row.graphGroup)
1033               {
1034                 drawLineGraph(g, aa[gg], aa[gg].annotations, startRes, endRes, y, groupmin,
1035                         groupmax, row.graphHeight);
1036               }
1037             }
1038
1039             graphGroupDrawn[row.graphGroup] = true;
1040           }
1041           else
1042           {
1043             drawLineGraph(g, row, row_annotations, startRes, endRes, y, row.graphMin,
1044                     row.graphMax, row.graphHeight);
1045           }
1046         }
1047         else if (row.graph == AlignmentAnnotation.BAR_GRAPH)
1048         {
1049           drawBarGraph(g, row, row_annotations, startRes, endRes, row.graphMin,
1050                   row.graphMax, y);
1051         }
1052       }
1053
1054       if (row.graph > 0 && row.hasText)
1055       {
1056         y += charHeight;
1057       }
1058
1059       if (row.graph == 0)
1060       {
1061         y += aa[i].height;
1062       }
1063     }
1064     return !usedFaded;
1065   }
1066
1067   private final Color GLYPHLINE_COLOR = Color.gray;
1068
1069   private final Color SHEET_COLOUR = Color.green;
1070
1071   private final Color HELIX_COLOUR = Color.red;
1072
1073   private final Color STEM_COLOUR = Color.blue;
1074   
1075   private  Color sdNOTCANONICAL_COLOUR;
1076
1077   public void drawGlyphLine(Graphics g, Annotation[] row,
1078           int lastSSX, int x, int y, int iconOffset, int startRes,
1079           int column, boolean validRes, boolean validEnd)
1080   {
1081     g.setColor(GLYPHLINE_COLOR);
1082     g.fillRect(lastSSX, y + 6 + iconOffset, (x * charWidth) - lastSSX, 2);
1083   }
1084
1085   public void drawSheetAnnot(Graphics g, Annotation[] row,
1086
1087           int lastSSX, int x, int y, int iconOffset, int startRes,
1088           int column, boolean validRes, boolean validEnd)
1089   {
1090     g.setColor(SHEET_COLOUR);
1091
1092     if (!validEnd || !validRes || row==null || row[column] == null
1093             || row[column].secondaryStructure != 'E')
1094     {
1095       g.fillRect(lastSSX, y + 4 + iconOffset,
1096               (x * charWidth) - lastSSX - 4, 7);
1097       g.fillPolygon(new int[]
1098       { (x * charWidth) - 4, (x * charWidth) - 4, (x * charWidth) },
1099               new int[]
1100               { y + iconOffset, y + 14 + iconOffset, y + 7 + iconOffset },
1101               3);
1102     }
1103     else
1104     {
1105       g.fillRect(lastSSX, y + 4 + iconOffset,
1106               (x + 1) * charWidth - lastSSX, 7);
1107     }
1108
1109   }
1110
1111   public void drawHelixAnnot(Graphics g, Annotation[] row,
1112           int lastSSX, int x, int y, int iconOffset, int startRes,
1113           int column, boolean validRes, boolean validEnd)
1114   {
1115     g.setColor(HELIX_COLOUR);
1116
1117     int sCol = (lastSSX / charWidth) + startRes;
1118     int x1 = lastSSX;
1119     int x2 = (x * charWidth);
1120
1121     if (MAC)
1122     {
1123       int ofs = charWidth / 2;
1124       // Off by 1 offset when drawing rects and ovals
1125       // to offscreen image on the MAC
1126       g.fillRoundRect(lastSSX, y + 4 + iconOffset, x2 - x1, 8, 8, 8);
1127       if (sCol == 0 || row[sCol - 1] == null
1128               || row[sCol - 1].secondaryStructure != 'H')
1129       {
1130       }
1131       else
1132       {
1133         // g.setColor(Color.orange);
1134         g.fillRoundRect(lastSSX, y + 4 + iconOffset, x2 - x1 - ofs + 1, 8,
1135                 0, 0);
1136       }
1137       if (!validRes || row[column] == null
1138               || row[column].secondaryStructure != 'H')
1139       {
1140
1141       }
1142       else
1143       {
1144         // g.setColor(Color.magenta);
1145         g.fillRoundRect(lastSSX + ofs, y + 4 + iconOffset, x2 - x1 - ofs
1146                 + 1, 8, 0, 0);
1147
1148       }
1149
1150       return;
1151     }
1152
1153     if (sCol == 0 || row[sCol - 1] == null
1154             || row[sCol - 1].secondaryStructure != 'H')
1155     {
1156       g.fillArc(lastSSX, y + 4 + iconOffset, charWidth, 8, 90, 180);
1157       x1 += charWidth / 2;
1158     }
1159
1160     if (!validRes || row[column] == null
1161             || row[column].secondaryStructure != 'H')
1162     {
1163       g.fillArc((x * charWidth) - charWidth, y + 4 + iconOffset, charWidth,
1164               8, 270, 180);
1165       x2 -= charWidth / 2;
1166     }
1167
1168     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 8);
1169   }
1170
1171   public void drawLineGraph(Graphics g, AlignmentAnnotation _aa, Annotation[] aa_annotations, int sRes,
1172           int eRes, int y, float min, float max, int graphHeight)
1173   {
1174     if (sRes > aa_annotations.length)
1175     {
1176       return;
1177     }
1178
1179     int x = 0;
1180
1181     // Adjustment for fastpaint to left
1182     if (eRes < endRes)
1183     {
1184       eRes++;
1185     }
1186
1187     eRes = Math.min(eRes, aa_annotations.length);
1188
1189     if (sRes == 0)
1190     {
1191       x++;
1192     }
1193
1194     int y1 = y, y2 = y;
1195     float range = max - min;
1196
1197     // //Draw origin
1198     if (min < 0)
1199     {
1200       y2 = y - (int) ((0 - min / range) * graphHeight);
1201     }
1202
1203     g.setColor(Color.gray);
1204     g.drawLine(x - charWidth, y2, (eRes - sRes + 1) * charWidth, y2);
1205
1206     eRes = Math.min(eRes, aa_annotations.length);
1207
1208     int column;
1209     int aaMax = aa_annotations.length - 1;
1210
1211     while (x < eRes - sRes)
1212     {
1213       column = sRes + x;
1214       if (hasHiddenColumns)
1215       {
1216         column = columnSelection.adjustForHiddenColumns(column);
1217       }
1218
1219       if (column > aaMax)
1220       {
1221         break;
1222       }
1223
1224       if (aa_annotations[column] == null
1225               || aa_annotations[column - 1] == null)
1226       {
1227         x++;
1228         continue;
1229       }
1230
1231       if (aa_annotations[column].colour == null)
1232         g.setColor(Color.black);
1233       else
1234         g.setColor(aa_annotations[column].colour);
1235
1236       y1 = y
1237               - (int) (((aa_annotations[column - 1].value - min) / range) * graphHeight);
1238       y2 = y
1239               - (int) (((aa_annotations[column].value - min) / range) * graphHeight);
1240
1241       g.drawLine(x * charWidth - charWidth / 2, y1, x * charWidth
1242               + charWidth / 2, y2);
1243       x++;
1244     }
1245
1246     if (_aa.threshold != null)
1247     {
1248       g.setColor(_aa.threshold.colour);
1249       Graphics2D g2 = (Graphics2D) g;
1250       g2.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE,
1251               BasicStroke.JOIN_ROUND, 3f, new float[]
1252               { 5f, 3f }, 0f));
1253
1254       y2 = (int) (y - ((_aa.threshold.value - min) / range) * graphHeight);
1255       g.drawLine(0, y2, (eRes - sRes) * charWidth, y2);
1256       g2.setStroke(new BasicStroke());
1257     }
1258   }
1259
1260   public void drawBarGraph(Graphics g, AlignmentAnnotation _aa, Annotation[] aa_annotations, int sRes,
1261           int eRes, float min, float max, int y)
1262   {
1263     if (sRes > aa_annotations.length)
1264     {
1265       return;
1266     }
1267     Font ofont = g.getFont();
1268     eRes = Math.min(eRes, aa_annotations.length);
1269
1270     int x = 0, y1 = y, y2 = y;
1271
1272     float range = max - min;
1273
1274     if (min < 0)
1275     {
1276       y2 = y - (int) ((0 - min / (range)) * _aa.graphHeight);
1277     }
1278
1279     g.setColor(Color.gray);
1280
1281     g.drawLine(x, y2, (eRes - sRes) * charWidth, y2);
1282
1283     int column;
1284     int aaMax = aa_annotations.length - 1;
1285     boolean renderHistogram = true, renderProfile = true, normaliseProfile = false;
1286     // if (aa.autoCalculated && aa.label.startsWith("Consensus"))
1287     {
1288       // TODO: generalise this to have render styles for consensus/profile data
1289       if (_aa.groupRef != null)
1290       {
1291         renderHistogram = _aa.groupRef.isShowConsensusHistogram();
1292         renderProfile = _aa.groupRef.isShowSequenceLogo();
1293         normaliseProfile = _aa.groupRef.isNormaliseSequenceLogo();
1294       }
1295       else
1296       {
1297         renderHistogram = av_renderHistogram;
1298         renderProfile = av_renderProfile;
1299         normaliseProfile = av_normaliseProfile;
1300       }
1301     }
1302     while (x < eRes - sRes)
1303     {
1304       column = sRes + x;
1305       if (hasHiddenColumns)
1306       {
1307         column = columnSelection.adjustForHiddenColumns(column);
1308       }
1309
1310       if (column > aaMax)
1311       {
1312         break;
1313       }
1314
1315       if (aa_annotations[column] == null)
1316       {
1317         x++;
1318         continue;
1319       }
1320       if (aa_annotations[column].colour == null)
1321         g.setColor(Color.black);
1322       else
1323         g.setColor(aa_annotations[column].colour);
1324
1325       y1 = y
1326               - (int) (((aa_annotations[column].value - min) / (range)) * _aa.graphHeight);
1327
1328       if (renderHistogram)
1329       {
1330         if (y1 - y2 > 0)
1331         {
1332           g.fillRect(x * charWidth, y2, charWidth, y1 - y2);
1333         }
1334         else
1335         {
1336           g.fillRect(x * charWidth, y1, charWidth, y2 - y1);
1337         }
1338       }
1339       // draw profile if available
1340       if (renderProfile)
1341       {
1342
1343         int profl[] = getProfileFor(_aa, column);
1344         // just try to draw the logo if profl is not null
1345         if (profl != null && profl[1] != 0)
1346         {
1347           float ht = normaliseProfile ? y - _aa.graphHeight : y1;
1348           double htn = normaliseProfile ? _aa.graphHeight : (y2 - y1);// aa.graphHeight;
1349           double hght;
1350           float wdth;
1351           double ht2 = 0;
1352           char[] dc;
1353
1354           /**
1355            * profl.length == 74 indicates that the profile of a secondary
1356            * structure conservation row was accesed. Therefore dc gets length 2,
1357            * to have space for a basepair instead of just a single nucleotide
1358            */
1359           if (profl.length == 74)
1360           {
1361             dc = new char[2];
1362           }
1363           else
1364           {
1365             dc = new char[1];
1366           }
1367           LineMetrics lm = g.getFontMetrics(ofont).getLineMetrics("Q", g);
1368           double scale = 1f / (normaliseProfile ? profl[1] : 100f);
1369           float ofontHeight = 1f / lm.getAscent();// magnify to fill box
1370           double scl = 0.0;
1371           for (int c = 2; c < profl[0];)
1372           {
1373             dc[0] = (char) profl[c++];
1374
1375             if (_aa.label.startsWith("StrucConsensus"))
1376             {
1377               dc[1] = (char) profl[c++];
1378             }
1379
1380             wdth = charWidth;
1381             wdth /= fm.charsWidth(dc, 0, dc.length);
1382
1383             ht += scl;
1384             {
1385               scl = htn * scale * profl[c++];
1386               lm = ofont.getLineMetrics(dc, 0, 1, g.getFontMetrics()
1387                       .getFontRenderContext());
1388               g.setFont(ofont.deriveFont(AffineTransform.getScaleInstance(
1389                       wdth, scl / lm.getAscent())));
1390               lm = g.getFontMetrics().getLineMetrics(dc, 0, 1, g);
1391
1392               // Debug - render boxes around characters
1393               // g.setColor(Color.red);
1394               // g.drawRect(x*av.charWidth, (int)ht, av.charWidth,
1395               // (int)(scl));
1396               // g.setColor(profcolour.findColour(dc[0]).darker());
1397               g.setColor(profcolour.findColour(dc[0], column,null));
1398
1399               hght = (ht + (scl - lm.getDescent() - lm.getBaselineOffsets()[lm
1400                       .getBaselineIndex()]));
1401
1402               g.drawChars(dc, 0, dc.length, x * charWidth, (int) hght);
1403             }
1404           }
1405           g.setFont(ofont);
1406         }
1407       }
1408       x++;
1409     }
1410     if (_aa.threshold != null)
1411     {
1412       g.setColor(_aa.threshold.colour);
1413       Graphics2D g2 = (Graphics2D) g;
1414       g2.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE,
1415               BasicStroke.JOIN_ROUND, 3f, new float[]
1416               { 5f, 3f }, 0f));
1417
1418       y2 = (int) (y - ((_aa.threshold.value - min) / range) * _aa.graphHeight);
1419       g.drawLine(0, y2, (eRes - sRes) * charWidth, y2);
1420       g2.setStroke(new BasicStroke());
1421     }
1422   }
1423
1424   // used by overview window
1425   public void drawGraph(Graphics g, AlignmentAnnotation _aa, Annotation[] aa_annotations, int width,
1426           int y, int sRes, int eRes)
1427   {
1428     eRes = Math.min(eRes, aa_annotations.length);
1429     g.setColor(Color.white);
1430     g.fillRect(0, 0, width, y);
1431     g.setColor(new Color(0, 0, 180));
1432
1433     int x = 0, height;
1434
1435     for (int j = sRes; j < eRes; j++)
1436     {
1437       if (aa_annotations[j] != null)
1438       {
1439         if (aa_annotations[j].colour == null)
1440           g.setColor(Color.black);
1441         else
1442           g.setColor(aa_annotations[j].colour);
1443
1444         height = (int) ((aa_annotations[j].value / _aa.graphMax) * y);
1445         if (height > y)
1446         {
1447           height = y;
1448         }
1449
1450         g.fillRect(x, y - height, charWidth, height);
1451       }
1452       x += charWidth;
1453     }
1454   }
1455
1456   
1457   Color getNotCanonicalColor(char lastss)
1458         {
1459           switch (lastss)
1460       {
1461           case '{':  
1462               case '}':
1463                   return Color.cyan;
1464                  
1465               case '[':
1466               case ']':
1467                   return Color.green;
1468                  
1469               case '>':
1470               case '<':
1471                   return Color.magenta;
1472                   
1473               case 'A':
1474               case 'a':
1475                   return Color.orange;
1476                 
1477               case 'B':
1478               case 'b':
1479                   return Color.pink;
1480                   
1481               case 'C':
1482               case 'c':
1483                   return Color.red;
1484                  
1485               case 'D':
1486               case 'd':
1487                   return Color.yellow;
1488                   
1489               case '1':
1490               case 'e':
1491                   return Color.black;
1492                  
1493               case 'F':
1494               case 'f':
1495                   return Color.darkGray;
1496                  
1497               case 'G':
1498               case 'g':
1499                   return Color.gray;
1500                 
1501               case '2':
1502               case 'h':
1503                   return Color.lightGray;
1504                   
1505               case 'I':
1506               case 'i':
1507                   return Color.white;
1508                  
1509               case 'J':
1510               case 'j':
1511                   return Color.cyan;
1512                   
1513               case 'K':
1514               case 'k':
1515                   return Color.magenta;
1516                 
1517               case 'L':
1518               case 'l':
1519                   return Color.orange;
1520         
1521               case 'M':
1522               case 'm':
1523                   return Color.red;
1524                 
1525               case 'N':
1526               case 'n':
1527                   return Color.yellow;
1528                   
1529               case 'O':
1530               case 'o':
1531                   return Color.pink;
1532                 
1533               case 'P':
1534               case 'p':
1535                   return Color.black;
1536                 
1537               case 'Q':
1538               case 'q':
1539                   return Color.blue;
1540         
1541               case 'R':
1542               case 'r':
1543                   return Color.cyan;
1544         
1545               case 'S':
1546               case 's':
1547                   return Color.magenta;
1548                 
1549               case 'T':
1550               case 't':
1551                   return Color.darkGray;
1552                  
1553               case 'U':
1554               case 'u':
1555                   return Color.yellow;
1556                   
1557               case 'V':
1558               case 'v':
1559                   return Color.blue;
1560                  
1561               case 'W':
1562               case 'w':
1563                   return Color.orange;
1564                   
1565               case 'X':
1566               case 'x':
1567                   return Color.magenta;
1568                  
1569               case 'Y':
1570               case 'y':
1571                   return Color.blue;
1572                   
1573               case 'Z':
1574               case 'z':
1575                   return Color.blue;
1576                  
1577         default :
1578                 System.out.println("This is not a interaction");
1579                 return null;
1580                 
1581       }
1582         }
1583 }
1584
1585         
1586