JAL-1159 patch for building menard branch on cruisecontrol
[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               if (aa[gg].graphMax > groupmax)
1020               {
1021                 groupmax = aa[gg].graphMax;
1022               }
1023               if (aa[gg].graphMin < groupmin)
1024               {
1025                 groupmin = aa[gg].graphMin;
1026               }
1027             }
1028
1029             for (int gg = 0; gg < aa.length; gg++)
1030             {
1031               if (aa[gg].graphGroup == row.graphGroup)
1032               {
1033                 drawLineGraph(g, aa[gg], aa[gg].annotations, startRes, endRes, y, groupmin,
1034                         groupmax, row.graphHeight);
1035               }
1036             }
1037
1038             graphGroupDrawn[row.graphGroup] = true;
1039           }
1040           else
1041           {
1042             drawLineGraph(g, row, row_annotations, startRes, endRes, y, row.graphMin,
1043                     row.graphMax, row.graphHeight);
1044           }
1045         }
1046         else if (row.graph == AlignmentAnnotation.BAR_GRAPH)
1047         {
1048           drawBarGraph(g, row, row_annotations, startRes, endRes, row.graphMin,
1049                   row.graphMax, y);
1050         }
1051       }
1052
1053       if (row.graph > 0 && row.hasText)
1054       {
1055         y += charHeight;
1056       }
1057
1058       if (row.graph == 0)
1059       {
1060         y += aa[i].height;
1061       }
1062     }
1063     return !usedFaded;
1064   }
1065
1066   private final Color GLYPHLINE_COLOR = Color.gray;
1067
1068   private final Color SHEET_COLOUR = Color.green;
1069
1070   private final Color HELIX_COLOUR = Color.red;
1071
1072   private final Color STEM_COLOUR = Color.blue;
1073   
1074   private  Color sdNOTCANONICAL_COLOUR;
1075
1076   public void drawGlyphLine(Graphics g, Annotation[] row,
1077           int lastSSX, int x, int y, int iconOffset, int startRes,
1078           int column, boolean validRes, boolean validEnd)
1079   {
1080     g.setColor(GLYPHLINE_COLOR);
1081     g.fillRect(lastSSX, y + 6 + iconOffset, (x * charWidth) - lastSSX, 2);
1082   }
1083
1084   public void drawSheetAnnot(Graphics g, Annotation[] row,
1085
1086           int lastSSX, int x, int y, int iconOffset, int startRes,
1087           int column, boolean validRes, boolean validEnd)
1088   {
1089     g.setColor(SHEET_COLOUR);
1090
1091     if (!validEnd || !validRes || row==null || row[column] == null
1092             || row[column].secondaryStructure != 'E')
1093     {
1094       g.fillRect(lastSSX, y + 4 + iconOffset,
1095               (x * charWidth) - lastSSX - 4, 7);
1096       g.fillPolygon(new int[]
1097       { (x * charWidth) - 4, (x * charWidth) - 4, (x * charWidth) },
1098               new int[]
1099               { y + iconOffset, y + 14 + iconOffset, y + 7 + iconOffset },
1100               3);
1101     }
1102     else
1103     {
1104       g.fillRect(lastSSX, y + 4 + iconOffset,
1105               (x + 1) * charWidth - lastSSX, 7);
1106     }
1107
1108   }
1109
1110   public void drawHelixAnnot(Graphics g, Annotation[] row,
1111           int lastSSX, int x, int y, int iconOffset, int startRes,
1112           int column, boolean validRes, boolean validEnd)
1113   {
1114     g.setColor(HELIX_COLOUR);
1115
1116     int sCol = (lastSSX / charWidth) + startRes;
1117     int x1 = lastSSX;
1118     int x2 = (x * charWidth);
1119
1120     if (MAC)
1121     {
1122       int ofs = charWidth / 2;
1123       // Off by 1 offset when drawing rects and ovals
1124       // to offscreen image on the MAC
1125       g.fillRoundRect(lastSSX, y + 4 + iconOffset, x2 - x1, 8, 8, 8);
1126       if (sCol == 0 || row[sCol - 1] == null
1127               || row[sCol - 1].secondaryStructure != 'H')
1128       {
1129       }
1130       else
1131       {
1132         // g.setColor(Color.orange);
1133         g.fillRoundRect(lastSSX, y + 4 + iconOffset, x2 - x1 - ofs + 1, 8,
1134                 0, 0);
1135       }
1136       if (!validRes || row[column] == null
1137               || row[column].secondaryStructure != 'H')
1138       {
1139
1140       }
1141       else
1142       {
1143         // g.setColor(Color.magenta);
1144         g.fillRoundRect(lastSSX + ofs, y + 4 + iconOffset, x2 - x1 - ofs
1145                 + 1, 8, 0, 0);
1146
1147       }
1148
1149       return;
1150     }
1151
1152     if (sCol == 0 || row[sCol - 1] == null
1153             || row[sCol - 1].secondaryStructure != 'H')
1154     {
1155       g.fillArc(lastSSX, y + 4 + iconOffset, charWidth, 8, 90, 180);
1156       x1 += charWidth / 2;
1157     }
1158
1159     if (!validRes || row[column] == null
1160             || row[column].secondaryStructure != 'H')
1161     {
1162       g.fillArc((x * charWidth) - charWidth, y + 4 + iconOffset, charWidth,
1163               8, 270, 180);
1164       x2 -= charWidth / 2;
1165     }
1166
1167     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 8);
1168   }
1169
1170   public void drawLineGraph(Graphics g, AlignmentAnnotation _aa, Annotation[] aa_annotations, int sRes,
1171           int eRes, int y, float min, float max, int graphHeight)
1172   {
1173     if (sRes > aa_annotations.length)
1174     {
1175       return;
1176     }
1177
1178     int x = 0;
1179
1180     // Adjustment for fastpaint to left
1181     if (eRes < endRes)
1182     {
1183       eRes++;
1184     }
1185
1186     eRes = Math.min(eRes, aa_annotations.length);
1187
1188     if (sRes == 0)
1189     {
1190       x++;
1191     }
1192
1193     int y1 = y, y2 = y;
1194     float range = max - min;
1195
1196     // //Draw origin
1197     if (min < 0)
1198     {
1199       y2 = y - (int) ((0 - min / range) * graphHeight);
1200     }
1201
1202     g.setColor(Color.gray);
1203     g.drawLine(x - charWidth, y2, (eRes - sRes + 1) * charWidth, y2);
1204
1205     eRes = Math.min(eRes, aa_annotations.length);
1206
1207     int column;
1208     int aaMax = aa_annotations.length - 1;
1209
1210     while (x < eRes - sRes)
1211     {
1212       column = sRes + x;
1213       if (hasHiddenColumns)
1214       {
1215         column = columnSelection.adjustForHiddenColumns(column);
1216       }
1217
1218       if (column > aaMax)
1219       {
1220         break;
1221       }
1222
1223       if (aa_annotations[column] == null
1224               || aa_annotations[column - 1] == null)
1225       {
1226         x++;
1227         continue;
1228       }
1229
1230       if (aa_annotations[column].colour == null)
1231         g.setColor(Color.black);
1232       else
1233         g.setColor(aa_annotations[column].colour);
1234
1235       y1 = y
1236               - (int) (((aa_annotations[column - 1].value - min) / range) * graphHeight);
1237       y2 = y
1238               - (int) (((aa_annotations[column].value - min) / range) * graphHeight);
1239
1240       g.drawLine(x * charWidth - charWidth / 2, y1, x * charWidth
1241               + charWidth / 2, y2);
1242       x++;
1243     }
1244
1245     if (_aa.threshold != null)
1246     {
1247       g.setColor(_aa.threshold.colour);
1248       Graphics2D g2 = (Graphics2D) g;
1249       g2.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE,
1250               BasicStroke.JOIN_ROUND, 3f, new float[]
1251               { 5f, 3f }, 0f));
1252
1253       y2 = (int) (y - ((_aa.threshold.value - min) / range) * graphHeight);
1254       g.drawLine(0, y2, (eRes - sRes) * charWidth, y2);
1255       g2.setStroke(new BasicStroke());
1256     }
1257   }
1258
1259   public void drawBarGraph(Graphics g, AlignmentAnnotation _aa, Annotation[] aa_annotations, int sRes,
1260           int eRes, float min, float max, int y)
1261   {
1262     if (sRes > aa_annotations.length)
1263     {
1264       return;
1265     }
1266     Font ofont = g.getFont();
1267     eRes = Math.min(eRes, aa_annotations.length);
1268
1269     int x = 0, y1 = y, y2 = y;
1270
1271     float range = max - min;
1272
1273     if (min < 0)
1274     {
1275       y2 = y - (int) ((0 - min / (range)) * _aa.graphHeight);
1276     }
1277
1278     g.setColor(Color.gray);
1279
1280     g.drawLine(x, y2, (eRes - sRes) * charWidth, y2);
1281
1282     int column;
1283     int aaMax = aa_annotations.length - 1;
1284     boolean renderHistogram = true, renderProfile = true, normaliseProfile = false;
1285     // if (aa.autoCalculated && aa.label.startsWith("Consensus"))
1286     {
1287       // TODO: generalise this to have render styles for consensus/profile data
1288       if (_aa.groupRef != null)
1289       {
1290         renderHistogram = _aa.groupRef.isShowConsensusHistogram();
1291         renderProfile = _aa.groupRef.isShowSequenceLogo();
1292         normaliseProfile = _aa.groupRef.isNormaliseSequenceLogo();
1293       }
1294       else
1295       {
1296         renderHistogram = av_renderHistogram;
1297         renderProfile = av_renderProfile;
1298         normaliseProfile = av_normaliseProfile;
1299       }
1300     }
1301     while (x < eRes - sRes)
1302     {
1303       column = sRes + x;
1304       if (hasHiddenColumns)
1305       {
1306         column = columnSelection.adjustForHiddenColumns(column);
1307       }
1308
1309       if (column > aaMax)
1310       {
1311         break;
1312       }
1313
1314       if (aa_annotations[column] == null)
1315       {
1316         x++;
1317         continue;
1318       }
1319       if (aa_annotations[column].colour == null)
1320         g.setColor(Color.black);
1321       else
1322         g.setColor(aa_annotations[column].colour);
1323
1324       y1 = y
1325               - (int) (((aa_annotations[column].value - min) / (range)) * _aa.graphHeight);
1326
1327       if (renderHistogram)
1328       {
1329         if (y1 - y2 > 0)
1330         {
1331           g.fillRect(x * charWidth, y2, charWidth, y1 - y2);
1332         }
1333         else
1334         {
1335           g.fillRect(x * charWidth, y1, charWidth, y2 - y1);
1336         }
1337       }
1338       // draw profile if available
1339       if (renderProfile)
1340       {
1341
1342         int profl[] = getProfileFor(_aa, column);
1343         // just try to draw the logo if profl is not null
1344         if (profl != null && profl[1] != 0)
1345         {
1346           float ht = normaliseProfile ? y - _aa.graphHeight : y1;
1347           double htn = normaliseProfile ? _aa.graphHeight : (y2 - y1);// aa.graphHeight;
1348           double hght;
1349           float wdth;
1350           double ht2 = 0;
1351           char[] dc;
1352
1353           /**
1354            * profl.length == 74 indicates that the profile of a secondary
1355            * structure conservation row was accesed. Therefore dc gets length 2,
1356            * to have space for a basepair instead of just a single nucleotide
1357            */
1358           if (profl.length == 74)
1359           {
1360             dc = new char[2];
1361           }
1362           else
1363           {
1364             dc = new char[1];
1365           }
1366           LineMetrics lm = g.getFontMetrics(ofont).getLineMetrics("Q", g);
1367           double scale = 1f / (normaliseProfile ? profl[1] : 100f);
1368           float ofontHeight = 1f / lm.getAscent();// magnify to fill box
1369           double scl = 0.0;
1370           for (int c = 2; c < profl[0];)
1371           {
1372             dc[0] = (char) profl[c++];
1373
1374             if (_aa.label.startsWith("StrucConsensus"))
1375             {
1376               dc[1] = (char) profl[c++];
1377             }
1378
1379             wdth = charWidth;
1380             wdth /= fm.charsWidth(dc, 0, dc.length);
1381
1382             ht += scl;
1383             {
1384               scl = htn * scale * profl[c++];
1385               lm = ofont.getLineMetrics(dc, 0, 1, g.getFontMetrics()
1386                       .getFontRenderContext());
1387               g.setFont(ofont.deriveFont(AffineTransform.getScaleInstance(
1388                       wdth, scl / lm.getAscent())));
1389               lm = g.getFontMetrics().getLineMetrics(dc, 0, 1, g);
1390
1391               // Debug - render boxes around characters
1392               // g.setColor(Color.red);
1393               // g.drawRect(x*av.charWidth, (int)ht, av.charWidth,
1394               // (int)(scl));
1395               // g.setColor(profcolour.findColour(dc[0]).darker());
1396               g.setColor(profcolour.findColour(dc[0], column,null));
1397
1398               hght = (ht + (scl - lm.getDescent() - lm.getBaselineOffsets()[lm
1399                       .getBaselineIndex()]));
1400
1401               g.drawChars(dc, 0, dc.length, x * charWidth, (int) hght);
1402             }
1403           }
1404           g.setFont(ofont);
1405         }
1406       }
1407       x++;
1408     }
1409     if (_aa.threshold != null)
1410     {
1411       g.setColor(_aa.threshold.colour);
1412       Graphics2D g2 = (Graphics2D) g;
1413       g2.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE,
1414               BasicStroke.JOIN_ROUND, 3f, new float[]
1415               { 5f, 3f }, 0f));
1416
1417       y2 = (int) (y - ((_aa.threshold.value - min) / range) * _aa.graphHeight);
1418       g.drawLine(0, y2, (eRes - sRes) * charWidth, y2);
1419       g2.setStroke(new BasicStroke());
1420     }
1421   }
1422
1423   // used by overview window
1424   public void drawGraph(Graphics g, AlignmentAnnotation _aa, Annotation[] aa_annotations, int width,
1425           int y, int sRes, int eRes)
1426   {
1427     eRes = Math.min(eRes, aa_annotations.length);
1428     g.setColor(Color.white);
1429     g.fillRect(0, 0, width, y);
1430     g.setColor(new Color(0, 0, 180));
1431
1432     int x = 0, height;
1433
1434     for (int j = sRes; j < eRes; j++)
1435     {
1436       if (aa_annotations[j] != null)
1437       {
1438         if (aa_annotations[j].colour == null)
1439           g.setColor(Color.black);
1440         else
1441           g.setColor(aa_annotations[j].colour);
1442
1443         height = (int) ((aa_annotations[j].value / _aa.graphMax) * y);
1444         if (height > y)
1445         {
1446           height = y;
1447         }
1448
1449         g.fillRect(x, y - height, charWidth, height);
1450       }
1451       x += charWidth;
1452     }
1453   }
1454
1455   
1456   Color getNotCanonicalColor(char lastss)
1457         {
1458           switch (lastss)
1459       {
1460           case '{':  
1461               case '}':
1462                   return Color.cyan;
1463                  
1464               case '[':
1465               case ']':
1466                   return Color.green;
1467                  
1468               case '>':
1469               case '<':
1470                   return Color.magenta;
1471                   
1472               case 'A':
1473               case 'a':
1474                   return Color.orange;
1475                 
1476               case 'B':
1477               case 'b':
1478                   return Color.pink;
1479                   
1480               case 'C':
1481               case 'c':
1482                   return Color.red;
1483                  
1484               case 'D':
1485               case 'd':
1486                   return Color.yellow;
1487                   
1488               case '1':
1489               case 'e':
1490                   return Color.black;
1491                  
1492               case 'F':
1493               case 'f':
1494                   return Color.darkGray;
1495                  
1496               case 'G':
1497               case 'g':
1498                   return Color.gray;
1499                 
1500               case '2':
1501               case 'h':
1502                   return Color.lightGray;
1503                   
1504               case 'I':
1505               case 'i':
1506                   return Color.white;
1507                  
1508               case 'J':
1509               case 'j':
1510                   return Color.cyan;
1511                   
1512               case 'K':
1513               case 'k':
1514                   return Color.magenta;
1515                 
1516               case 'L':
1517               case 'l':
1518                   return Color.orange;
1519         
1520               case 'M':
1521               case 'm':
1522                   return Color.red;
1523                 
1524               case 'N':
1525               case 'n':
1526                   return Color.yellow;
1527                   
1528               case 'O':
1529               case 'o':
1530                   return Color.pink;
1531                 
1532               case 'P':
1533               case 'p':
1534                   return Color.black;
1535                 
1536               case 'Q':
1537               case 'q':
1538                   return Color.blue;
1539         
1540               case 'R':
1541               case 'r':
1542                   return Color.cyan;
1543         
1544               case 'S':
1545               case 's':
1546                   return Color.magenta;
1547                 
1548               case 'T':
1549               case 't':
1550                   return Color.darkGray;
1551                  
1552               case 'U':
1553               case 'u':
1554                   return Color.yellow;
1555                   
1556               case 'V':
1557               case 'v':
1558                   return Color.blue;
1559                  
1560               case 'W':
1561               case 'w':
1562                   return Color.orange;
1563                   
1564               case 'X':
1565               case 'x':
1566                   return Color.magenta;
1567                  
1568               case 'Y':
1569               case 'y':
1570                   return Color.blue;
1571                   
1572               case 'Z':
1573               case 'z':
1574                   return Color.blue;
1575                  
1576         default :
1577                 System.out.println("This is not a interaction");
1578                 return null;
1579                 
1580       }
1581         }
1582 }
1583
1584         
1585