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