JAL-3597 additional references nulled, bug in test corrected
[jalview.git] / src / jalview / renderer / AnnotationRenderer.java
index 1b58dbc..671bf6b 100644 (file)
@@ -1,30 +1,40 @@
 /*
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.0b1)
- * Copyright (C) 2014 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
  * Jalview is free software: you can redistribute it and/or
  * modify it under the terms of the GNU General Public License 
  * 
  * This file is part of Jalview.
  * 
  * Jalview is free software: you can redistribute it and/or
  * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
  *  
  * Jalview is distributed in the hope that it will be useful, but 
  * WITHOUT ANY WARRANTY; without even the implied warranty 
  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
  * PURPOSE.  See the GNU General Public License for more details.
  * 
  *  
  * Jalview is distributed in the hope that it will be useful, but 
  * WITHOUT ANY WARRANTY; without even the implied warranty 
  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
  * PURPOSE.  See the GNU General Public License for more details.
  * 
- * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
 package jalview.renderer;
 
 import jalview.analysis.AAFrequency;
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
 package jalview.renderer;
 
 import jalview.analysis.AAFrequency;
+import jalview.analysis.CodingUtils;
+import jalview.analysis.Rna;
 import jalview.analysis.StructureFrequency;
 import jalview.api.AlignViewportI;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.ColumnSelection;
 import jalview.analysis.StructureFrequency;
 import jalview.api.AlignViewportI;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.ProfilesI;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemeI;
+import jalview.schemes.NucleotideColourScheme;
+import jalview.schemes.ResidueProperties;
+import jalview.schemes.ZappoColourScheme;
+import jalview.util.Platform;
 
 import java.awt.BasicStroke;
 import java.awt.Color;
 
 import java.awt.BasicStroke;
 import java.awt.Color;
@@ -39,101 +49,39 @@ import java.awt.image.ImageObserver;
 import java.util.BitSet;
 import java.util.Hashtable;
 
 import java.util.BitSet;
 import java.util.Hashtable;
 
-import com.stevesoft.pat.Regex;
-
 public class AnnotationRenderer
 {
 public class AnnotationRenderer
 {
+  private static final int UPPER_TO_LOWER = 'a' - 'A'; // 32
+
+  private static final int CHAR_A = 'A'; // 65
+
+  private static final int CHAR_Z = 'Z'; // 90
+
   /**
    * flag indicating if timing and redraw parameter info should be output
    */
   private final boolean debugRedraw;
 
   /**
    * flag indicating if timing and redraw parameter info should be output
    */
   private final boolean debugRedraw;
 
-  public AnnotationRenderer()
-  {
-    this(false);
-  }
-  /**
-   * Create a new annotation Renderer
-   * @param debugRedraw flag indicating if timing and redraw parameter info should be output
-   */
-  public AnnotationRenderer(boolean debugRedraw)
-  {
-    this.debugRedraw=debugRedraw;
-  }
-
-  public void drawStemAnnot(Graphics g, Annotation[] row_annotations,
-          int lastSSX, int x, int y, int iconOffset, int startRes,
-          int column, boolean validRes, boolean validEnd)
-  {
-    g.setColor(STEM_COLOUR);
-    int sCol = (lastSSX / charWidth) + startRes;
-    int x1 = lastSSX;
-    int x2 = (x * charWidth);
-    Regex closeparen = new Regex("(\\))");
-
-    String dc = (column == 0 || row_annotations[column - 1] == null) ? ""
-            : row_annotations[column - 1].displayCharacter;
-
-    boolean diffupstream = sCol == 0 || row_annotations[sCol - 1] == null
-            || !dc.equals(row_annotations[sCol - 1].displayCharacter);
-    boolean diffdownstream = !validRes || !validEnd
-            || row_annotations[column] == null
-            || !dc.equals(row_annotations[column].displayCharacter);
-    // System.out.println("Column "+column+" diff up: "+diffupstream+" down:"+diffdownstream);
-    // If a closing base pair half of the stem, display a backward arrow
-    if (column > 0 && closeparen.search(dc))
-    {
-       
-      if (diffupstream)
-      // if (validRes && column>1 && row_annotations[column-2]!=null &&
-      // dc.equals(row_annotations[column-2].displayCharacter))
-      {
-        g.fillPolygon(new int[]
-        { lastSSX + 5, lastSSX + 5, lastSSX }, new int[]
-        { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3);
-        x1 += 5;
-      }
-      if (diffdownstream)
-      {
-        x2 -= 1;
-      }
-    }
-    else
-    {
-       
-      // display a forward arrow
-      if (diffdownstream)
-      {
-        g.fillPolygon(new int[]
-        { x2 - 5, x2 - 5, x2 }, new int[]
-        { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3);
-        x2 -= 5;
-      }
-      if (diffupstream)
-      {
-        x1 += 1;
-      }
-    }
-    // draw arrow body
-    g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 7);
-  }
-
   private int charWidth, endRes, charHeight;
 
   private boolean validCharWidth, hasHiddenColumns;
 
   private FontMetrics fm;
 
   private int charWidth, endRes, charHeight;
 
   private boolean validCharWidth, hasHiddenColumns;
 
   private FontMetrics fm;
 
-  private final boolean MAC = new jalview.util.Platform().isAMac();
+  private final boolean MAC = Platform.isAMac();
 
   boolean av_renderHistogram = true, av_renderProfile = true,
           av_normaliseProfile = false;
 
 
   boolean av_renderHistogram = true, av_renderProfile = true,
           av_normaliseProfile = false;
 
-  ColourSchemeI profcolour = null;
+  ResidueShaderI profcolour = null;
 
   private ColumnSelection columnSelection;
 
 
   private ColumnSelection columnSelection;
 
-  private Hashtable[] hconsensus;
+  private HiddenColumns hiddenColumns;
+
+  private ProfilesI hconsensus;
+
+  private Hashtable[] complementConsensus;
 
   private Hashtable[] hStrucConsensus;
 
 
   private Hashtable[] hStrucConsensus;
 
@@ -157,36 +105,136 @@ public class AnnotationRenderer
    * width of image to render in panel
    */
   private int imgWidth;
    * width of image to render in panel
    */
   private int imgWidth;
+
   /**
    * offset to beginning of visible area
    */
   private int sOffset;
   /**
    * offset to beginning of visible area
    */
   private int sOffset;
+
   /**
    * offset to end of visible area
    */
   private int visHeight;
   /**
    * offset to end of visible area
    */
   private int visHeight;
+
   /**
   /**
-   * indicate if the renderer should only render the visible portion of the annotation given the current view settings
+   * indicate if the renderer should only render the visible portion of the
+   * annotation given the current view settings
    */
    */
-  private boolean useClip=true;
+  private boolean useClip = true;
+
   /**
   /**
-   * master flag indicating if renderer should ever try to clip. not enabled for jalview 2.8.1 
+   * master flag indicating if renderer should ever try to clip. not enabled for
+   * jalview 2.8.1
    */
    */
-  private boolean canClip=false;
+  private boolean canClip = false;
 
 
-  
-  public void drawNotCanonicalAnnot(Graphics g, Color nonCanColor, Annotation[] row_annotations,
-          int lastSSX, int x, int y, int iconOffset, int startRes,
-          int column, boolean validRes, boolean validEnd)
+  public AnnotationRenderer()
+  {
+    this(false);
+  }
+
+  /**
+   * Create a new annotation Renderer
+   * 
+   * @param debugRedraw
+   *          flag indicating if timing and redraw parameter info should be
+   *          output
+   */
+  public AnnotationRenderer(boolean debugRedraw)
+  {
+    this.debugRedraw = debugRedraw;
+  }
+
+  /**
+   * Remove any references and resources when this object is no longer required
+   */
+  public void dispose()
   {
   {
-       //System.out.println(nonCanColor);
-       
+    hiddenColumns = null;
+    hconsensus = null;
+    complementConsensus = null;
+    hStrucConsensus = null;
+    fadedImage = null;
+    annotationPanel = null;
+  }
+
+  void drawStemAnnot(Graphics g, Annotation[] row_annotations, int lastSSX,
+          int x, int y, int iconOffset, int startRes, int column,
+          boolean validRes, boolean validEnd)
+  {
+    g.setColor(STEM_COLOUR);
+    int sCol = (lastSSX / charWidth)
+            + hiddenColumns.visibleToAbsoluteColumn(startRes);
+    int x1 = lastSSX;
+    int x2 = (x * charWidth);
+
+    char dc = (column == 0 || row_annotations[column - 1] == null) ? ' '
+            : row_annotations[column - 1].secondaryStructure;
+
+    boolean diffupstream = sCol == 0 || row_annotations[sCol - 1] == null
+            || dc != row_annotations[sCol - 1].secondaryStructure;
+    boolean diffdownstream = !validRes || !validEnd
+            || row_annotations[column] == null
+            || dc != row_annotations[column].secondaryStructure;
+
+    if (column > 0 && Rna.isClosingParenthesis(dc))
+    {
+      if (diffupstream)
+      // if (validRes && column>1 && row_annotations[column-2]!=null &&
+      // dc.equals(row_annotations[column-2].displayCharacter))
+      {
+        /*
+         * if new annotation with a closing base pair half of the stem, 
+         * display a backward arrow
+         */
+        g.fillPolygon(new int[] { lastSSX + 5, lastSSX + 5, lastSSX },
+                new int[]
+                { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset },
+                3);
+        x1 += 5;
+      }
+      if (diffdownstream)
+      {
+        x2 -= 1;
+      }
+    }
+    else
+    {
+      // display a forward arrow
+      if (diffdownstream)
+      {
+        /*
+         * if annotation ending with an opeing base pair half of the stem, 
+         * display a forward arrow
+         */
+        g.fillPolygon(new int[] { x2 - 5, x2 - 5, x2 },
+                new int[]
+                { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset },
+                3);
+        x2 -= 5;
+      }
+      if (diffupstream)
+      {
+        x1 += 1;
+      }
+    }
+    // draw arrow body
+    g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 7);
+  }
+
+  void drawNotCanonicalAnnot(Graphics g, Color nonCanColor,
+          Annotation[] row_annotations, int lastSSX, int x, int y,
+          int iconOffset, int startRes, int column, boolean validRes,
+          boolean validEnd)
+  {
+    // System.out.println(nonCanColor);
+
     g.setColor(nonCanColor);
     g.setColor(nonCanColor);
-    int sCol = (lastSSX / charWidth) + startRes;
+    int sCol = (lastSSX / charWidth)
+            + hiddenColumns.visibleToAbsoluteColumn(startRes);
     int x1 = lastSSX;
     int x2 = (x * charWidth);
     int x1 = lastSSX;
     int x2 = (x * charWidth);
-    Regex closeparen = new Regex("}|]|<|[a-z]");
-    
+
     String dc = (column == 0 || row_annotations[column - 1] == null) ? ""
             : row_annotations[column - 1].displayCharacter;
 
     String dc = (column == 0 || row_annotations[column - 1] == null) ? ""
             : row_annotations[column - 1].displayCharacter;
 
@@ -195,18 +243,20 @@ public class AnnotationRenderer
     boolean diffdownstream = !validRes || !validEnd
             || row_annotations[column] == null
             || !dc.equals(row_annotations[column].displayCharacter);
     boolean diffdownstream = !validRes || !validEnd
             || row_annotations[column] == null
             || !dc.equals(row_annotations[column].displayCharacter);
-    // System.out.println("Column "+column+" diff up: "+diffupstream+" down:"+diffdownstream);
+    // System.out.println("Column "+column+" diff up: "+diffupstream+"
+    // down:"+diffdownstream);
     // If a closing base pair half of the stem, display a backward arrow
     // If a closing base pair half of the stem, display a backward arrow
-    if (column > 0 && closeparen.search(dc))//  closeletter_b.search(dc)||closeletter_c.search(dc)||closeletter_d.search(dc)||closecrochet.search(dc)) )
+    if (column > 0 && Rna.isClosingParenthesis(dc))
     {
     {
-       
+
       if (diffupstream)
       // if (validRes && column>1 && row_annotations[column-2]!=null &&
       // dc.equals(row_annotations[column-2].displayCharacter))
       {
       if (diffupstream)
       // if (validRes && column>1 && row_annotations[column-2]!=null &&
       // dc.equals(row_annotations[column-2].displayCharacter))
       {
-        g.fillPolygon(new int[]
-        { lastSSX + 5, lastSSX + 5, lastSSX }, new int[]
-        { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3);
+        g.fillPolygon(new int[] { lastSSX + 5, lastSSX + 5, lastSSX },
+                new int[]
+                { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset },
+                3);
         x1 += 5;
       }
       if (diffdownstream)
         x1 += 5;
       }
       if (diffdownstream)
@@ -216,13 +266,14 @@ public class AnnotationRenderer
     }
     else
     {
     }
     else
     {
-       
+
       // display a forward arrow
       if (diffdownstream)
       {
       // display a forward arrow
       if (diffdownstream)
       {
-        g.fillPolygon(new int[]
-        { x2 - 5, x2 - 5, x2 }, new int[]
-        { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3);
+        g.fillPolygon(new int[] { x2 - 5, x2 - 5, x2 },
+                new int[]
+                { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset },
+                3);
         x2 -= 5;
       }
       if (diffupstream)
         x2 -= 5;
       }
       if (diffupstream)
@@ -233,6 +284,7 @@ public class AnnotationRenderer
     // draw arrow body
     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 7);
   }
     // draw arrow body
     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 7);
   }
+
   // public void updateFromAnnotationPanel(FontMetrics annotFM, AlignViewportI
   // av)
   public void updateFromAwtRenderPanel(AwtRenderPanelI annotPanel,
   // public void updateFromAnnotationPanel(FontMetrics annotFM, AlignViewportI
   // av)
   public void updateFromAwtRenderPanel(AwtRenderPanelI annotPanel,
@@ -243,19 +295,23 @@ public class AnnotationRenderer
     fadedImage = annotPanel.getFadedImage();
     imgWidth = annotPanel.getFadedImageWidth();
     // visible area for rendering
     fadedImage = annotPanel.getFadedImage();
     imgWidth = annotPanel.getFadedImageWidth();
     // visible area for rendering
-    int[] bounds=annotPanel.getVisibleVRange();
-    if (bounds!=null)
+    int[] bounds = annotPanel.getVisibleVRange();
+    if (bounds != null)
     {
       sOffset = bounds[0];
       visHeight = bounds[1];
     {
       sOffset = bounds[0];
       visHeight = bounds[1];
-      if (visHeight==0)
+      if (visHeight == 0)
+      {
+        useClip = false;
+      }
+      else
       {
       {
-        useClip=false;
-      } else {
-        useClip=canClip;
+        useClip = canClip;
       }
       }
-    } else {
-      useClip=false;
+    }
+    else
+    {
+      useClip = false;
     }
 
     updateFromAlignViewport(av);
     }
 
     updateFromAlignViewport(av);
@@ -264,48 +320,75 @@ public class AnnotationRenderer
   public void updateFromAlignViewport(AlignViewportI av)
   {
     charWidth = av.getCharWidth();
   public void updateFromAlignViewport(AlignViewportI av)
   {
     charWidth = av.getCharWidth();
-    endRes = av.getEndRes();
+    endRes = av.getRanges().getEndRes();
     charHeight = av.getCharHeight();
     hasHiddenColumns = av.hasHiddenColumns();
     validCharWidth = av.isValidCharWidth();
     av_renderHistogram = av.isShowConsensusHistogram();
     av_renderProfile = av.isShowSequenceLogo();
     av_normaliseProfile = av.isNormaliseSequenceLogo();
     charHeight = av.getCharHeight();
     hasHiddenColumns = av.hasHiddenColumns();
     validCharWidth = av.isValidCharWidth();
     av_renderHistogram = av.isShowConsensusHistogram();
     av_renderProfile = av.isShowSequenceLogo();
     av_normaliseProfile = av.isNormaliseSequenceLogo();
-    profcolour = av.getGlobalColourScheme();
-    if (profcolour == null)
+    profcolour = av.getResidueShading();
+    if (profcolour == null || profcolour.getColourScheme() == null)
     {
     {
-      // Set the default colour for sequence logo if the alignnent has no
-      // colourscheme set
-      profcolour = av.getAlignment().isNucleotide() ? new jalview.schemes.NucleotideColourScheme()
-              : new jalview.schemes.ZappoColourScheme();
+      /*
+       * Use default colour for sequence logo if 
+       * the alignment has no colourscheme set
+       * (would like to use user preference but n/a for applet)
+       */
+      ColourSchemeI col = av.getAlignment().isNucleotide()
+              ? new NucleotideColourScheme()
+              : new ZappoColourScheme();
+      profcolour = new ResidueShader(col);
     }
     columnSelection = av.getColumnSelection();
     }
     columnSelection = av.getColumnSelection();
-    hconsensus = av.getSequenceConsensusHash();// hconsensus;
-    hStrucConsensus = av.getRnaStructureConsensusHash(); // hStrucConsensus;
-    av_ignoreGapsConsensus = av.getIgnoreGapsConsensus();
+    hiddenColumns = av.getAlignment().getHiddenColumns();
+    hconsensus = av.getSequenceConsensusHash();
+    complementConsensus = av.getComplementConsensusHash();
+    hStrucConsensus = av.getRnaStructureConsensusHash();
+    av_ignoreGapsConsensus = av.isIgnoreGapsConsensus();
   }
 
   }
 
-  public int[] getProfileFor(AlignmentAnnotation aa, int column)
+  /**
+   * Returns profile data; the first element is the profile type, the second is
+   * the number of distinct values, the third the total count, and the remainder
+   * depend on the profile type.
+   * 
+   * @param aa
+   * @param column
+   * @return
+   */
+  int[] getProfileFor(AlignmentAnnotation aa, int column)
   {
     // TODO : consider refactoring the global alignment calculation
     // properties/rendering attributes as a global 'alignment group' which holds
     // all vis settings for the alignment as a whole rather than a subset
     //
   {
     // TODO : consider refactoring the global alignment calculation
     // properties/rendering attributes as a global 'alignment group' which holds
     // all vis settings for the alignment as a whole rather than a subset
     //
-    if (aa.autoCalculated && aa.label.startsWith("Consensus"))
+    if (aa.autoCalculated && (aa.label.startsWith("Consensus")
+            || aa.label.startsWith("cDNA Consensus")))
     {
     {
+      boolean forComplement = aa.label.startsWith("cDNA Consensus");
       if (aa.groupRef != null && aa.groupRef.consensusData != null
               && aa.groupRef.isShowSequenceLogo())
       {
       if (aa.groupRef != null && aa.groupRef.consensusData != null
               && aa.groupRef.isShowSequenceLogo())
       {
+        // TODO? group consensus for cDNA complement
         return AAFrequency.extractProfile(
         return AAFrequency.extractProfile(
-                aa.groupRef.consensusData[column],
+                aa.groupRef.consensusData.get(column),
                 aa.groupRef.getIgnoreGapsConsensus());
       }
       // TODO extend annotation row to enable dynamic and static profile data to
       // be stored
       if (aa.groupRef == null && aa.sequenceRef == null)
       {
                 aa.groupRef.getIgnoreGapsConsensus());
       }
       // TODO extend annotation row to enable dynamic and static profile data to
       // be stored
       if (aa.groupRef == null && aa.sequenceRef == null)
       {
-        return AAFrequency.extractProfile(hconsensus[column],
-                av_ignoreGapsConsensus);
+        if (forComplement)
+        {
+          return AAFrequency.extractCdnaProfile(complementConsensus[column],
+                  av_ignoreGapsConsensus);
+        }
+        else
+        {
+          return AAFrequency.extractProfile(hconsensus.get(column),
+                  av_ignoreGapsConsensus);
+        }
       }
     }
     else
       }
     }
     else
@@ -335,6 +418,8 @@ public class AnnotationRenderer
     return null;
   }
 
     return null;
   }
 
+  boolean rna = false;
+
   /**
    * Render the annotation rows associated with an alignment.
    * 
   /**
    * Render the annotation rows associated with an alignment.
    * 
@@ -357,7 +442,7 @@ public class AnnotationRenderer
           AlignViewportI av, Graphics g, int activeRow, int startRes,
           int endRes)
   {
           AlignViewportI av, Graphics g, int activeRow, int startRes,
           int endRes)
   {
-    long stime=System.currentTimeMillis();
+    long stime = System.currentTimeMillis();
     boolean usedFaded = false;
     // NOTES:
     // AnnotationPanel needs to implement: ImageObserver, access to
     boolean usedFaded = false;
     // NOTES:
     // AnnotationPanel needs to implement: ImageObserver, access to
@@ -366,7 +451,7 @@ public class AnnotationRenderer
     fm = g.getFontMetrics();
     AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
     int temp = 0;
     fm = g.getFontMetrics();
     AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
     int temp = 0;
-    if (aa==null)
+    if (aa == null)
     {
       return false;
     }
     {
       return false;
     }
@@ -378,11 +463,17 @@ public class AnnotationRenderer
     boolean validRes = false;
     boolean validEnd = false;
     boolean labelAllCols = false;
     boolean validRes = false;
     boolean validEnd = false;
     boolean labelAllCols = false;
-    boolean centreColLabels, centreColLabelsDef = av
-            .getCentreColumnLabels();
+    boolean centreColLabels;
+    boolean centreColLabelsDef = av.isCentreColumnLabels();
     boolean scaleColLabel = false;
     boolean scaleColLabel = false;
-    AlignmentAnnotation consensusAnnot=av.getAlignmentConsensusAnnotation(),structConsensusAnnot=av.getAlignmentStrucConsensusAnnotation();
-    boolean renderHistogram = true, renderProfile = true, normaliseProfile = false;
+    final AlignmentAnnotation consensusAnnot = av
+            .getAlignmentConsensusAnnotation();
+    final AlignmentAnnotation structConsensusAnnot = av
+            .getAlignmentStrucConsensusAnnotation();
+    final AlignmentAnnotation complementConsensusAnnot = av
+            .getComplementConsensusAnnotation();
+    boolean renderHistogram = true, renderProfile = true,
+            normaliseProfile = false, isRNA = rna;
 
     BitSet graphGroupDrawn = new BitSet();
     int charOffset = 0; // offset for a label
 
     BitSet graphGroupDrawn = new BitSet();
     int charOffset = 0; // offset for a label
@@ -391,13 +482,15 @@ public class AnnotationRenderer
     Font ofont = g.getFont();
     // \u03B2 \u03B1
     // debug ints
     Font ofont = g.getFont();
     // \u03B2 \u03B1
     // debug ints
-    int yfrom=0,f_i=0,yto=0,f_to=0;
-    boolean clipst=false,clipend=false;
+    int yfrom = 0, f_i = 0, yto = 0, f_to = 0;
+    boolean clipst = false, clipend = false;
     for (int i = 0; i < aa.length; i++)
     {
       AlignmentAnnotation row = aa[i];
     for (int i = 0; i < aa.length; i++)
     {
       AlignmentAnnotation row = aa[i];
+      isRNA = row.isRNA();
       {
       {
-        // check if this is a consensus annotation row and set the display settings appropriately
+        // check if this is a consensus annotation row and set the display
+        // settings appropriately
         // TODO: generalise this to have render styles for consensus/profile
         // data
         if (row.groupRef != null && row == row.groupRef.getConsensus())
         // TODO: generalise this to have render styles for consensus/profile
         // data
         if (row.groupRef != null && row == row.groupRef.getConsensus())
@@ -406,14 +499,18 @@ public class AnnotationRenderer
           renderProfile = row.groupRef.isShowSequenceLogo();
           normaliseProfile = row.groupRef.isNormaliseSequenceLogo();
         }
           renderProfile = row.groupRef.isShowSequenceLogo();
           normaliseProfile = row.groupRef.isNormaliseSequenceLogo();
         }
-        else if (row == consensusAnnot || row == structConsensusAnnot)
+        else if (row == consensusAnnot || row == structConsensusAnnot
+                || row == complementConsensusAnnot)
         {
           renderHistogram = av_renderHistogram;
           renderProfile = av_renderProfile;
           normaliseProfile = av_normaliseProfile;
         {
           renderHistogram = av_renderHistogram;
           renderProfile = av_renderProfile;
           normaliseProfile = av_normaliseProfile;
-        } else {
+        }
+        else
+        {
           renderHistogram = true;
           renderHistogram = true;
-          // don't need to set render/normaliseProfile since they are not currently used in any other annotation track renderer
+          // don't need to set render/normaliseProfile since they are not
+          // currently used in any other annotation track renderer
         }
       }
       Annotation[] row_annotations = row.annotations;
         }
       }
       Annotation[] row_annotations = row.annotations;
@@ -426,142 +523,147 @@ public class AnnotationRenderer
       scaleColLabel = row.scaleColLabel;
       lastSS = ' ';
       lastSSX = 0;
       scaleColLabel = row.scaleColLabel;
       lastSS = ' ';
       lastSSX = 0;
-      
-      if (!useClip || ((y-charHeight)<visHeight && (y+row.height+charHeight*2)>=sOffset)) 
+
+      if (!useClip || ((y - charHeight) < visHeight
+              && (y + row.height + charHeight * 2) >= sOffset))
       {// if_in_visible_region
         if (!clipst)
         {
       {// if_in_visible_region
         if (!clipst)
         {
-          clipst=true;
-          yfrom=y;
-          f_i=i;
+          clipst = true;
+          yfrom = y;
+          f_i = i;
         }
         yto = y;
         }
         yto = y;
-        f_to=i;
-      if (row.graph > 0)
-      {
-        if (row.graphGroup > -1 && graphGroupDrawn.get(row.graphGroup)) {
-          continue;
-        }
-
-        // this is so that we draw the characters below the graph
-        y += row.height;
-
-        if (row.hasText)
+        f_to = i;
+        if (row.graph > 0)
         {
         {
-          iconOffset = charHeight - fm.getDescent();
-          y -= charHeight;
-        }
-      }
-      else if (row.hasText)
-      {
-        iconOffset = charHeight - fm.getDescent();
-
-      }
-      else
-      {
-        iconOffset = 0;
-      }
-
-      if (row.autoCalculated && av.isCalculationInProgress(row))
-      {
-        y += charHeight;
-        usedFaded = true;
-        g.drawImage(fadedImage, 0, y - row.height, imgWidth, y, 0, y
-                - row.height, imgWidth, y, annotationPanel);
-        g.setColor(Color.black);
-        // g.drawString("Calculating "+aa[i].label+"....",20, y-row.height/2);
+          if (row.graphGroup > -1 && graphGroupDrawn.get(row.graphGroup))
+          {
+            continue;
+          }
 
 
-        continue;
-      }
+          // this is so that we draw the characters below the graph
+          y += row.height;
 
 
-      /*
-       * else if (annotationPanel.av.updatingConservation &&
-       * aa[i].label.equals("Conservation")) {
-       * 
-       * y += charHeight; g.drawImage(annotationPanel.fadedImage, 0, y -
-       * row.height, annotationPanel.imgWidth, y, 0, y - row.height,
-       * annotationPanel.imgWidth, y, annotationPanel);
-       * 
-       * g.setColor(Color.black); //
-       * g.drawString("Calculating Conservation.....",20, y-row.height/2);
-       * 
-       * continue; } else if (annotationPanel.av.updatingConservation &&
-       * aa[i].label.equals("Quality")) {
-       * 
-       * y += charHeight; g.drawImage(annotationPanel.fadedImage, 0, y -
-       * row.height, annotationPanel.imgWidth, y, 0, y - row.height,
-       * annotationPanel.imgWidth, y, annotationPanel); g.setColor(Color.black);
-       * // / g.drawString("Calculating Quality....",20, y-row.height/2);
-       * 
-       * continue; }
-       */
-      // first pass sets up state for drawing continuation from left-hand column
-      // of startRes
-      x = (startRes == 0) ? 0 : -1;
-      while (x < endRes - startRes)
-      {
-        if (hasHiddenColumns)
-        {
-          column = columnSelection.adjustForHiddenColumns(startRes + x);
-          if (column > row_annotations.length - 1)
+          if (row.hasText)
           {
           {
-            break;
+            iconOffset = charHeight - fm.getDescent();
+            y -= charHeight;
           }
         }
           }
         }
-        else
+        else if (row.hasText)
         {
         {
-          column = startRes + x;
-        }
+          iconOffset = charHeight - fm.getDescent();
 
 
-        if ((row_annotations == null) || (row_annotations.length <= column)
-                || (row_annotations[column] == null))
-        {
-          validRes = false;
         }
         else
         {
         }
         else
         {
-          validRes = true;
+          iconOffset = 0;
         }
         }
-        if (x > -1)
+
+        if (row.autoCalculated && av.isCalculationInProgress(row))
         {
         {
-          if (activeRow == i)
+          y += charHeight;
+          usedFaded = true;
+          g.drawImage(fadedImage, 0, y - row.height, imgWidth, y, 0,
+                  y - row.height, imgWidth, y, annotationPanel);
+          g.setColor(Color.black);
+          // g.drawString("Calculating "+aa[i].label+"....",20, y-row.height/2);
+
+          continue;
+        }
+
+        /*
+         * else if (annotationPanel.av.updatingConservation &&
+         * aa[i].label.equals("Conservation")) {
+         * 
+         * y += charHeight; g.drawImage(annotationPanel.fadedImage, 0, y -
+         * row.height, annotationPanel.imgWidth, y, 0, y - row.height,
+         * annotationPanel.imgWidth, y, annotationPanel);
+         * 
+         * g.setColor(Color.black); //
+         * g.drawString("Calculating Conservation.....",20, y-row.height/2);
+         * 
+         * continue; } else if (annotationPanel.av.updatingConservation &&
+         * aa[i].label.equals("Quality")) {
+         * 
+         * y += charHeight; g.drawImage(annotationPanel.fadedImage, 0, y -
+         * row.height, annotationPanel.imgWidth, y, 0, y - row.height,
+         * annotationPanel.imgWidth, y, annotationPanel);
+         * g.setColor(Color.black); // /
+         * g.drawString("Calculating Quality....",20, y-row.height/2);
+         * 
+         * continue; }
+         */
+        // first pass sets up state for drawing continuation from left-hand
+        // column
+        // of startRes
+        x = (startRes == 0) ? 0 : -1;
+        while (x < endRes - startRes)
+        {
+          if (hasHiddenColumns)
+          {
+            column = hiddenColumns.visibleToAbsoluteColumn(startRes + x);
+            if (column > row_annotations.length - 1)
+            {
+              break;
+            }
+          }
+          else
           {
           {
-            g.setColor(Color.red);
+            column = startRes + x;
+          }
 
 
-            if (columnSelection != null)
+          if ((row_annotations == null)
+                  || (row_annotations.length <= column)
+                  || (row_annotations[column] == null))
+          {
+            validRes = false;
+          }
+          else
+          {
+            validRes = true;
+          }
+          final String displayChar = validRes
+                  ? row_annotations[column].displayCharacter
+                  : null;
+          if (x > -1)
+          {
+            if (activeRow == i)
             {
             {
-              for (int n = 0; n < columnSelection.size(); n++)
-              {
-                int v = columnSelection.columnAt(n);
+              g.setColor(Color.red);
 
 
-                if (v == column)
+              if (columnSelection != null)
+              {
+                if (columnSelection.contains(column))
                 {
                   g.fillRect(x * charWidth, y, charWidth, charHeight);
                 }
               }
             }
                 {
                   g.fillRect(x * charWidth, y, charWidth, charHeight);
                 }
               }
             }
-          }
-          if (!row.isValidStruc())
-          {
-            g.setColor(Color.orange);
-            g.fillRect((int) row.getInvalidStrucPos() * charWidth, y,
-                    charWidth, charHeight);
-          }
-          if (validCharWidth
-                  && validRes
-                  && row_annotations[column].displayCharacter != null
-                  && (row_annotations[column].displayCharacter.length() > 0))
-          {
-
-            if (centreColLabels || scaleColLabel)
+            if (row.getInvalidStrucPos() > x)
+            {
+              g.setColor(Color.orange);
+              g.fillRect(x * charWidth, y, charWidth, charHeight);
+            }
+            else if (row.getInvalidStrucPos() == x)
+            {
+              g.setColor(Color.orange.darker());
+              g.fillRect(x * charWidth, y, charWidth, charHeight);
+            }
+            if (validCharWidth && validRes && displayChar != null
+                    && (displayChar.length() > 0))
             {
             {
-              fmWidth = fm.charsWidth(
-                      row_annotations[column].displayCharacter
-                              .toCharArray(), 0,
-                      row_annotations[column].displayCharacter.length());
 
 
-              if (scaleColLabel)
+              fmWidth = fm.charsWidth(displayChar.toCharArray(), 0,
+                      displayChar.length());
+              if (/* centreColLabels || */scaleColLabel)
               {
               {
+                // fmWidth = fm.charsWidth(displayChar.toCharArray(), 0,
+                // displayChar.length());
+                //
+                // if (scaleColLabel)
+                // {
                 // justify the label and scale to fit in column
                 if (fmWidth > charWidth)
                 {
                 // justify the label and scale to fit in column
                 if (fmWidth > charWidth)
                 {
@@ -573,381 +675,396 @@ public class AnnotationRenderer
                   // and update the label's width to reflect the scaling.
                   fmWidth = charWidth;
                 }
                   // and update the label's width to reflect the scaling.
                   fmWidth = charWidth;
                 }
+                // }
+              }
+              // TODO is it ok to use width of / show all characters here?
+              // else
+              // {
+              // fmWidth = fm.charWidth(displayChar.charAt(0));
+              // }
+              charOffset = (int) ((charWidth - fmWidth) / 2f);
+
+              if (row_annotations[column].colour == null)
+              {
+                g.setColor(Color.black);
+              }
+              else
+              {
+                g.setColor(row_annotations[column].colour);
               }
               }
-            }
-            else
-            {
-              fmWidth = fm
-                      .charWidth(row_annotations[column].displayCharacter
-                              .charAt(0));
-            }
-            charOffset = (int) ((charWidth - fmWidth) / 2f);
-
-            if (row_annotations[column].colour == null)
-              g.setColor(Color.black);
-            else
-              g.setColor(row_annotations[column].colour);
 
 
-            if (column == 0 || row.graph > 0)
-            {
-              g.drawString(row_annotations[column].displayCharacter,
-                      (x * charWidth) + charOffset, y + iconOffset);
-            }
-            else if (row_annotations[column - 1] == null
-                    || (labelAllCols
-                            || !row_annotations[column].displayCharacter
-                                    .equals(row_annotations[column - 1].displayCharacter) || (row_annotations[column].displayCharacter
-                            .length() < 2 && row_annotations[column].secondaryStructure == ' ')))
-            {
-               g.drawString(row_annotations[column].displayCharacter
-                         , x
-                      * charWidth + charOffset, y + iconOffset);
+              if (column == 0 || row.graph > 0)
+              {
+                g.drawString(displayChar, (x * charWidth) + charOffset,
+                        y + iconOffset);
+              }
+              else if (row_annotations[column - 1] == null || (labelAllCols
+                      || !displayChar.equals(
+                              row_annotations[column - 1].displayCharacter)
+                      || (displayChar.length() < 2
+                              && row_annotations[column].secondaryStructure == ' ')))
+              {
+                g.drawString(displayChar, x * charWidth + charOffset,
+                        y + iconOffset);
+              }
+              g.setFont(ofont);
             }
             }
-            g.setFont(ofont);
           }
           }
-        }
-        if (row.hasIcons)
-        {
-          char ss = validRes ? row_annotations[column].secondaryStructure
-                  : '-';
-          
-          if (ss == '(')
+          if (row.hasIcons)
           {
           {
-            // distinguish between forward/backward base-pairing
-            if (row_annotations[column].displayCharacter.indexOf(')') > -1)
+            char ss = validRes ? row_annotations[column].secondaryStructure
+                    : '-';
+
+            if (ss == '(')
             {
             {
-            
-              ss = ')';
-              
+              // distinguish between forward/backward base-pairing
+              if (displayChar.indexOf(')') > -1)
+              {
+
+                ss = ')';
+
+              }
             }
             }
-          }
-           if (ss == '[')
-          {
-            if ((row_annotations[column].displayCharacter.indexOf(']') > -1))
+            if (ss == '[')
             {
             {
+              if ((displayChar.indexOf(']') > -1))
+              {
                 ss = ']';
                 ss = ']';
-                
-                
+
+              }
             }
             }
-          }
-           if (ss == '{')
-           {
-             // distinguish between forward/backward base-pairing
-             if (row_annotations[column].displayCharacter.indexOf('}') > -1)
-             {
-               ss = '}';
-               
-               
-             }
-           }
-           if (ss == '<')
-           {
-             // distinguish between forward/backward base-pairing
-             if (row_annotations[column].displayCharacter.indexOf('<') > -1)
-             {
-               ss = '>';
-               
-               
-             }
-           }
-           if (ss >=65)
-           {
-             // distinguish between forward/backward base-pairing
-             if (row_annotations[column].displayCharacter.indexOf(ss+32) > -1)
-             {
-              
-               ss = (char) (ss+32);
-               
-               
-             }
-           }
-           
-               
-          if (!validRes || (ss != lastSS))
-             {
-               
-               
-            if (x > -1)
-             {
-               
-               
-              int nb_annot=x-temp;
-              //System.out.println("\t type :"+lastSS+"\t x :"+x+"\t nbre annot :"+nb_annot);
-              switch (lastSS)
-             {
-               
-              case '$':
-                drawHelixAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
-                        column, validRes, validEnd);
-                break;
-
-              case 'µ':
-                drawSheetAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
-                        column, validRes, validEnd);
-                break;
-
-              case '(': // Stem case for RNA secondary structure
-              case ')': // and opposite direction
-                drawStemAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
-                        column, validRes, validEnd);
-                temp=x;
-                break;
-              case '{':
-              case '}':
-              case '[':
-              case ']':
-              case '>':
-              case '<':
-              case 'A':
-              case 'a':
-              case 'B':
-              case 'b':
-              case 'C':
-              case 'c':
-              case 'D':
-              case 'd':
-              case 'E':
-              case 'e':
-              case 'F':
-              case 'f':
-              case 'G':
-              case 'g':
-              case 'H':
-              case 'h':
-              case 'I':
-              case 'i':
-              case 'J':
-              case 'j':
-              case 'K':
-              case 'k':
-              case 'L':
-              case 'l':
-              case 'M':
-              case 'm':
-              case 'N':
-              case 'n':
-              case 'O':
-              case 'o':
-              case 'P':
-              case 'p':
-              case 'Q':
-              case 'q':
-              case 'R':
-              case 'r':
-              case 'S':
-              case 's':
-              case 'T':
-              case 't':
-              case 'U':
-              case 'u':
-              case 'V':
-              case 'v':
-              case 'W':
-              case 'w':
-              case 'X':
-              case 'x':
-              case 'Y':
-              case 'y':
-              case 'Z':
-              case 'z':
-                 
-                 Color nonCanColor= getNotCanonicalColor(lastSS);
-                 drawNotCanonicalAnnot(g, nonCanColor, row_annotations, lastSSX, x, y, iconOffset, startRes,
-                          column, validRes, validEnd);
-                 temp=x;
-                 break;
-              default:
-                g.setColor(Color.gray);
-                g.fillRect(lastSSX, y + 6 + iconOffset, (x * charWidth)
-                        - lastSSX, 2);
-                temp=x;
-                break;
+            if (ss == '{')
+            {
+              // distinguish between forward/backward base-pairing
+              if (displayChar.indexOf('}') > -1)
+              {
+                ss = '}';
+
               }
             }
               }
             }
-            if (validRes)
+            if (ss == '<')
             {
             {
-              lastSS = ss;
+              // distinguish between forward/backward base-pairing
+              if (displayChar.indexOf('<') > -1)
+              {
+                ss = '>';
+
+              }
             }
             }
-            else
+            if (isRNA && (ss >= CHAR_A) && (ss <= CHAR_Z))
             {
             {
-              lastSS = ' ';
+              // distinguish between forward/backward base-pairing
+              int ssLowerCase = ss + UPPER_TO_LOWER;
+              // TODO would .equals() be safer here? or charAt(0)?
+              if (displayChar.indexOf(ssLowerCase) > -1)
+              {
+                ss = (char) ssLowerCase;
+              }
             }
             }
-            if (x > -1)
+
+            if (!validRes || (ss != lastSS))
             {
             {
-              lastSSX = (x * charWidth);
+
+              if (x > -1)
+              {
+
+                int nb_annot = x - temp;
+                // System.out.println("\t type :"+lastSS+"\t x :"+x+"\t nbre
+                // annot :"+nb_annot);
+                switch (lastSS)
+                {
+                case '(': // Stem case for RNA secondary structure
+                case ')': // and opposite direction
+                  drawStemAnnot(g, row_annotations, lastSSX, x, y,
+                          iconOffset, startRes, column, validRes, validEnd);
+                  temp = x;
+                  break;
+
+                case 'H':
+                  if (!isRNA)
+                  {
+                    drawHelixAnnot(g, row_annotations, lastSSX, x, y,
+                            iconOffset, startRes, column, validRes,
+                            validEnd);
+                    break;
+                  }
+                  // no break if isRNA - falls through to drawNotCanonicalAnnot!
+                case 'E':
+                  if (!isRNA)
+                  {
+                    drawSheetAnnot(g, row_annotations, lastSSX, x, y,
+                            iconOffset, startRes, column, validRes,
+                            validEnd);
+                    break;
+                  }
+                  // no break if isRNA - fall through to drawNotCanonicalAnnot!
+
+                case '{':
+                case '}':
+                case '[':
+                case ']':
+                case '>':
+                case '<':
+                case 'A':
+                case 'a':
+                case 'B':
+                case 'b':
+                case 'C':
+                case 'c':
+                case 'D':
+                case 'd':
+                case 'e':
+                case 'F':
+                case 'f':
+                case 'G':
+                case 'g':
+                case 'h':
+                case 'I':
+                case 'i':
+                case 'J':
+                case 'j':
+                case 'K':
+                case 'k':
+                case 'L':
+                case 'l':
+                case 'M':
+                case 'm':
+                case 'N':
+                case 'n':
+                case 'O':
+                case 'o':
+                case 'P':
+                case 'p':
+                case 'Q':
+                case 'q':
+                case 'R':
+                case 'r':
+                case 'S':
+                case 's':
+                case 'T':
+                case 't':
+                case 'U':
+                case 'u':
+                case 'V':
+                case 'v':
+                case 'W':
+                case 'w':
+                case 'X':
+                case 'x':
+                case 'Y':
+                case 'y':
+                case 'Z':
+                case 'z':
+
+                  Color nonCanColor = getNotCanonicalColor(lastSS);
+                  drawNotCanonicalAnnot(g, nonCanColor, row_annotations,
+                          lastSSX, x, y, iconOffset, startRes, column,
+                          validRes, validEnd);
+                  temp = x;
+                  break;
+                default:
+                  g.setColor(Color.gray);
+                  g.fillRect(lastSSX, y + 6 + iconOffset,
+                          (x * charWidth) - lastSSX, 2);
+                  temp = x;
+                  break;
+                }
+              }
+              if (validRes)
+              {
+                lastSS = ss;
+              }
+              else
+              {
+                lastSS = ' ';
+              }
+              if (x > -1)
+              {
+                lastSSX = (x * charWidth);
+              }
             }
           }
             }
           }
+          column++;
+          x++;
         }
         }
-        column++;
-        x++;
-      }
-      if (column >= row_annotations.length)
-      {
-        column = row_annotations.length - 1;
-        validEnd = false;
-      }
-      else
-      {
-        validEnd = true;
-      }
-      if ((row_annotations == null) || (row_annotations.length <= column)
-              || (row_annotations[column] == null))
-      {
-        validRes = false;
-      }
-      else
-      {
-        validRes = true;
-      }
-
-      // x ++;
+        if (column >= row_annotations.length)
+        {
+          column = row_annotations.length - 1;
+          validEnd = false;
+        }
+        else
+        {
+          validEnd = true;
+        }
+        if ((row_annotations == null) || (row_annotations.length <= column)
+                || (row_annotations[column] == null))
+        {
+          validRes = false;
+        }
+        else
+        {
+          validRes = true;
+        }
+        // x ++;
 
 
-      if (row.hasIcons)
-      {
-        switch (lastSS)
+        if (row.hasIcons)
         {
         {
-        case '$':
-          drawHelixAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
-                  column, validRes, validEnd);
-          break;
-
-        case 'µ':
-          drawSheetAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
-                  column, validRes, validEnd);
-          break;
-        case 's':
-        case 'S': // Stem case for RNA secondary structure
-               
-          drawStemAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
-                  column, validRes, validEnd);
-          
-          break;
-        case '{':
-        case '}':
-        case '[':
-        case ']':
-        case '>':
-        case '<':
-        case 'A':
-        case 'a':
-        case 'B':
-        case 'b':
-        case 'C':
-        case 'c':
-        case 'D':
-        case 'd':
-        case 'E':
-        case 'e':
-        case 'F':
-        case 'f':
-        case 'G':
-        case 'g':
-        case 'H':
-        case 'h':
-        case 'I':
-        case 'i':
-        case 'J':
-        case 'j':
-        case 'K':
-        case 'k':
-        case 'L':
-        case 'l':
-        case 'M':
-        case 'm':
-        case 'N':
-        case 'n':
-        case 'O':
-        case 'o':
-        case 'P':
-        case 'p':
-        case 'Q':
-        case 'q':
-        case 'R':
-        case 'r':
-        case 'T':
-        case 't':
-        case 'U':
-        case 'u':
-        case 'V':
-        case 'v':
-        case 'W':
-        case 'w':
-        case 'X':
-        case 'x':
-        case 'Y':
-        case 'y':
-        case 'Z':
-        case 'z':
-               //System.out.println(lastSS);
-          Color nonCanColor = getNotCanonicalColor(lastSS);
-         drawNotCanonicalAnnot(g,nonCanColor, row_annotations, lastSSX, x, y, iconOffset, startRes,
-                    column, validRes, validEnd);
-         break;
-        default:
-          drawGlyphLine(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
-                  column, validRes, validEnd);
-          break;
+          switch (lastSS)
+          {
+
+          case 'H':
+            if (!isRNA)
+            {
+              drawHelixAnnot(g, row_annotations, lastSSX, x, y, iconOffset,
+                      startRes, column, validRes, validEnd);
+              break;
+            }
+            // no break if isRNA - fall through to drawNotCanonicalAnnot!
+
+          case 'E':
+            if (!isRNA)
+            {
+              drawSheetAnnot(g, row_annotations, lastSSX, x, y, iconOffset,
+                      startRes, column, validRes, validEnd);
+              break;
+            }
+            // no break if isRNA - fall through to drawNotCanonicalAnnot!
+
+          case '(':
+          case ')': // Stem case for RNA secondary structure
+
+            drawStemAnnot(g, row_annotations, lastSSX, x, y, iconOffset,
+                    startRes, column, validRes, validEnd);
+
+            break;
+          case '{':
+          case '}':
+          case '[':
+          case ']':
+          case '>':
+          case '<':
+          case 'A':
+          case 'a':
+          case 'B':
+          case 'b':
+          case 'C':
+          case 'c':
+          case 'D':
+          case 'd':
+          case 'e':
+          case 'F':
+          case 'f':
+          case 'G':
+          case 'g':
+          case 'h':
+          case 'I':
+          case 'i':
+          case 'J':
+          case 'j':
+          case 'K':
+          case 'k':
+          case 'L':
+          case 'l':
+          case 'M':
+          case 'm':
+          case 'N':
+          case 'n':
+          case 'O':
+          case 'o':
+          case 'P':
+          case 'p':
+          case 'Q':
+          case 'q':
+          case 'R':
+          case 'r':
+          case 'T':
+          case 't':
+          case 'U':
+          case 'u':
+          case 'V':
+          case 'v':
+          case 'W':
+          case 'w':
+          case 'X':
+          case 'x':
+          case 'Y':
+          case 'y':
+          case 'Z':
+          case 'z':
+            // System.out.println(lastSS);
+            Color nonCanColor = getNotCanonicalColor(lastSS);
+            drawNotCanonicalAnnot(g, nonCanColor, row_annotations, lastSSX,
+                    x, y, iconOffset, startRes, column, validRes, validEnd);
+            break;
+          default:
+            drawGlyphLine(g, row_annotations, lastSSX, x, y, iconOffset,
+                    startRes, column, validRes, validEnd);
+            break;
+          }
         }
         }
-      }
 
 
-      if (row.graph > 0 && row.graphHeight > 0)
-      {
-        if (row.graph == AlignmentAnnotation.LINE_GRAPH)
+        if (row.graph > 0 && row.graphHeight > 0)
         {
         {
-          if (row.graphGroup > -1 && !graphGroupDrawn.get(row.graphGroup))
+          if (row.graph == AlignmentAnnotation.LINE_GRAPH)
           {
           {
-            // TODO: JAL-1291 revise rendering model so the graphGroup map is computed efficiently for all visible labels
-            float groupmax = -999999, groupmin = 9999999;
-            for (int gg = 0; gg < aa.length; gg++)
+            if (row.graphGroup > -1 && !graphGroupDrawn.get(row.graphGroup))
             {
             {
-              if (aa[gg].graphGroup != row.graphGroup)
+              // TODO: JAL-1291 revise rendering model so the graphGroup map is
+              // computed efficiently for all visible labels
+              float groupmax = -999999, groupmin = 9999999;
+              for (int gg = 0; gg < aa.length; gg++)
               {
               {
-                continue;
-              }
+                if (aa[gg].graphGroup != row.graphGroup)
+                {
+                  continue;
+                }
 
 
-              if (aa[gg] != row)
-              {
-                aa[gg].visible = false;
-              }
-              if (aa[gg].graphMax > groupmax)
-              {
-                groupmax = aa[gg].graphMax;
+                if (aa[gg] != row)
+                {
+                  aa[gg].visible = false;
+                }
+                if (aa[gg].graphMax > groupmax)
+                {
+                  groupmax = aa[gg].graphMax;
+                }
+                if (aa[gg].graphMin < groupmin)
+                {
+                  groupmin = aa[gg].graphMin;
+                }
               }
               }
-              if (aa[gg].graphMin < groupmin)
+
+              for (int gg = 0; gg < aa.length; gg++)
               {
               {
-                groupmin = aa[gg].graphMin;
+                if (aa[gg].graphGroup == row.graphGroup)
+                {
+                  drawLineGraph(g, aa[gg], aa[gg].annotations, startRes,
+                          endRes, y, groupmin, groupmax, row.graphHeight);
+                }
               }
               }
-            }
 
 
-            for (int gg = 0; gg < aa.length; gg++)
+              graphGroupDrawn.set(row.graphGroup);
+            }
+            else
             {
             {
-              if (aa[gg].graphGroup == row.graphGroup)
-              {
-                drawLineGraph(g, aa[gg], aa[gg].annotations, startRes, endRes, y, groupmin,
-                        groupmax, row.graphHeight);
-              }
+              drawLineGraph(g, row, row_annotations, startRes, endRes, y,
+                      row.graphMin, row.graphMax, row.graphHeight);
             }
             }
-
-            graphGroupDrawn.set(row.graphGroup);
           }
           }
-          else
+          else if (row.graph == AlignmentAnnotation.BAR_GRAPH)
           {
           {
-            drawLineGraph(g, row, row_annotations, startRes, endRes, y, row.graphMin,
-                    row.graphMax, row.graphHeight);
+            drawBarGraph(g, row, row_annotations, startRes, endRes,
+                    row.graphMin, row.graphMax, y, renderHistogram,
+                    renderProfile, normaliseProfile);
           }
         }
           }
         }
-        else if (row.graph == AlignmentAnnotation.BAR_GRAPH)
-        {
-          drawBarGraph(g, row, row_annotations, startRes, endRes,
-                  row.graphMin, row.graphMax, y, renderHistogram,renderProfile,normaliseProfile);
-        }
       }
       }
-    } else {
-      if (clipst && !clipend)
+      else
       {
       {
-        clipend = true;
-      }
-    }// end if_in_visible_region
+        if (clipst && !clipend)
+        {
+          clipend = true;
+        }
+      } // end if_in_visible_region
       if (row.graph > 0 && row.hasText)
       {
         y += charHeight;
       if (row.graph > 0 && row.hasText)
       {
         y += charHeight;
@@ -964,13 +1081,13 @@ public class AnnotationRenderer
       {
         if (clipst)
         {
       {
         if (clipst)
         {
-          System.err.println("Start clip at : " + yfrom + " (index " + f_i
-                  + ")");
+          System.err.println(
+                  "Start clip at : " + yfrom + " (index " + f_i + ")");
         }
         if (clipend)
         {
         }
         if (clipend)
         {
-          System.err.println("End clip at : " + yto + " (index " + f_to
-                  + ")");
+          System.err.println(
+                  "End clip at : " + yto + " (index " + f_to + ")");
         }
       }
       ;
         }
       }
       ;
@@ -982,25 +1099,25 @@ public class AnnotationRenderer
     return !usedFaded;
   }
 
     return !usedFaded;
   }
 
-  private final Color GLYPHLINE_COLOR = Color.gray;
+  public static final Color GLYPHLINE_COLOR = Color.gray;
 
 
-  private final Color SHEET_COLOUR = Color.green;
+  public static final Color SHEET_COLOUR = Color.green;
 
 
-  private final Color HELIX_COLOUR = Color.red;
+  public static final Color HELIX_COLOUR = Color.red;
 
 
-  private final Color STEM_COLOUR = Color.blue;
-  
-  private  Color sdNOTCANONICAL_COLOUR;
+  public static final Color STEM_COLOUR = Color.blue;
 
 
-  public void drawGlyphLine(Graphics g, Annotation[] row,
-          int lastSSX, int x, int y, int iconOffset, int startRes,
-          int column, boolean validRes, boolean validEnd)
+  private Color sdNOTCANONICAL_COLOUR;
+
+  void drawGlyphLine(Graphics g, Annotation[] row, int lastSSX, int x,
+          int y, int iconOffset, int startRes, int column, boolean validRes,
+          boolean validEnd)
   {
     g.setColor(GLYPHLINE_COLOR);
     g.fillRect(lastSSX, y + 6 + iconOffset, (x * charWidth) - lastSSX, 2);
   }
 
   {
     g.setColor(GLYPHLINE_COLOR);
     g.fillRect(lastSSX, y + 6 + iconOffset, (x * charWidth) - lastSSX, 2);
   }
 
-  public void drawSheetAnnot(Graphics g, Annotation[] row,
+  void drawSheetAnnot(Graphics g, Annotation[] row,
 
           int lastSSX, int x, int y, int iconOffset, int startRes,
           int column, boolean validRes, boolean validEnd)
 
           int lastSSX, int x, int y, int iconOffset, int startRes,
           int column, boolean validRes, boolean validEnd)
@@ -1010,29 +1127,31 @@ public class AnnotationRenderer
     if (!validEnd || !validRes || row == null || row[column] == null
             || row[column].secondaryStructure != 'E')
     {
     if (!validEnd || !validRes || row == null || row[column] == null
             || row[column].secondaryStructure != 'E')
     {
-      g.fillRect(lastSSX, y + 4 + iconOffset,
-              (x * charWidth) - lastSSX - 4, 7);
-      g.fillPolygon(new int[]
-      { (x * charWidth) - 4, (x * charWidth) - 4, (x * charWidth) },
+      g.fillRect(lastSSX, y + 4 + iconOffset, (x * charWidth) - lastSSX - 4,
+              7);
+      g.fillPolygon(
+              new int[]
+              { (x * charWidth) - 4, (x * charWidth) - 4, (x * charWidth) },
               new int[]
               { y + iconOffset, y + 14 + iconOffset, y + 7 + iconOffset },
               3);
     }
     else
     {
               new int[]
               { y + iconOffset, y + 14 + iconOffset, y + 7 + iconOffset },
               3);
     }
     else
     {
-      g.fillRect(lastSSX, y + 4 + iconOffset,
-              (x + 1) * charWidth - lastSSX, 7);
+      g.fillRect(lastSSX, y + 4 + iconOffset, (x + 1) * charWidth - lastSSX,
+              7);
     }
 
   }
 
     }
 
   }
 
-  public void drawHelixAnnot(Graphics g, Annotation[] row, int lastSSX,
-          int x, int y, int iconOffset, int startRes, int column,
-          boolean validRes, boolean validEnd)
+  void drawHelixAnnot(Graphics g, Annotation[] row, int lastSSX, int x,
+          int y, int iconOffset, int startRes, int column, boolean validRes,
+          boolean validEnd)
   {
     g.setColor(HELIX_COLOUR);
 
   {
     g.setColor(HELIX_COLOUR);
 
-    int sCol = (lastSSX / charWidth) + startRes;
+    int sCol = (lastSSX / charWidth)
+            + hiddenColumns.visibleToAbsoluteColumn(startRes);
     int x1 = lastSSX;
     int x2 = (x * charWidth);
 
     int x1 = lastSSX;
     int x2 = (x * charWidth);
 
@@ -1060,8 +1179,8 @@ public class AnnotationRenderer
       else
       {
         // g.setColor(Color.magenta);
       else
       {
         // g.setColor(Color.magenta);
-        g.fillRoundRect(lastSSX + ofs, y + 4 + iconOffset, x2 - x1 - ofs
-                + 1, 8, 0, 0);
+        g.fillRoundRect(lastSSX + ofs, y + 4 + iconOffset,
+                x2 - x1 - ofs + 1, 8, 0, 0);
 
       }
 
 
       }
 
@@ -1086,9 +1205,9 @@ public class AnnotationRenderer
     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 8);
   }
 
     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 8);
   }
 
-  public void drawLineGraph(Graphics g, AlignmentAnnotation _aa,
-          Annotation[] aa_annotations, int sRes, int eRes, int y,
-          float min, float max, int graphHeight)
+  void drawLineGraph(Graphics g, AlignmentAnnotation _aa,
+          Annotation[] aa_annotations, int sRes, int eRes, int y, float min,
+          float max, int graphHeight)
   {
     if (sRes > aa_annotations.length)
     {
   {
     if (sRes > aa_annotations.length)
     {
@@ -1132,7 +1251,7 @@ public class AnnotationRenderer
       column = sRes + x;
       if (hasHiddenColumns)
       {
       column = sRes + x;
       if (hasHiddenColumns)
       {
-        column = columnSelection.adjustForHiddenColumns(column);
+        column = hiddenColumns.visibleToAbsoluteColumn(column);
       }
 
       if (column > aaMax)
       }
 
       if (column > aaMax)
@@ -1148,17 +1267,21 @@ public class AnnotationRenderer
       }
 
       if (aa_annotations[column].colour == null)
       }
 
       if (aa_annotations[column].colour == null)
+      {
         g.setColor(Color.black);
         g.setColor(Color.black);
+      }
       else
       else
+      {
         g.setColor(aa_annotations[column].colour);
         g.setColor(aa_annotations[column].colour);
+      }
 
 
-      y1 = y
-              - (int) (((aa_annotations[column - 1].value - min) / range) * graphHeight);
-      y2 = y
-              - (int) (((aa_annotations[column].value - min) / range) * graphHeight);
+      y1 = y - (int) (((aa_annotations[column - 1].value - min) / range)
+              * graphHeight);
+      y2 = y - (int) (((aa_annotations[column].value - min) / range)
+              * graphHeight);
 
 
-      g.drawLine(x * charWidth - charWidth / 2, y1, x * charWidth
-              + charWidth / 2, y2);
+      g.drawLine(x * charWidth - charWidth / 2, y1,
+              x * charWidth + charWidth / 2, y2);
       x++;
     }
 
       x++;
     }
 
@@ -1176,9 +1299,10 @@ public class AnnotationRenderer
     }
   }
 
     }
   }
 
-  public void drawBarGraph(Graphics g, AlignmentAnnotation _aa,
+  void drawBarGraph(Graphics g, AlignmentAnnotation _aa,
           Annotation[] aa_annotations, int sRes, int eRes, float min,
           Annotation[] aa_annotations, int sRes, int eRes, float min,
-          float max, int y, boolean renderHistogram,boolean renderProfile,boolean normaliseProfile)
+          float max, int y, boolean renderHistogram, boolean renderProfile,
+          boolean normaliseProfile)
   {
     if (sRes > aa_annotations.length)
     {
   {
     if (sRes > aa_annotations.length)
     {
@@ -1207,7 +1331,7 @@ public class AnnotationRenderer
       column = sRes + x;
       if (hasHiddenColumns)
       {
       column = sRes + x;
       if (hasHiddenColumns)
       {
-        column = columnSelection.adjustForHiddenColumns(column);
+        column = hiddenColumns.visibleToAbsoluteColumn(column);
       }
 
       if (column > aaMax)
       }
 
       if (column > aaMax)
@@ -1221,12 +1345,16 @@ public class AnnotationRenderer
         continue;
       }
       if (aa_annotations[column].colour == null)
         continue;
       }
       if (aa_annotations[column].colour == null)
+      {
         g.setColor(Color.black);
         g.setColor(Color.black);
+      }
       else
       else
+      {
         g.setColor(aa_annotations[column].colour);
         g.setColor(aa_annotations[column].colour);
+      }
 
 
-      y1 = y
-              - (int) (((aa_annotations[column].value - min) / (range)) * _aa.graphHeight);
+      y1 = y - (int) (((aa_annotations[column].value - min) / (range))
+              * _aa.graphHeight);
 
       if (renderHistogram)
       {
 
       if (renderHistogram)
       {
@@ -1243,10 +1371,16 @@ public class AnnotationRenderer
       if (renderProfile)
       {
 
       if (renderProfile)
       {
 
+        /*
+         * {profile type, #values, total count, char1, pct1, char2, pct2...}
+         */
         int profl[] = getProfileFor(_aa, column);
         int profl[] = getProfileFor(_aa, column);
+
         // just try to draw the logo if profl is not null
         // just try to draw the logo if profl is not null
-        if (profl != null && profl[1] != 0)
+        if (profl != null && profl[2] != 0)
         {
         {
+          boolean isStructureProfile = profl[0] == AlignmentAnnotation.STRUCTURE_PROFILE;
+          boolean isCdnaProfile = profl[0] == AlignmentAnnotation.CDNA_PROFILE;
           float ht = normaliseProfile ? y - _aa.graphHeight : y1;
           double htn = normaliseProfile ? _aa.graphHeight : (y2 - y1);// aa.graphHeight;
           double hght;
           float ht = normaliseProfile ? y - _aa.graphHeight : y1;
           double htn = normaliseProfile ? _aa.graphHeight : (y2 - y1);// aa.graphHeight;
           double hght;
@@ -1255,55 +1389,81 @@ public class AnnotationRenderer
           char[] dc;
 
           /**
           char[] dc;
 
           /**
-           * profl.length == 74 indicates that the profile of a secondary
-           * structure conservation row was accesed. Therefore dc gets length 2,
-           * to have space for a basepair instead of just a single nucleotide
+           * Render a single base for a sequence profile, a base pair for
+           * structure profile, and a triplet for a cdna profile
            */
            */
-          if (profl.length == 74)
-          {
-            dc = new char[2];
-          }
-          else
-          {
-            dc = new char[1];
-          }
+          dc = new char[isStructureProfile ? 2 : (isCdnaProfile ? 3 : 1)];
+
           LineMetrics lm = g.getFontMetrics(ofont).getLineMetrics("Q", g);
           LineMetrics lm = g.getFontMetrics(ofont).getLineMetrics("Q", g);
-          double scale = 1f / (normaliseProfile ? profl[1] : 100f);
+          double scale = 1f / (normaliseProfile ? profl[2] : 100f);
           float ofontHeight = 1f / lm.getAscent();// magnify to fill box
           double scl = 0.0;
           float ofontHeight = 1f / lm.getAscent();// magnify to fill box
           double scl = 0.0;
-          for (int c = 2; c < profl[0];)
-          {
-            dc[0] = (char) profl[c++];
 
 
-            if (_aa.label.startsWith("StrucConsensus"))
+          /*
+           * Traverse the character(s)/percentage data in the array
+           */
+          int c = 3;
+          int valuesProcessed = 0;
+          // profl[1] is the number of values in the profile
+          while (valuesProcessed < profl[1])
+          {
+            if (isStructureProfile)
             {
             {
+              // todo can we encode a structure pair as an int, like codons?
+              dc[0] = (char) profl[c++];
               dc[1] = (char) profl[c++];
             }
               dc[1] = (char) profl[c++];
             }
+            else if (isCdnaProfile)
+            {
+              dc = CodingUtils.decodeCodon(profl[c++]);
+            }
+            else
+            {
+              dc[0] = (char) profl[c++];
+            }
 
             wdth = charWidth;
             wdth /= fm.charsWidth(dc, 0, dc.length);
 
             ht += scl;
 
             wdth = charWidth;
             wdth /= fm.charsWidth(dc, 0, dc.length);
 
             ht += scl;
+            // next profl[] position is profile % for the character(s)
+            scl = htn * scale * profl[c++];
+            lm = ofont.getLineMetrics(dc, 0, 1,
+                    g.getFontMetrics().getFontRenderContext());
+            Font font = ofont.deriveFont(AffineTransform
+                    .getScaleInstance(wdth, scl / lm.getAscent()));
+            g.setFont(font);
+            lm = g.getFontMetrics().getLineMetrics(dc, 0, 1, g);
+
+            // Debug - render boxes around characters
+            // g.setColor(Color.red);
+            // g.drawRect(x*av.charWidth, (int)ht, av.charWidth,
+            // (int)(scl));
+            // g.setColor(profcolour.findColour(dc[0]).darker());
+
+            /*
+             * Set character colour as per alignment colour scheme; use the
+             * codon translation if a cDNA profile
+             */
+            Color colour = null;
+            if (isCdnaProfile)
+            {
+              final String codonTranslation = ResidueProperties
+                      .codonTranslate(new String(dc));
+              colour = profcolour.findColour(codonTranslation.charAt(0),
+                      column, null);
+            }
+            else
             {
             {
-              scl = htn * scale * profl[c++];
-              lm = ofont.getLineMetrics(dc, 0, 1, g.getFontMetrics()
-                      .getFontRenderContext());
-              g.setFont(ofont.deriveFont(AffineTransform.getScaleInstance(
-                      wdth, scl / lm.getAscent())));
-              lm = g.getFontMetrics().getLineMetrics(dc, 0, 1, g);
-
-              // Debug - render boxes around characters
-              // g.setColor(Color.red);
-              // g.drawRect(x*av.charWidth, (int)ht, av.charWidth,
-              // (int)(scl));
-              // g.setColor(profcolour.findColour(dc[0]).darker());
-              g.setColor(profcolour.findColour(dc[0], column, null));
-
-              hght = (ht + (scl - lm.getDescent() - lm.getBaselineOffsets()[lm
-                      .getBaselineIndex()]));
-
-              g.drawChars(dc, 0, dc.length, x * charWidth, (int) hght);
+              colour = profcolour.findColour(dc[0], column, null);
             }
             }
+            g.setColor(colour == Color.white ? Color.lightGray : colour);
+
+            hght = (ht + (scl - lm.getDescent()
+                    - lm.getBaselineOffsets()[lm.getBaselineIndex()]));
+
+            g.drawChars(dc, 0, dc.length, x * charWidth, (int) hght);
+            valuesProcessed++;
           }
           g.setFont(ofont);
         }
           }
           g.setFont(ofont);
         }
@@ -1318,8 +1478,8 @@ public class AnnotationRenderer
               BasicStroke.JOIN_ROUND, 3f, new float[]
               { 5f, 3f }, 0f));
 
               BasicStroke.JOIN_ROUND, 3f, new float[]
               { 5f, 3f }, 0f));
 
-      y2 = (int) (y - ((_aa.threshold.value - min) / range)
-              * _aa.graphHeight);
+      y2 = (int) (y
+              - ((_aa.threshold.value - min) / range) * _aa.graphHeight);
       g.drawLine(0, y2, (eRes - sRes) * charWidth, y2);
       g2.setStroke(new BasicStroke());
     }
       g.drawLine(0, y2, (eRes - sRes) * charWidth, y2);
       g2.setStroke(new BasicStroke());
     }
@@ -1341,9 +1501,13 @@ public class AnnotationRenderer
       if (aa_annotations[j] != null)
       {
         if (aa_annotations[j].colour == null)
       if (aa_annotations[j] != null)
       {
         if (aa_annotations[j].colour == null)
+        {
           g.setColor(Color.black);
           g.setColor(Color.black);
+        }
         else
         else
+        {
           g.setColor(aa_annotations[j].colour);
           g.setColor(aa_annotations[j].colour);
+        }
 
         height = (int) ((aa_annotations[j].value / _aa.graphMax) * y);
         if (height > y)
 
         height = (int) ((aa_annotations[j].value / _aa.graphMax) * y);
         if (height > y)
@@ -1357,134 +1521,130 @@ public class AnnotationRenderer
     }
   }
 
     }
   }
 
-  
   Color getNotCanonicalColor(char lastss)
   Color getNotCanonicalColor(char lastss)
-       {
-         switch (lastss)
-      {
-         case '{':  
-             case '}':
-                 return new Color(255,125,5);
-                
-             case '[':
-             case ']':
-                 return new Color(245,115,10);
-                
-             case '>':
-             case '<':
-                 return new Color(235,135,15);
-                 
-             case 'A':
-             case 'a':
-                 return new Color(225,105,20);
-               
-             case 'B':
-             case 'b':
-                 return new Color(215,145,30);
-                 
-             case 'C':
-             case 'c':
-                 return new Color(205,95,35);
-                
-             case 'D':
-             case 'd':
-                 return new Color(195,155,45);
-                 
-             case 'E':
-             case 'e':
-                 return new Color(185,85,55);
-                
-             case 'F':
-             case 'f':
-                 return new Color(175,165,65);
-                
-             case 'G':
-             case 'g':
-                 return new Color(170,75,75);
-               
-             case 'H':
-             case 'h':
-                 return new Color(160,175,85);
-                 
-             case 'I':
-             case 'i':
-                 return new Color(150,65,95);
-                
-             case 'J':
-             case 'j':
-                 return new Color(140,185,105);
-                 
-             case 'K':
-             case 'k':
-                 return new Color(130,55,110);
-               
-             case 'L':
-             case 'l':
-                 return new Color(120,195,120);
-       
-             case 'M':
-             case 'm':
-                 return new Color(110,45,130);
-               
-             case 'N':
-             case 'n':
-                 return new Color(100,205,140);
-                 
-             case 'O':
-             case 'o':
-                 return new Color(90,35,150);
-               
-             case 'P':
-             case 'p':
-                 return new Color(85,215,160);
-               
-             case 'Q':
-             case 'q':
-                 return new Color(75,25,170);
-       
-             case 'R':
-             case 'r':
-                 return new Color(65,225,180);
-       
-             case 'S':
-             case 's':
-                 return new Color(55,15,185);
-               
-             case 'T':
-             case 't':
-                 return new Color(45,235,195);
-                
-             case 'U':
-             case 'u':
-                 return new Color(35,5,205);
-                 
-             case 'V':
-             case 'v':
-                 return new Color(25,245,215);
-                
-             case 'W':
-             case 'w':
-                 return new Color(15,0,225);
-                 
-             case 'X':
-             case 'x':
-                 return new Color(10,255,235);
-                
-             case 'Y':
-             case 'y':
-                 return new Color(5,150,245);
-                 
-             case 'Z':
-             case 'z':
-                 return new Color(0,80,255);
-                
-       default :
-               System.out.println("This is not a interaction : "+lastss);
-               return null;
-               
-      }
-       }
-}
+  {
+    switch (lastss)
+    {
+    case '{':
+    case '}':
+      return new Color(255, 125, 5);
+
+    case '[':
+    case ']':
+      return new Color(245, 115, 10);
+
+    case '>':
+    case '<':
+      return new Color(235, 135, 15);
+
+    case 'A':
+    case 'a':
+      return new Color(225, 105, 20);
+
+    case 'B':
+    case 'b':
+      return new Color(215, 145, 30);
+
+    case 'C':
+    case 'c':
+      return new Color(205, 95, 35);
+
+    case 'D':
+    case 'd':
+      return new Color(195, 155, 45);
 
 
-       
-       
+    case 'E':
+    case 'e':
+      return new Color(185, 85, 55);
+
+    case 'F':
+    case 'f':
+      return new Color(175, 165, 65);
+
+    case 'G':
+    case 'g':
+      return new Color(170, 75, 75);
+
+    case 'H':
+    case 'h':
+      return new Color(160, 175, 85);
+
+    case 'I':
+    case 'i':
+      return new Color(150, 65, 95);
+
+    case 'J':
+    case 'j':
+      return new Color(140, 185, 105);
+
+    case 'K':
+    case 'k':
+      return new Color(130, 55, 110);
+
+    case 'L':
+    case 'l':
+      return new Color(120, 195, 120);
+
+    case 'M':
+    case 'm':
+      return new Color(110, 45, 130);
+
+    case 'N':
+    case 'n':
+      return new Color(100, 205, 140);
+
+    case 'O':
+    case 'o':
+      return new Color(90, 35, 150);
+
+    case 'P':
+    case 'p':
+      return new Color(85, 215, 160);
+
+    case 'Q':
+    case 'q':
+      return new Color(75, 25, 170);
+
+    case 'R':
+    case 'r':
+      return new Color(65, 225, 180);
+
+    case 'S':
+    case 's':
+      return new Color(55, 15, 185);
+
+    case 'T':
+    case 't':
+      return new Color(45, 235, 195);
+
+    case 'U':
+    case 'u':
+      return new Color(35, 5, 205);
+
+    case 'V':
+    case 'v':
+      return new Color(25, 245, 215);
+
+    case 'W':
+    case 'w':
+      return new Color(15, 0, 225);
+
+    case 'X':
+    case 'x':
+      return new Color(10, 255, 235);
+
+    case 'Y':
+    case 'y':
+      return new Color(5, 150, 245);
+
+    case 'Z':
+    case 'z':
+      return new Color(0, 80, 255);
+
+    default:
+      System.out.println("This is not a interaction : " + lastss);
+      return null;
+
+    }
+  }
+}