Merge branch 'JAL-1347' 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)
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                 && 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     AlignmentAnnotation consensusAnnot=av.getAlignmentConsensusAnnotation(),structConsensusAnnot=av.getAlignmentStrucConsensusAnnotation();
383     boolean renderHistogram = true, renderProfile = true, normaliseProfile = false;
384
385     BitSet graphGroupDrawn = new BitSet();
386     int charOffset = 0; // offset for a label
387     float fmWidth, fmScaling = 1f; // scaling for a label to fit it into a
388     // column.
389     Font ofont = g.getFont();
390     // \u03B2 \u03B1
391     // debug ints
392     int yfrom=0,f_i=0,yto=0,f_to=0;
393     boolean clipst=false,clipend=false;
394     for (int i = 0; i < aa.length; i++)
395     {
396       AlignmentAnnotation row = aa[i];
397       {
398         // check if this is a consensus annotation row and set the display settings appropriately
399         // TODO: generalise this to have render styles for consensus/profile
400         // data
401         if (row.groupRef != null && row == row.groupRef.getConsensus())
402         {
403           renderHistogram = row.groupRef.isShowConsensusHistogram();
404           renderProfile = row.groupRef.isShowSequenceLogo();
405           normaliseProfile = row.groupRef.isNormaliseSequenceLogo();
406         }
407         else if (row == consensusAnnot || row == structConsensusAnnot)
408         {
409           renderHistogram = av_renderHistogram;
410           renderProfile = av_renderProfile;
411           normaliseProfile = av_normaliseProfile;
412         } else {
413           renderHistogram = true;
414           // don't need to set render/normaliseProfile since they are not currently used in any other annotation track renderer
415         }
416       }
417       Annotation[] row_annotations = row.annotations;
418       if (!row.visible)
419       {
420         continue;
421       }
422       centreColLabels = row.centreColLabels || centreColLabelsDef;
423       labelAllCols = row.showAllColLabels;
424       scaleColLabel = row.scaleColLabel;
425       lastSS = ' ';
426       lastSSX = 0;
427       
428       if (!useClip || ((y-charHeight)<visHeight && (y+row.height+charHeight*2)>=sOffset)) 
429       {// if_in_visible_region
430         if (!clipst)
431         {
432           clipst=true;
433           yfrom=y;
434           f_i=i;
435         }
436         yto = y;
437         f_to=i;
438       if (row.graph > 0)
439       {
440         if (row.graphGroup > -1 && graphGroupDrawn.get(row.graphGroup)) {
441           continue;
442         }
443
444         // this is so that we draw the characters below the graph
445         y += row.height;
446
447         if (row.hasText)
448         {
449           iconOffset = charHeight - fm.getDescent();
450           y -= charHeight;
451         }
452       }
453       else if (row.hasText)
454       {
455         iconOffset = charHeight - fm.getDescent();
456
457       }
458       else
459       {
460         iconOffset = 0;
461       }
462
463       if (row.autoCalculated && av.isCalculationInProgress(row))
464       {
465         y += charHeight;
466         usedFaded = true;
467         g.drawImage(fadedImage, 0, y - row.height, imgWidth, y, 0, y
468                 - row.height, imgWidth, y, annotationPanel);
469         g.setColor(Color.black);
470         // g.drawString("Calculating "+aa[i].label+"....",20, y-row.height/2);
471
472         continue;
473       }
474
475       /*
476        * else if (annotationPanel.av.updatingConservation &&
477        * aa[i].label.equals("Conservation")) {
478        * 
479        * y += charHeight; g.drawImage(annotationPanel.fadedImage, 0, y -
480        * row.height, annotationPanel.imgWidth, y, 0, y - row.height,
481        * annotationPanel.imgWidth, y, annotationPanel);
482        * 
483        * g.setColor(Color.black); //
484        * g.drawString("Calculating Conservation.....",20, y-row.height/2);
485        * 
486        * continue; } else if (annotationPanel.av.updatingConservation &&
487        * aa[i].label.equals("Quality")) {
488        * 
489        * y += charHeight; g.drawImage(annotationPanel.fadedImage, 0, y -
490        * row.height, annotationPanel.imgWidth, y, 0, y - row.height,
491        * annotationPanel.imgWidth, y, annotationPanel); g.setColor(Color.black);
492        * // / g.drawString("Calculating Quality....",20, y-row.height/2);
493        * 
494        * continue; }
495        */
496       // first pass sets up state for drawing continuation from left-hand column
497       // of startRes
498       x = (startRes == 0) ? 0 : -1;
499       while (x < endRes - startRes)
500       {
501         if (hasHiddenColumns)
502         {
503           column = columnSelection.adjustForHiddenColumns(startRes + x);
504           if (column > row_annotations.length - 1)
505           {
506             break;
507           }
508         }
509         else
510         {
511           column = startRes + x;
512         }
513
514         if ((row_annotations == null) || (row_annotations.length <= column)
515                 || (row_annotations[column] == null))
516         {
517           validRes = false;
518         }
519         else
520         {
521           validRes = true;
522         }
523         if (x > -1)
524         {
525           if (activeRow == i)
526           {
527             g.setColor(Color.red);
528
529             if (columnSelection != null)
530             {
531               for (int n = 0; n < columnSelection.size(); n++)
532               {
533                 int v = columnSelection.columnAt(n);
534
535                 if (v == column)
536                 {
537                   g.fillRect(x * charWidth, y, charWidth, charHeight);
538                 }
539               }
540             }
541           }
542           if (!row.isValidStruc())
543           {
544             g.setColor(Color.orange);
545             g.fillRect((int) row.getInvalidStrucPos() * charWidth, y,
546                     charWidth, charHeight);
547           }
548           if (validCharWidth
549                   && validRes
550                   && row_annotations[column].displayCharacter != null
551                   && (row_annotations[column].displayCharacter.length() > 0))
552           {
553
554             if (centreColLabels || scaleColLabel)
555             {
556               fmWidth = fm.charsWidth(
557                       row_annotations[column].displayCharacter
558                               .toCharArray(), 0,
559                       row_annotations[column].displayCharacter.length());
560
561               if (scaleColLabel)
562               {
563                 // justify the label and scale to fit in column
564                 if (fmWidth > charWidth)
565                 {
566                   // scale only if the current font isn't already small enough
567                   fmScaling = charWidth;
568                   fmScaling /= fmWidth;
569                   g.setFont(ofont.deriveFont(AffineTransform
570                           .getScaleInstance(fmScaling, 1.0)));
571                   // and update the label's width to reflect the scaling.
572                   fmWidth = charWidth;
573                 }
574               }
575             }
576             else
577             {
578               fmWidth = fm
579                       .charWidth(row_annotations[column].displayCharacter
580                               .charAt(0));
581             }
582             charOffset = (int) ((charWidth - fmWidth) / 2f);
583
584             if (row_annotations[column].colour == null)
585               g.setColor(Color.black);
586             else
587               g.setColor(row_annotations[column].colour);
588
589             if (column == 0 || row.graph > 0)
590             {
591               g.drawString(row_annotations[column].displayCharacter,
592                       (x * charWidth) + charOffset, y + iconOffset);
593             }
594             else if (row_annotations[column - 1] == null
595                     || (labelAllCols
596                             || !row_annotations[column].displayCharacter
597                                     .equals(row_annotations[column - 1].displayCharacter) || (row_annotations[column].displayCharacter
598                             .length() < 2 && row_annotations[column].secondaryStructure == ' ')))
599             {
600                 g.drawString(row_annotations[column].displayCharacter
601                           , x
602                       * charWidth + charOffset, y + iconOffset);
603             }
604             g.setFont(ofont);
605           }
606         }
607         if (row.hasIcons)
608         {
609           char ss = validRes ? row_annotations[column].secondaryStructure
610                   : '-';
611           
612           if (ss == '(')
613           {
614             // distinguish between forward/backward base-pairing
615             if (row_annotations[column].displayCharacter.indexOf(')') > -1)
616             {
617             
618               ss = ')';
619               
620             }
621           }
622            if (ss == '[')
623           {
624             if ((row_annotations[column].displayCharacter.indexOf(']') > -1))
625             {
626                 ss = ']';
627                 
628                 
629             }
630           }
631            if (ss == '{')
632            {
633              // distinguish between forward/backward base-pairing
634              if (row_annotations[column].displayCharacter.indexOf('}') > -1)
635              {
636                ss = '}';
637                
638                
639              }
640            }
641            if (ss == '<')
642            {
643              // distinguish between forward/backward base-pairing
644              if (row_annotations[column].displayCharacter.indexOf('<') > -1)
645              {
646                ss = '>';
647                
648                
649              }
650            }
651            if (ss == 'A')
652            {
653              // distinguish between forward/backward base-pairing
654              if (row_annotations[column].displayCharacter.indexOf('a') > -1)
655              {
656                ss = 'a';
657               
658                
659              }
660            }
661            
662            if (ss == 'B')
663            {
664              // distinguish between forward/backward base-pairing
665              if (row_annotations[column].displayCharacter.indexOf('b') > -1)
666              {
667                ss = 'b';
668                
669              }
670            }
671            
672            if (ss == 'C')
673            {
674              // distinguish between forward/backward base-pairing
675              if (row_annotations[column].displayCharacter.indexOf('c') > -1)
676              {
677                ss = 'c';
678                
679              }
680            }
681            if (ss == 'D')
682            {
683              // distinguish between forward/backward base-pairing
684              if (row_annotations[column].displayCharacter.indexOf('d') > -1)
685              {
686                ss = 'd';
687                
688              }
689            }
690            if (ss == '1')
691            {
692              // distinguish between forward/backward base-pairing
693              if (row_annotations[column].displayCharacter.indexOf('e') > -1)
694              {
695                ss = 'e';
696                
697              }
698            }
699            if (ss == 'F')
700            {
701              // distinguish between forward/backward base-pairing
702              if (row_annotations[column].displayCharacter.indexOf('f') > -1)
703              {
704                ss = 'f';
705                
706              }
707            }
708            if (ss == 'G')
709            {
710              // distinguish between forward/backward base-pairing
711              if (row_annotations[column].displayCharacter.indexOf('g') > -1)
712              {
713                ss = 'g';
714                
715              }
716            }
717            if (ss == '2')
718            {
719              // distinguish between forward/backward base-pairing
720              if (row_annotations[column].displayCharacter.indexOf('h') > -1)
721              {
722                ss = 'h';
723                
724              }
725            }
726            if (ss == 'I')
727            {
728              // distinguish between forward/backward base-pairing
729              if (row_annotations[column].displayCharacter.indexOf('i') > -1)
730              {
731                ss = 'i';
732                
733              }
734            }
735            if (ss == 'J')
736            {
737              // distinguish between forward/backward base-pairing
738              if (row_annotations[column].displayCharacter.indexOf('j') > -1)
739              {
740                ss = 'j';
741                
742              }
743            }
744            if (ss == 'K')
745            {
746              // distinguish between forward/backward base-pairing
747              if (row_annotations[column].displayCharacter.indexOf('k') > -1)
748              {
749                ss = 'k';
750                
751              }
752            }
753            if (ss == 'L')
754            {
755              // distinguish between forward/backward base-pairing
756              if (row_annotations[column].displayCharacter.indexOf('l') > -1)
757              {
758                ss = 'l';
759                
760              }
761            }
762            if (ss == 'M')
763            {
764              // distinguish between forward/backward base-pairing
765              if (row_annotations[column].displayCharacter.indexOf('m') > -1)
766              {
767                ss = 'm';
768                
769              }
770            }
771            if (ss == 'N')
772            {
773              // distinguish between forward/backward base-pairing
774              if (row_annotations[column].displayCharacter.indexOf('n') > -1)
775              {
776                ss = 'n';
777                
778              }
779            }
780            if (ss == 'O')
781            {
782              // distinguish between forward/backward base-pairing
783              if (row_annotations[column].displayCharacter.indexOf('o') > -1)
784              {
785                ss = 'o';
786                
787              }
788            }
789            if (ss == 'P')
790            {
791              // distinguish between forward/backward base-pairing
792              if (row_annotations[column].displayCharacter.indexOf('p') > -1)
793              {
794                ss = 'p';
795                
796              }
797            }
798            if (ss == 'Q')
799            {
800              // distinguish between forward/backward base-pairing
801              if (row_annotations[column].displayCharacter.indexOf('q') > -1)
802              {
803                ss = 'q';
804                
805              }
806            }
807            if (ss == 'R')
808            {
809              // distinguish between forward/backward base-pairing
810              if (row_annotations[column].displayCharacter.indexOf('r') > -1)
811              {
812                ss = 'r';
813                
814              }
815            }
816            if (ss == 'S')
817            {
818              // distinguish between forward/backward base-pairing
819              if (row_annotations[column].displayCharacter.indexOf('s') > -1)
820              {
821                ss = 's';
822                
823              }
824            }
825            if (ss == 'T')
826            {
827              // distinguish between forward/backward base-pairing
828              if (row_annotations[column].displayCharacter.indexOf('t') > -1)
829              {
830                ss = 't';
831                
832              }
833            }
834            if (ss == 'U')
835            {
836              // distinguish between forward/backward base-pairing
837              if (row_annotations[column].displayCharacter.indexOf('u') > -1)
838              {
839                ss = 'u';
840                
841              }
842            }
843            if (ss == 'V')
844            {
845              // distinguish between forward/backward base-pairing
846              if (row_annotations[column].displayCharacter.indexOf('v') > -1)
847              {
848                ss = 'v';
849                
850              }
851            }
852            if (ss == 'W')
853            {
854              // distinguish between forward/backward base-pairing
855              if (row_annotations[column].displayCharacter.indexOf('w') > -1)
856              {
857                ss = 'w';
858                
859              }
860            }
861            if (ss == 'X')
862            {
863              // distinguish between forward/backward base-pairing
864              if (row_annotations[column].displayCharacter.indexOf('x') > -1)
865              {
866                ss = 'x';
867                
868              }
869            }
870            if (ss == 'Y')
871            {
872              // distinguish between forward/backward base-pairing
873              if (row_annotations[column].displayCharacter.indexOf('y') > -1)
874              {
875                ss = 'y';
876                
877              }
878            }
879            if (ss == 'Z')
880            {
881              // distinguish between forward/backward base-pairing
882              if (row_annotations[column].displayCharacter.indexOf('z') > -1)
883              {
884                ss = 'z';
885                
886              }
887            }
888           if (!validRes || (ss != lastSS))
889           {
890             if (x > -1)
891             {
892               switch (lastSS)
893               {
894               case 'H':
895                 drawHelixAnnot(g, row_annotations, lastSSX, x, y,
896                         iconOffset, startRes, column, validRes, validEnd);
897                 break;
898
899               case 'E':
900                 drawSheetAnnot(g, row_annotations, lastSSX, x, y,
901                         iconOffset, startRes, column, validRes, validEnd);
902                 break;
903
904               case '(': // Stem case for RNA secondary structure
905               case ')': // and opposite direction
906                 drawStemAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
907                         column, validRes, validEnd);
908 //              case 'S': // Stem case for RNA secondary structure
909 //              case 's': // and opposite direction
910 //                drawStemAnnot(g, row_annotations, lastSSX, x, y,
911 //                        iconOffset, startRes, column, validRes, validEnd);
912                 break;
913               case '{':
914               case '}':
915               case '[':
916               case ']':
917               case '>':
918               case '<':
919               case 'A':
920               case 'a':
921               case 'B':
922               case 'b':
923               case 'C':
924               case 'c':
925               case 'D':
926               case 'd':
927               case '1':
928               case 'e':
929               case 'F':
930               case 'f':
931               case 'G':
932               case 'g':
933               case '2':
934               case 'h':
935               case 'I':
936               case 'i':
937               case 'J':
938               case 'j':
939               case 'K':
940               case 'k':
941               case 'L':
942               case 'l':
943               case 'M':
944               case 'm':
945               case 'N':
946               case 'n':
947               case 'O':
948               case 'o':
949               case 'P':
950               case 'p':
951               case 'Q':
952               case 'q':
953               case 'R':
954               case 'r':
955               case 'S':
956               case 's':
957               case 'T':
958               case 't':
959               case 'U':
960               case 'u':
961               case 'V':
962               case 'v':
963               case 'W':
964               case 'w':
965               case 'X':
966               case 'x':
967               case 'Y':
968               case 'y':
969               case 'Z':
970               case 'z':
971                   //System.out.println(lastSS);
972                   Color nonCanColor= getNotCanonicalColor(lastSS);
973                   drawNotCanonicalAnnot(g, nonCanColor, row_annotations, lastSSX, x, y, iconOffset, startRes,
974                           column, validRes, validEnd);
975                   break;
976               default:
977                 g.setColor(Color.gray);
978                 g.fillRect(lastSSX, y + 6 + iconOffset, (x * charWidth)
979                         - lastSSX, 2);
980
981                 break;
982               }
983             }
984             if (validRes)
985             {
986               lastSS = ss;
987             }
988             else
989             {
990               lastSS = ' ';
991             }
992             if (x > -1)
993             {
994               lastSSX = (x * charWidth);
995             }
996           }
997         }
998         column++;
999         x++;
1000       }
1001       if (column >= row_annotations.length)
1002       {
1003         column = row_annotations.length - 1;
1004         validEnd = false;
1005       }
1006       else
1007       {
1008         validEnd = true;
1009       }
1010       if ((row_annotations == null) || (row_annotations.length <= column)
1011               || (row_annotations[column] == null))
1012       {
1013         validRes = false;
1014       }
1015       else
1016       {
1017         validRes = true;
1018       }
1019
1020       // x ++;
1021
1022       if (row.hasIcons)
1023       {
1024         switch (lastSS)
1025         {
1026         case 'H':
1027           drawHelixAnnot(g, row_annotations, lastSSX, x, y, iconOffset,
1028                   startRes, column, validRes, validEnd);
1029           break;
1030
1031         case 'E':
1032           drawSheetAnnot(g, row_annotations, lastSSX, x, y, iconOffset,
1033                   startRes, column, validRes, validEnd);
1034           break;
1035         case 's':
1036         case 'S': // Stem case for RNA secondary structure
1037           drawStemAnnot(g, row_annotations, lastSSX, x, y, iconOffset,
1038                   startRes, column, validRes, validEnd);
1039           break;
1040         case '{':
1041         case '}':
1042         case '[':
1043         case ']':
1044         case '>':
1045         case '<':
1046         case 'A':
1047         case 'a':
1048         case 'B':
1049         case 'b':
1050         case 'C':
1051         case 'c':
1052         case 'D':
1053         case 'd':
1054         case '1':
1055         case 'e':
1056         case 'F':
1057         case 'f':
1058         case 'G':
1059         case 'g':
1060         case '2':
1061         case 'h':
1062         case 'I':
1063         case 'i':
1064         case 'J':
1065         case 'j':
1066         case 'K':
1067         case 'k':
1068         case 'L':
1069         case 'l':
1070         case 'M':
1071         case 'm':
1072         case 'N':
1073         case 'n':
1074         case 'O':
1075         case 'o':
1076         case 'P':
1077         case 'p':
1078         case 'Q':
1079         case 'q':
1080         case 'R':
1081         case 'r':
1082         case 'T':
1083         case 't':
1084         case 'U':
1085         case 'u':
1086         case 'V':
1087         case 'v':
1088         case 'W':
1089         case 'w':
1090         case 'X':
1091         case 'x':
1092         case 'Y':
1093         case 'y':
1094         case 'Z':
1095         case 'z':
1096                 //System.out.println(lastSS);
1097           Color nonCanColor = getNotCanonicalColor(lastSS);
1098           drawNotCanonicalAnnot(g,nonCanColor, row_annotations, lastSSX, x, y, iconOffset, startRes,
1099                     column, validRes, validEnd);
1100           break;
1101         default:
1102           drawGlyphLine(g, row_annotations, lastSSX, x, y, iconOffset,
1103                   startRes, column, validRes, validEnd);
1104           break;
1105         }
1106       }
1107
1108       if (row.graph > 0 && row.graphHeight > 0)
1109       {
1110         if (row.graph == AlignmentAnnotation.LINE_GRAPH)
1111         {
1112           if (row.graphGroup > -1 && !graphGroupDrawn.get(row.graphGroup))
1113           {
1114             // TODO: JAL-1291 revise rendering model so the graphGroup map is computed efficiently for all visible labels
1115             float groupmax = -999999, groupmin = 9999999;
1116             for (int gg = 0; gg < aa.length; gg++)
1117             {
1118               if (aa[gg].graphGroup != row.graphGroup)
1119               {
1120                 continue;
1121               }
1122
1123               if (aa[gg] != row)
1124               {
1125                 aa[gg].visible = false;
1126               }
1127               if (aa[gg].graphMax > groupmax)
1128               {
1129                 groupmax = aa[gg].graphMax;
1130               }
1131               if (aa[gg].graphMin < groupmin)
1132               {
1133                 groupmin = aa[gg].graphMin;
1134               }
1135             }
1136
1137             for (int gg = 0; gg < aa.length; gg++)
1138             {
1139               if (aa[gg].graphGroup == row.graphGroup)
1140               {
1141                 drawLineGraph(g, aa[gg], aa[gg].annotations, startRes,
1142                         endRes, y, groupmin, groupmax, row.graphHeight);
1143               }
1144             }
1145
1146             graphGroupDrawn.set(row.graphGroup);
1147           }
1148           else
1149           {
1150             drawLineGraph(g, row, row_annotations, startRes, endRes, y,
1151                     row.graphMin, row.graphMax, row.graphHeight);
1152           }
1153         }
1154         else if (row.graph == AlignmentAnnotation.BAR_GRAPH)
1155         {
1156           drawBarGraph(g, row, row_annotations, startRes, endRes,
1157                   row.graphMin, row.graphMax, y, renderHistogram,renderProfile,normaliseProfile);
1158         }
1159       }
1160     } else {
1161       if (clipst && !clipend)
1162       {
1163         clipend = true;
1164       }
1165     }// end if_in_visible_region
1166       if (row.graph > 0 && row.hasText)
1167       {
1168         y += charHeight;
1169       }
1170
1171       if (row.graph == 0)
1172       {
1173         y += aa[i].height;
1174       }
1175     }
1176     if (debugRedraw)
1177     {
1178       if (canClip)
1179       {
1180         if (clipst)
1181         {
1182           System.err.println("Start clip at : " + yfrom + " (index " + f_i
1183                   + ")");
1184         }
1185         if (clipend)
1186         {
1187           System.err.println("End clip at : " + yto + " (index " + f_to
1188                   + ")");
1189         }
1190       }
1191       ;
1192       System.err.println("Annotation Rendering time:"
1193               + (System.currentTimeMillis() - stime));
1194     }
1195     ;
1196
1197     return !usedFaded;
1198   }
1199
1200   private final Color GLYPHLINE_COLOR = Color.gray;
1201
1202   private final Color SHEET_COLOUR = Color.green;
1203
1204   private final Color HELIX_COLOUR = Color.red;
1205
1206   private final Color STEM_COLOUR = Color.blue;
1207   
1208   private  Color sdNOTCANONICAL_COLOUR;
1209
1210   public void drawGlyphLine(Graphics g, Annotation[] row, int lastSSX,
1211           int x, int y, int iconOffset, int startRes, int column,
1212           boolean validRes, boolean validEnd)
1213   {
1214     g.setColor(GLYPHLINE_COLOR);
1215     g.fillRect(lastSSX, y + 6 + iconOffset, (x * charWidth) - lastSSX, 2);
1216   }
1217
1218   public void drawSheetAnnot(Graphics g, Annotation[] row, int lastSSX,
1219           int x, int y, int iconOffset, int startRes, int column,
1220           boolean validRes, boolean validEnd)
1221   {
1222     g.setColor(SHEET_COLOUR);
1223
1224     if (!validEnd || !validRes || row == null || row[column] == null
1225             || row[column].secondaryStructure != 'E')
1226     {
1227       g.fillRect(lastSSX, y + 4 + iconOffset,
1228               (x * charWidth) - lastSSX - 4, 7);
1229       g.fillPolygon(new int[]
1230       { (x * charWidth) - 4, (x * charWidth) - 4, (x * charWidth) },
1231               new int[]
1232               { y + iconOffset, y + 14 + iconOffset, y + 7 + iconOffset },
1233               3);
1234     }
1235     else
1236     {
1237       g.fillRect(lastSSX, y + 4 + iconOffset,
1238               (x + 1) * charWidth - lastSSX, 7);
1239     }
1240
1241   }
1242
1243   public void drawHelixAnnot(Graphics g, Annotation[] row, int lastSSX,
1244           int x, int y, int iconOffset, int startRes, int column,
1245           boolean validRes, boolean validEnd)
1246   {
1247     g.setColor(HELIX_COLOUR);
1248
1249     int sCol = (lastSSX / charWidth) + startRes;
1250     int x1 = lastSSX;
1251     int x2 = (x * charWidth);
1252
1253     if (MAC)
1254     {
1255       int ofs = charWidth / 2;
1256       // Off by 1 offset when drawing rects and ovals
1257       // to offscreen image on the MAC
1258       g.fillRoundRect(lastSSX, y + 4 + iconOffset, x2 - x1, 8, 8, 8);
1259       if (sCol == 0 || row[sCol - 1] == null
1260               || row[sCol - 1].secondaryStructure != 'H')
1261       {
1262       }
1263       else
1264       {
1265         // g.setColor(Color.orange);
1266         g.fillRoundRect(lastSSX, y + 4 + iconOffset, x2 - x1 - ofs + 1, 8,
1267                 0, 0);
1268       }
1269       if (!validRes || row[column] == null
1270               || row[column].secondaryStructure != 'H')
1271       {
1272
1273       }
1274       else
1275       {
1276         // g.setColor(Color.magenta);
1277         g.fillRoundRect(lastSSX + ofs, y + 4 + iconOffset, x2 - x1 - ofs
1278                 + 1, 8, 0, 0);
1279
1280       }
1281
1282       return;
1283     }
1284
1285     if (sCol == 0 || row[sCol - 1] == null
1286             || row[sCol - 1].secondaryStructure != 'H')
1287     {
1288       g.fillArc(lastSSX, y + 4 + iconOffset, charWidth, 8, 90, 180);
1289       x1 += charWidth / 2;
1290     }
1291
1292     if (!validRes || row[column] == null
1293             || row[column].secondaryStructure != 'H')
1294     {
1295       g.fillArc((x * charWidth) - charWidth, y + 4 + iconOffset, charWidth,
1296               8, 270, 180);
1297       x2 -= charWidth / 2;
1298     }
1299
1300     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 8);
1301   }
1302
1303   public void drawLineGraph(Graphics g, AlignmentAnnotation _aa,
1304           Annotation[] aa_annotations, int sRes, int eRes, int y,
1305           float min, float max, int graphHeight)
1306   {
1307     if (sRes > aa_annotations.length)
1308     {
1309       return;
1310     }
1311
1312     int x = 0;
1313
1314     // Adjustment for fastpaint to left
1315     if (eRes < endRes)
1316     {
1317       eRes++;
1318     }
1319
1320     eRes = Math.min(eRes, aa_annotations.length);
1321
1322     if (sRes == 0)
1323     {
1324       x++;
1325     }
1326
1327     int y1 = y, y2 = y;
1328     float range = max - min;
1329
1330     // //Draw origin
1331     if (min < 0)
1332     {
1333       y2 = y - (int) ((0 - min / range) * graphHeight);
1334     }
1335
1336     g.setColor(Color.gray);
1337     g.drawLine(x - charWidth, y2, (eRes - sRes + 1) * charWidth, y2);
1338
1339     eRes = Math.min(eRes, aa_annotations.length);
1340
1341     int column;
1342     int aaMax = aa_annotations.length - 1;
1343
1344     while (x < eRes - sRes)
1345     {
1346       column = sRes + x;
1347       if (hasHiddenColumns)
1348       {
1349         column = columnSelection.adjustForHiddenColumns(column);
1350       }
1351
1352       if (column > aaMax)
1353       {
1354         break;
1355       }
1356
1357       if (aa_annotations[column] == null
1358               || aa_annotations[column - 1] == null)
1359       {
1360         x++;
1361         continue;
1362       }
1363
1364       if (aa_annotations[column].colour == null)
1365         g.setColor(Color.black);
1366       else
1367         g.setColor(aa_annotations[column].colour);
1368
1369       y1 = y
1370               - (int) (((aa_annotations[column - 1].value - min) / range) * graphHeight);
1371       y2 = y
1372               - (int) (((aa_annotations[column].value - min) / range) * graphHeight);
1373
1374       g.drawLine(x * charWidth - charWidth / 2, y1, x * charWidth
1375               + charWidth / 2, y2);
1376       x++;
1377     }
1378
1379     if (_aa.threshold != null)
1380     {
1381       g.setColor(_aa.threshold.colour);
1382       Graphics2D g2 = (Graphics2D) g;
1383       g2.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE,
1384               BasicStroke.JOIN_ROUND, 3f, new float[]
1385               { 5f, 3f }, 0f));
1386
1387       y2 = (int) (y - ((_aa.threshold.value - min) / range) * graphHeight);
1388       g.drawLine(0, y2, (eRes - sRes) * charWidth, y2);
1389       g2.setStroke(new BasicStroke());
1390     }
1391   }
1392
1393   public void drawBarGraph(Graphics g, AlignmentAnnotation _aa,
1394           Annotation[] aa_annotations, int sRes, int eRes, float min,
1395           float max, int y, boolean renderHistogram,boolean renderProfile,boolean normaliseProfile)
1396   {
1397     if (sRes > aa_annotations.length)
1398     {
1399       return;
1400     }
1401     Font ofont = g.getFont();
1402     eRes = Math.min(eRes, aa_annotations.length);
1403
1404     int x = 0, y1 = y, y2 = y;
1405
1406     float range = max - min;
1407
1408     if (min < 0)
1409     {
1410       y2 = y - (int) ((0 - min / (range)) * _aa.graphHeight);
1411     }
1412
1413     g.setColor(Color.gray);
1414
1415     g.drawLine(x, y2, (eRes - sRes) * charWidth, y2);
1416
1417     int column;
1418     int aaMax = aa_annotations.length - 1;
1419     while (x < eRes - sRes)
1420     {
1421       column = sRes + x;
1422       if (hasHiddenColumns)
1423       {
1424         column = columnSelection.adjustForHiddenColumns(column);
1425       }
1426
1427       if (column > aaMax)
1428       {
1429         break;
1430       }
1431
1432       if (aa_annotations[column] == null)
1433       {
1434         x++;
1435         continue;
1436       }
1437       if (aa_annotations[column].colour == null)
1438         g.setColor(Color.black);
1439       else
1440         g.setColor(aa_annotations[column].colour);
1441
1442       y1 = y
1443               - (int) (((aa_annotations[column].value - min) / (range)) * _aa.graphHeight);
1444
1445       if (renderHistogram)
1446       {
1447         if (y1 - y2 > 0)
1448         {
1449           g.fillRect(x * charWidth, y2, charWidth, y1 - y2);
1450         }
1451         else
1452         {
1453           g.fillRect(x * charWidth, y1, charWidth, y2 - y1);
1454         }
1455       }
1456       // draw profile if available
1457       if (renderProfile)
1458       {
1459
1460         int profl[] = getProfileFor(_aa, column);
1461         // just try to draw the logo if profl is not null
1462         if (profl != null && profl[1] != 0)
1463         {
1464           float ht = normaliseProfile ? y - _aa.graphHeight : y1;
1465           double htn = normaliseProfile ? _aa.graphHeight : (y2 - y1);// aa.graphHeight;
1466           double hght;
1467           float wdth;
1468           double ht2 = 0;
1469           char[] dc;
1470
1471           /**
1472            * profl.length == 74 indicates that the profile of a secondary
1473            * structure conservation row was accesed. Therefore dc gets length 2,
1474            * to have space for a basepair instead of just a single nucleotide
1475            */
1476           if (profl.length == 74)
1477           {
1478             dc = new char[2];
1479           }
1480           else
1481           {
1482             dc = new char[1];
1483           }
1484           LineMetrics lm = g.getFontMetrics(ofont).getLineMetrics("Q", g);
1485           double scale = 1f / (normaliseProfile ? profl[1] : 100f);
1486           float ofontHeight = 1f / lm.getAscent();// magnify to fill box
1487           double scl = 0.0;
1488           for (int c = 2; c < profl[0];)
1489           {
1490             dc[0] = (char) profl[c++];
1491
1492             if (_aa.label.startsWith("StrucConsensus"))
1493             {
1494               dc[1] = (char) profl[c++];
1495             }
1496
1497             wdth = charWidth;
1498             wdth /= fm.charsWidth(dc, 0, dc.length);
1499
1500             ht += scl;
1501             {
1502               scl = htn * scale * profl[c++];
1503               lm = ofont.getLineMetrics(dc, 0, 1, g.getFontMetrics()
1504                       .getFontRenderContext());
1505               g.setFont(ofont.deriveFont(AffineTransform.getScaleInstance(
1506                       wdth, scl / lm.getAscent())));
1507               lm = g.getFontMetrics().getLineMetrics(dc, 0, 1, g);
1508
1509               // Debug - render boxes around characters
1510               // g.setColor(Color.red);
1511               // g.drawRect(x*av.charWidth, (int)ht, av.charWidth,
1512               // (int)(scl));
1513               // g.setColor(profcolour.findColour(dc[0]).darker());
1514               g.setColor(profcolour.findColour(dc[0], column, null));
1515
1516               hght = (ht + (scl - lm.getDescent() - lm.getBaselineOffsets()[lm
1517                       .getBaselineIndex()]));
1518
1519               g.drawChars(dc, 0, dc.length, x * charWidth, (int) hght);
1520             }
1521           }
1522           g.setFont(ofont);
1523         }
1524       }
1525       x++;
1526     }
1527     if (_aa.threshold != null)
1528     {
1529       g.setColor(_aa.threshold.colour);
1530       Graphics2D g2 = (Graphics2D) g;
1531       g2.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE,
1532               BasicStroke.JOIN_ROUND, 3f, new float[]
1533               { 5f, 3f }, 0f));
1534
1535       y2 = (int) (y - ((_aa.threshold.value - min) / range)
1536               * _aa.graphHeight);
1537       g.drawLine(0, y2, (eRes - sRes) * charWidth, y2);
1538       g2.setStroke(new BasicStroke());
1539     }
1540   }
1541
1542   // used by overview window
1543   public void drawGraph(Graphics g, AlignmentAnnotation _aa,
1544           Annotation[] aa_annotations, int width, int y, int sRes, int eRes)
1545   {
1546     eRes = Math.min(eRes, aa_annotations.length);
1547     g.setColor(Color.white);
1548     g.fillRect(0, 0, width, y);
1549     g.setColor(new Color(0, 0, 180));
1550
1551     int x = 0, height;
1552
1553     for (int j = sRes; j < eRes; j++)
1554     {
1555       if (aa_annotations[j] != null)
1556       {
1557         if (aa_annotations[j].colour == null)
1558           g.setColor(Color.black);
1559         else
1560           g.setColor(aa_annotations[j].colour);
1561
1562         height = (int) ((aa_annotations[j].value / _aa.graphMax) * y);
1563         if (height > y)
1564         {
1565           height = y;
1566         }
1567
1568         g.fillRect(x, y - height, charWidth, height);
1569       }
1570       x += charWidth;
1571     }
1572   }
1573
1574   
1575   Color getNotCanonicalColor(char lastss)
1576         {
1577           switch (lastss)
1578       {
1579           case '{':  
1580               case '}':
1581                   return Color.cyan;
1582                  
1583               case '[':
1584               case ']':
1585                   return Color.green;
1586                  
1587               case '>':
1588               case '<':
1589                   return Color.magenta;
1590                   
1591               case 'A':
1592               case 'a':
1593                   return Color.orange;
1594                 
1595               case 'B':
1596               case 'b':
1597                   return Color.pink;
1598                   
1599               case 'C':
1600               case 'c':
1601                   return Color.red;
1602                  
1603               case 'D':
1604               case 'd':
1605                   return Color.yellow;
1606                   
1607               case '1':
1608               case 'e':
1609                   return Color.black;
1610                  
1611               case 'F':
1612               case 'f':
1613                   return Color.darkGray;
1614                  
1615               case 'G':
1616               case 'g':
1617                   return Color.gray;
1618                 
1619               case '2':
1620               case 'h':
1621                   return Color.lightGray;
1622                   
1623               case 'I':
1624               case 'i':
1625                   return Color.white;
1626                  
1627               case 'J':
1628               case 'j':
1629                   return Color.cyan;
1630                   
1631               case 'K':
1632               case 'k':
1633                   return Color.magenta;
1634                 
1635               case 'L':
1636               case 'l':
1637                   return Color.orange;
1638         
1639               case 'M':
1640               case 'm':
1641                   return Color.red;
1642                 
1643               case 'N':
1644               case 'n':
1645                   return Color.yellow;
1646                   
1647               case 'O':
1648               case 'o':
1649                   return Color.pink;
1650                 
1651               case 'P':
1652               case 'p':
1653                   return Color.black;
1654                 
1655               case 'Q':
1656               case 'q':
1657                   return Color.blue;
1658         
1659               case 'R':
1660               case 'r':
1661                   return Color.cyan;
1662         
1663               case 'S':
1664               case 's':
1665                   return Color.magenta;
1666                 
1667               case 'T':
1668               case 't':
1669                   return Color.darkGray;
1670                  
1671               case 'U':
1672               case 'u':
1673                   return Color.yellow;
1674                   
1675               case 'V':
1676               case 'v':
1677                   return Color.blue;
1678                  
1679               case 'W':
1680               case 'w':
1681                   return Color.orange;
1682                   
1683               case 'X':
1684               case 'x':
1685                   return Color.magenta;
1686                  
1687               case 'Y':
1688               case 'y':
1689                   return Color.blue;
1690                   
1691               case 'Z':
1692               case 'z':
1693                   return Color.blue;
1694                  
1695         default :
1696                 System.out.println("This is not a interaction");
1697                 return null;
1698                 
1699       }
1700         }
1701 }
1702
1703         
1704