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